Added ProjectFirstMeetingRepository to get all first meetings

First meetings can be on a project and on an idea.
Now we fetch all first meetings for the supervisor and then remove any duplicates.

Added null checking so we don't display a link when a first meeting doesn't
have an associated project.
This commit is contained in:
Nico Athanassiadis 2025-04-15 11:28:12 +02:00
parent 2ef8efbaee
commit cd09ec3f14
9 changed files with 167 additions and 36 deletions

@ -44,6 +44,7 @@ import se.su.dsv.scipro.finalseminar.FinalSeminarUploadControllerImpl;
import se.su.dsv.scipro.finalthesis.FinalThesisService;
import se.su.dsv.scipro.finalthesis.FinalThesisServiceImpl;
import se.su.dsv.scipro.firstmeeting.FirstMeetingServiceImpl;
import se.su.dsv.scipro.firstmeeting.ProjectFirstMeetingRepository;
import se.su.dsv.scipro.forum.AbstractThreadRepository;
import se.su.dsv.scipro.forum.BasicForumService;
import se.su.dsv.scipro.forum.BasicForumServiceImpl;
@ -535,9 +536,16 @@ public class CoreConfig {
@Bean
public FirstMeetingServiceImpl firstMeetingService(
Provider<EntityManager> em,
ActivityPlanFacade activityPlanFacade
ActivityPlanFacade activityPlanFacade,
ProjectFirstMeetingRepository projectFirstMeetingRepository,
FirstMeetingRepository firstMeetingRepository
) {
return new FirstMeetingServiceImpl(em, activityPlanFacade);
return new FirstMeetingServiceImpl(
em,
activityPlanFacade,
projectFirstMeetingRepository,
firstMeetingRepository
);
}
@Bean

@ -13,6 +13,7 @@ import se.su.dsv.scipro.finalseminar.AuthorRepositoryImpl;
import se.su.dsv.scipro.finalseminar.FinalSeminarActiveParticipationRepositoryImpl;
import se.su.dsv.scipro.finalseminar.FinalSeminarOppositionRepoImpl;
import se.su.dsv.scipro.finalseminar.FinalSeminarRepositoryImpl;
import se.su.dsv.scipro.firstmeeting.ProjectFirstMeetingRepositoryImpl;
import se.su.dsv.scipro.forum.AbstractThreadRepositoryImpl;
import se.su.dsv.scipro.forum.ForumPostReadStateRepositoryImpl;
import se.su.dsv.scipro.forum.ForumPostRepositoryImpl;
@ -131,6 +132,11 @@ public class RepositoryConfiguration {
return new FirstMeetingRepositoryImpl(em);
}
@Bean
public ProjectFirstMeetingRepositoryImpl projectFirstMeetingRepository(Provider<EntityManager> em) {
return new ProjectFirstMeetingRepositoryImpl(em);
}
@Bean
public FooterAddressRepoImpl footerAddressRepo(Provider<EntityManager> em) {
return new FooterAddressRepoImpl(em);

@ -1,6 +1,5 @@
package se.su.dsv.scipro.firstmeeting;
import com.querydsl.jpa.impl.JPAQuery;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.persistence.EntityManager;
@ -9,13 +8,12 @@ import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import se.su.dsv.scipro.activityplan.Activity;
import se.su.dsv.scipro.activityplan.ActivityPlanFacade;
import se.su.dsv.scipro.activityplan.QActivity;
import se.su.dsv.scipro.activityplan.QActivityPlan;
import se.su.dsv.scipro.match.FirstMeeting;
import se.su.dsv.scipro.match.FirstMeetingRepository;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.project.ProjectStatus;
import se.su.dsv.scipro.project.QProject;
import se.su.dsv.scipro.system.AbstractServiceImpl;
import se.su.dsv.scipro.system.User;
@ -24,11 +22,20 @@ public class FirstMeetingServiceImpl
implements FirstMeetingService {
private final ActivityPlanFacade activityPlanFacade;
private final ProjectFirstMeetingRepository projectFirstMeetingRepository;
private final FirstMeetingRepository firstMeetingRepository;
@Inject
public FirstMeetingServiceImpl(final Provider<EntityManager> em, final ActivityPlanFacade activityPlanFacade) {
public FirstMeetingServiceImpl(
final Provider<EntityManager> em,
final ActivityPlanFacade activityPlanFacade,
ProjectFirstMeetingRepository projectFirstMeetingRepository,
FirstMeetingRepository firstMeetingRepository
) {
super(em, ProjectFirstMeeting.class, QProjectFirstMeeting.projectFirstMeeting);
this.activityPlanFacade = activityPlanFacade;
this.projectFirstMeetingRepository = projectFirstMeetingRepository;
this.firstMeetingRepository = firstMeetingRepository;
}
@Override
@ -40,21 +47,11 @@ public class FirstMeetingServiceImpl
@Override
public List<MyCalendarEvent> findFirstMeetingsBySupervisor(User supervisor) {
JPAQuery<ProjectFirstMeeting> firstMeetingsQuery = createQuery()
.select(QProjectFirstMeeting.projectFirstMeeting)
.from(QProjectFirstMeeting.projectFirstMeeting)
.join(QProjectFirstMeeting.projectFirstMeeting.activity, QActivity.activity)
.join(QActivity.activity.activityPlan, QActivityPlan.activityPlan)
.join(QActivityPlan.activityPlan.project, QProject.project)
.where(
QProject.project.headSupervisor
.eq(supervisor)
.and(QProject.project.projectStatus.eq(ProjectStatus.ACTIVE))
);
List<ProjectFirstMeeting> projectFirstMeetings =
projectFirstMeetingRepository.getProjectFirstMeetingsBySupervisor(supervisor);
List<FirstMeeting> ideaFirstMeetings = firstMeetingRepository.findBySupervisor(supervisor);
List<ProjectFirstMeeting> projectFirstMeetings = firstMeetingsQuery.fetch();
return projectFirstMeetings
Stream<MyCalendarEvent> projectEvents = projectFirstMeetings
.stream()
.map(projectFirstMeeting ->
new MyCalendarEvent(
@ -69,8 +66,43 @@ public class FirstMeetingServiceImpl
projectFirstMeeting.getActivity().getActivityPlan().getProject().getTitle(),
projectFirstMeeting.getActivity().getActivityPlan().getProject().getId()
)
)
.toList();
);
Stream<MyCalendarEvent> ideaEvents = ideaFirstMeetings
.stream()
.filter(ideaFirstMeeting -> {
return isNotInProjectFirstMeetings(ideaFirstMeeting, projectFirstMeetings);
})
.map(ideaFirstMeeting ->
new MyCalendarEvent(
ideaFirstMeeting.getDescription(),
ideaFirstMeeting.getFirstMeetingDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
ideaFirstMeeting.getRoom(),
ideaFirstMeeting.getIdea().getTitle(),
(ideaFirstMeeting.getIdea().getProject() != null)
? ideaFirstMeeting.getIdea().getProject().getId()
: null
)
);
return Stream.concat(projectEvents, ideaEvents).toList();
}
private static boolean isNotInProjectFirstMeetings(
FirstMeeting ideaFirstMeeting,
List<ProjectFirstMeeting> projectFirstMeetings
) {
Project ideaProject = ideaFirstMeeting.getIdea().getProject();
if (ideaProject == null) {
return true;
} else {
for (ProjectFirstMeeting projectFirstMeeting : projectFirstMeetings) {
if (projectFirstMeeting.getActivity().getActivityPlan().getProject().equals(ideaProject)) {
return false;
}
}
return true;
}
}
@Override

@ -0,0 +1,8 @@
package se.su.dsv.scipro.firstmeeting;
import java.util.List;
import se.su.dsv.scipro.system.User;
public interface ProjectFirstMeetingRepository {
List<ProjectFirstMeeting> getProjectFirstMeetingsBySupervisor(User supervisor);
}

@ -0,0 +1,36 @@
package se.su.dsv.scipro.firstmeeting;
import com.querydsl.jpa.impl.JPAQuery;
import jakarta.inject.Provider;
import jakarta.persistence.EntityManager;
import java.util.List;
import se.su.dsv.scipro.activityplan.QActivity;
import se.su.dsv.scipro.activityplan.QActivityPlan;
import se.su.dsv.scipro.project.ProjectStatus;
import se.su.dsv.scipro.project.QProject;
import se.su.dsv.scipro.system.AbstractRepository;
import se.su.dsv.scipro.system.User;
public class ProjectFirstMeetingRepositoryImpl extends AbstractRepository implements ProjectFirstMeetingRepository {
public ProjectFirstMeetingRepositoryImpl(Provider<EntityManager> em) {
super(em);
}
@Override
public List<ProjectFirstMeeting> getProjectFirstMeetingsBySupervisor(User supervisor) {
JPAQuery<ProjectFirstMeeting> firstMeetingsQuery = from(QProjectFirstMeeting.projectFirstMeeting)
.select(QProjectFirstMeeting.projectFirstMeeting)
.join(QProjectFirstMeeting.projectFirstMeeting.activity, QActivity.activity)
.join(QActivity.activity.activityPlan, QActivityPlan.activityPlan)
.join(QActivityPlan.activityPlan.project, QProject.project)
.where(
QProject.project.headSupervisor
.eq(supervisor)
.and(QProject.project.projectStatus.eq(ProjectStatus.ACTIVE))
);
List<ProjectFirstMeeting> projectFirstMeetings = firstMeetingsQuery.fetch();
return projectFirstMeetings;
}
}

@ -1,9 +1,13 @@
package se.su.dsv.scipro.match;
import jakarta.transaction.Transactional;
import java.util.List;
import se.su.dsv.scipro.system.JpaRepository;
import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
import se.su.dsv.scipro.system.User;
@Transactional
public interface FirstMeetingRepository
extends JpaRepository<FirstMeeting, Long>, QueryDslPredicateExecutor<FirstMeeting> {}
extends JpaRepository<FirstMeeting, Long>, QueryDslPredicateExecutor<FirstMeeting> {
List<FirstMeeting> findBySupervisor(User supervisor);
}

@ -3,7 +3,9 @@ package se.su.dsv.scipro.match;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.persistence.EntityManager;
import java.util.List;
import se.su.dsv.scipro.system.GenericRepo;
import se.su.dsv.scipro.system.User;
public class FirstMeetingRepositoryImpl extends GenericRepo<FirstMeeting, Long> implements FirstMeetingRepository {
@ -11,4 +13,15 @@ public class FirstMeetingRepositoryImpl extends GenericRepo<FirstMeeting, Long>
public FirstMeetingRepositoryImpl(Provider<EntityManager> em) {
super(em, FirstMeeting.class, QFirstMeeting.firstMeeting);
}
@Override
public List<FirstMeeting> findBySupervisor(User supervisor) {
// QFirstMeeting.firstMeeting.idea.match.supervisor.eq(supervisor)
return createQuery()
.select(QFirstMeeting.firstMeeting)
.join(QFirstMeeting.firstMeeting.idea, QIdea.idea)
.join(QIdea.idea.match, QMatch.match)
.where(QMatch.match.supervisor.eq(supervisor))
.fetch();
}
}

@ -34,6 +34,10 @@
<span wicket:id="eventLabel"></span><br>
<span wicket:id="roomLabel"></span>
</a>
<div wicket:id="eventInfo">
<span wicket:id="eventLabel"></span><br>
<span wicket:id="roomLabel"></span>
</div>
</div>
</div>
</wicket:container>

@ -1,6 +1,11 @@
package se.su.dsv.scipro.supervisor.panels;
import jakarta.inject.Inject;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
@ -26,12 +31,6 @@ import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.system.UserService;
import se.su.dsv.scipro.util.PageParameterKeys;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;
public class MyCalendarPanel extends Panel {
@Inject
@ -149,21 +148,42 @@ public class MyCalendarPanel extends Panel {
MyCalendarEvent event = item.getModelObject();
item.add(new AttributeModifier("class", "calendar-event-container"));
item.add(new AttributeModifier("title", event.projectTitle()));
item.add(new AttributeModifier("title", item.getModel().map(MyCalendarEvent::title)));
PageParameters pp = new PageParameters();
pp.add(PageParameterKeys.MAP.get(Project.class), event.projectId());
if (event.projectId() != null) {
pp.add(PageParameterKeys.MAP.get(Project.class), event.projectId());
}
BookmarkablePageLink<Void> projectLink = new BookmarkablePageLink<>(
"projectLink",
SupervisorProjectDetailsPage.class,
pp
);
) {
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(item.getModelObject().projectId() != null);
}
};
Label eventLabel = new Label("eventLabel", event.title());
Label roomLabel = new Label("roomLabel", event.room());
WebMarkupContainer eventInfo = new WebMarkupContainer("eventInfo") {
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(item.getModelObject().projectId() == null);
}
};
Label eventLabel = new Label("eventLabel", item.getModel().map(MyCalendarEvent::title));
Label roomLabel = new Label("roomLabel", item.getModel().map(MyCalendarEvent::room));
projectLink.add(eventLabel);
projectLink.add(roomLabel);
eventInfo.add(new Label("eventLabel", item.getModel().map(MyCalendarEvent::title)));
eventInfo.add(new Label("roomLabel", item.getModel().map(MyCalendarEvent::room)));
item.add(projectLink);
item.add(eventInfo);
}
};