Improved some project stats (#95)

* The actual stars and watchers can now be displayed when the home_projects view is refreshed.

* views are now also kept track on, it will only change if you refresh the home_projects view.

* Changed V1ApiService a little bit, The mapping of the result from the db calls to a bit cleaner and a little bit efficienter way (O(2n) -> O(n)) (not that it should matter that much.

Changed ApiV1Controller by adding a default by the  recommended tag (in case some project or plugin didnt have one but you still want to request the users information)

* Fix for the upsert statement, it complained about null values

* Made the fix for NPE in apiv4controller less hacky and added spaces between `//` and the message

* Refresh on count
This commit is contained in:
Dragonium 2020-08-30 21:55:26 +02:00 committed by GitHub
parent c99ea8de06
commit 719ada906b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 49 deletions

View File

@ -806,12 +806,12 @@ alter table project_views_individual owner to hangar;
create table project_views
(
day date not null,
day date not null default current_date,
project_id bigint not null
constraint project_views_project_id_fkey
references projects
on delete cascade,
views integer not null,
views integer not null default 1,
constraint project_views_pkey
primary key (project_id, day)
);
@ -935,12 +935,12 @@ FROM projects p
LEFT JOIN project_versions lv ON p.id = lv.project_id
JOIN project_members_all pm ON p.id = pm.id
LEFT JOIN (SELECT p_1.id,
count(*) AS stars
COUNT(ps_1.user_id) AS stars
FROM projects p_1
LEFT JOIN project_stars ps_1 ON p_1.id = ps_1.project_id
GROUP BY p_1.id) ps ON p.id = ps.id
LEFT JOIN (SELECT p_1.id,
count(*) AS watchers
count(pw_1.user_id) AS watchers
FROM projects p_1
LEFT JOIN project_watchers pw_1 ON p_1.id = pw_1.project_id
GROUP BY p_1.id) pw ON p.id = pw.id

View File

@ -216,7 +216,7 @@ public class Apiv1Controller extends HangarController {
projectObj.set("href", mapper.valueToTree(project.getOwnerName() + "/" + project.getSlug()));
projectObj.set("members", writeMembers(members.getOrDefault(project.getId(), new ArrayList<>())));
projectObj.set("channels", mapper.valueToTree( projectChannels.getOrDefault(project.getId(), new ArrayList<>())));
projectObj.set("recommended", writeVersion(recommendedVersions.get(project.getId()), project, recommendedVersionChannels.get(project.getId()), vTags.getOrDefault(recommendedVersions.get(project.getId()).getId(), new ArrayList<>())));
projectObj.set("recommended", writeVersion(project, recommendedVersions, recommendedVersionChannels, vTags));
ObjectNode projectCategoryObj = mapper.createObjectNode();
projectCategoryObj.set("title", mapper.valueToTree(project.getCategory().getTitle()));
projectCategoryObj.set("icon", mapper.valueToTree(project.getCategory().getIcon()));
@ -229,9 +229,12 @@ public class Apiv1Controller extends HangarController {
return projectsArray;
}
private ObjectNode writeVersion(ProjectVersionsTable version, ProjectsTable project, ProjectChannelsTable channel, List<ProjectVersionTagsTable> tags) {
private ObjectNode writeVersion(ProjectsTable project, Map<Long, ProjectVersionsTable> recommendedVersions, Map<Long, ProjectChannelsTable> recommendedVersionChannels, Map<Long, List<ProjectVersionTagsTable>> vTags) {
ProjectVersionsTable version = recommendedVersions.get(project.getId());
ObjectNode objectNode = mapper.createObjectNode();
if (version == null) return objectNode;
ProjectChannelsTable channel = recommendedVersionChannels.get(project.getId());
List<ProjectVersionTagsTable> tags = vTags.getOrDefault(version.getId(), new ArrayList<>());
objectNode.set("id", mapper.valueToTree(version.getId()));
objectNode.set("createdAt", mapper.valueToTree(version.getCreatedAt().toString()));

View File

@ -0,0 +1,31 @@
package io.papermc.hangar.db.dao;
import io.papermc.hangar.db.model.ProjectViewsTable;
import org.jdbi.v3.sqlobject.config.RegisterBeanMapper;
import org.jdbi.v3.sqlobject.statement.SqlQuery;
import org.jdbi.v3.sqlobject.statement.SqlUpdate;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
@Repository
@RegisterBeanMapper(ProjectViewsTable.class)
// TODO: decide where we do count it as a view and where we don't
public interface ProjectViewDao {
// The db will automatically get the current_date from itself (if you dont provide any date)
@SqlUpdate("insert into project_views (day, project_id, views) values (current_date , :projectId, 1)" +
"on conflict (day, project_id) " +
"do update set views = project_views.views + 1 ")
void increaseView(long projectId);
@SqlQuery("select views from project_views " +
"where day = :day and project_id = :projectId ")
long getViewsFromDate(LocalDate day, long projectId);
@SqlQuery("select day, project_id, views from project_views " +
"where day = :day and project_id = :projectId ")
ProjectViewsTable getViewsTable(LocalDate day, long projectId);
}

View File

@ -12,6 +12,8 @@ import io.papermc.hangar.model.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@ -35,44 +37,29 @@ public class V1ApiService {
return v1ApiDao.get().getUsers(offset, limit);
}
public <V> Map<Long, List<V>> mapListToMap(List<Map.Entry<Long, V>> map){
Map<Long, List<V>> returnMap = new HashMap<>();
map.forEach(entry -> {
returnMap.computeIfAbsent(entry.getKey(), userId ->new ArrayList<>()).add(entry.getValue());
});
return returnMap;
}
// TODO better way for this?
public Map<Long, List<String>> getStarredPlugins(List<Long> userIds) {
return v1ApiDao.get().getStarredPlugins(userIds).stream()
.collect(
Collectors.groupingBy(Entry::getKey)
).entrySet().stream()
.collect(
Collectors.toMap(
Entry::getKey,
longListEntry -> longListEntry.getValue().stream().map(Entry::getValue).collect(Collectors.toList()))
);
return mapListToMap(v1ApiDao.get().getStarredPlugins(userIds));
}
public Map<Long, List<Role>> getUsersGlobalRoles(List<Long> userIds) {
return v1ApiDao.get().getUsersGlobalRoles(userIds).stream()
.collect(
Collectors.groupingBy(Entry::getKey)
).entrySet().stream()
.collect(
Collectors.toMap(
Entry::getKey,
listEntry -> listEntry.getValue().stream().map(entry -> Role.fromId(entry.getValue().getId())).collect(Collectors.toList())
)
);
Map<Long, List<Role>> returnMap = new HashMap<>();
v1ApiDao.get().getUsersGlobalRoles(userIds).forEach(entry -> {
returnMap.computeIfAbsent(entry.getKey(), userId -> new ArrayList<>())
.add(Role.fromId(entry.getValue().getId()));
});
return returnMap;
}
public Map<Long, List<ProjectChannelsTable>> getProjectsChannels(List<Long> projectIds) {
return v1ApiDao.get().getProjectsChannels(projectIds).stream()
.collect(
Collectors.groupingBy(Entry::getKey)
).entrySet().stream()
.collect(
Collectors.toMap(
Entry::getKey,
listEntry -> listEntry.getValue().stream().map(Entry::getValue).collect(Collectors.toList())
)
);
return mapListToMap(v1ApiDao.get().getProjectsChannels(projectIds));
}
public Map<Long, ProjectVersionsTable> getProjectsRecommendedVersion(List<Long> projectIds) {
@ -91,15 +78,6 @@ public class V1ApiService {
}
public Map<Long, List<ProjectVersionTagsTable>> getVersionsTags(List<Long> versionIds) {
return v1ApiDao.get().getVersionsTags(versionIds).stream()
.collect(
Collectors.groupingBy(Entry::getKey)
).entrySet().stream()
.collect(
Collectors.toMap(
Entry::getKey,
listEntry -> listEntry.getValue().stream().map(Entry::getValue).collect(Collectors.toList())
)
);
return mapListToMap(v1ApiDao.get().getVersionsTags(versionIds));
}
}

View File

@ -1,9 +1,11 @@
package io.papermc.hangar.service.project;
import io.papermc.hangar.config.hangar.HangarConfig;
import io.papermc.hangar.db.dao.GeneralDao;
import io.papermc.hangar.db.dao.HangarDao;
import io.papermc.hangar.db.dao.ProjectDao;
import io.papermc.hangar.db.dao.UserDao;
import io.papermc.hangar.db.dao.ProjectViewDao;
import io.papermc.hangar.db.dao.api.ProjectApiDao;
import io.papermc.hangar.db.model.ProjectVersionsTable;
import io.papermc.hangar.db.model.ProjectVisibilityChangesTable;
@ -46,16 +48,20 @@ public class ProjectService {
private final HangarDao<UserDao> userDao;
private final HangarDao<VisibilityDao> visibilityDao;
private final HangarDao<ProjectApiDao> projectApiDao;
private final HangarDao<ProjectViewDao> projectViewDao;
private final HangarDao<GeneralDao> generalDao;
private final UserService userService;
private final FlagService flagService;
@Autowired
public ProjectService(HangarConfig hangarConfig, HangarDao<ProjectDao> projectDao, HangarDao<UserDao> userDao, HangarDao<VisibilityDao> visibilityDao, HangarDao<ProjectApiDao> projectApiDao, UserService userService, FlagService flagService) {
public ProjectService(HangarConfig hangarConfig, HangarDao<ProjectDao> projectDao, HangarDao<UserDao> userDao, HangarDao<VisibilityDao> visibilityDao, HangarDao<ProjectApiDao> projectApiDao, HangarDao<ProjectViewDao> projectViewDao, HangarDao<GeneralDao> generalDao, UserService userService, FlagService flagService) {
this.hangarConfig = hangarConfig;
this.projectDao = projectDao;
this.userDao = userDao;
this.visibilityDao = visibilityDao;
this.projectApiDao = projectApiDao;
this.projectViewDao = projectViewDao;
this.generalDao = generalDao;
this.userService = userService;
this.flagService = flagService;
}
@ -155,6 +161,10 @@ public class ProjectService {
public Project getProjectApi(String pluginId) { // TODO still probably have to work out a standard for how to handle the api models
ProjectsTable projectsTable = projectDao.get().getByPluginId(pluginId);
if (projectsTable == null) return null;
projectViewDao.get().increaseView(projectsTable.getId()); //TODO don't increase every time here
generalDao.get().refreshHomeProjects();
Project project = new Project();
project.setCreatedAt(projectsTable.getCreatedAt());
project.setPluginId(projectsTable.getPluginId());
@ -222,7 +232,7 @@ public class ProjectService {
case RECENT_DOWNLOADS : orderingFirstHalf ="p.recent_views *"; break;
case RECENT_VIEWS: orderingFirstHalf ="p.recent_downloads*"; break;
default:
orderingFirstHalf = " "; //Just in case and so that the ide doesnt complain
orderingFirstHalf = " "; // Just in case and so that the ide doesnt complain
}
ordering = orderingFirstHalf + relevance;
}
@ -236,7 +246,7 @@ public class ProjectService {
private String trimQuery(String query){
String trimmedQuery = null;
if(query != null && !query.isBlank()) {
trimmedQuery = query.trim(); //Ore#APIV2Queries line 169 && 200
trimmedQuery = query.trim(); // Ore#APIV2Queries line 169 && 200
}
return trimmedQuery;
}