Allow supervisors to write a note associated with their projects #8

Merged
ansv7779 merged 10 commits from 3399-supervisor-project-note into develop 2024-07-10 13:43:27 +02:00
7 changed files with 74 additions and 16 deletions
Showing only changes of commit e11b2cc22a - Show all commits

View File

@ -25,4 +25,5 @@ public interface ProjectForumService {
// TODO: Get these away from here // TODO: Get these away from here
List<Pair<ProjectThread, ForumPost>> latestPost(Project a, int amount); List<Pair<ProjectThread, ForumPost>> latestPost(Project a, int amount);
boolean hasUnreadThreads(Project project, User user);
} }

View File

@ -95,6 +95,17 @@ public class ProjectForumServiceImpl implements ProjectForumService {
return postRepository.latestPost(project, amount); return postRepository.latestPost(project, amount);
} }
@Override
public boolean hasUnreadThreads(Project project, User user) {
List<ProjectThread> threads = getThreads(project);
for (ProjectThread thread : threads) {
if (!basicForumService.isThreadRead(user, thread.getForumThread())) {
return true;
}
}
return false;
}
@Override @Override
public ProjectThread findOne(long threadId) { public ProjectThread findOne(long threadId) {
return projectThreadRepository.findOne(threadId); return projectThreadRepository.findOne(threadId);

View File

@ -20,6 +20,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ProjectForumServiceImplTest extends ForumModuleTest { public class ProjectForumServiceImplTest extends ForumModuleTest {
@ -29,23 +30,24 @@ public class ProjectForumServiceImplTest extends ForumModuleTest {
ProjectForumService service; ProjectForumService service;
private Project project; private Project project;
private User user; private User author;
private User supervisor;
@BeforeEach @BeforeEach
public void setUp() throws Exception { public void setUp() throws Exception {
ProjectType projectType = new ProjectType(DegreeType.BACHELOR, "Some project type", "Some description"); ProjectType projectType = new ProjectType(DegreeType.BACHELOR, "Some project type", "Some description");
projectType.addModule(ProjectModule.FORUM); projectType.addModule(ProjectModule.FORUM);
save(projectType); save(projectType);
final User supervisor = save(User.builder().firstName("Bob").lastName("The Builder").emailAddress("bob@example.com").build()); supervisor = save(User.builder().firstName("Bob").lastName("The Builder").emailAddress("bob@example.com").build());
project = Project.builder().title("Some title").projectType(projectType).startDate(LocalDate.now()).headSupervisor(supervisor).build(); project = Project.builder().title("Some title").projectType(projectType).startDate(LocalDate.now()).headSupervisor(supervisor).build();
save(project); save(project);
user = User.builder().firstName("Stina").lastName("Student").emailAddress("stina@example.com").build(); author = User.builder().firstName("Stina").lastName("Student").emailAddress("stina@example.com").build();
save(user); save(author);
} }
@Test @Test
public void testLoadThread() { public void testLoadThread() {
final ProjectThread thread = service.createThread(project, user, "subject", "content", Set.of()); final ProjectThread thread = service.createThread(project, author, "subject", "content", Set.of());
ProjectThread serviceThread = service.findOne(thread.getId()); ProjectThread serviceThread = service.findOne(thread.getId());
assertEquals(thread, serviceThread); assertEquals(thread, serviceThread);
@ -55,17 +57,17 @@ public class ProjectForumServiceImplTest extends ForumModuleTest {
public void testCreateThreadWithPostAttachment() throws Exception { public void testCreateThreadWithPostAttachment() throws Exception {
final String file = "attachment.txt"; final String file = "attachment.txt";
try (var is = ProjectForumServiceImplTest.class.getResourceAsStream(file)) { try (var is = ProjectForumServiceImplTest.class.getResourceAsStream(file)) {
final StreamingUpload upload = new StreamingUpload(file, "text/plain", user, 2, is); final StreamingUpload upload = new StreamingUpload(file, "text/plain", author, 2, is);
final ProjectThread thread = service.createThread(project, user, SUBJECT, CONTENT, Set.of(Attachment.newUpload(upload))); final ProjectThread thread = service.createThread(project, author, SUBJECT, CONTENT, Set.of(Attachment.newUpload(upload)));
assertNewForumThread(thread, project, user, SUBJECT, CONTENT, file); assertNewForumThread(thread, project, author, SUBJECT, CONTENT, file);
} }
} }
@Test @Test
public void testGetPostPageByForumThread() { public void testGetPostPageByForumThread() {
final ProjectThread thread = service.createThread(project, user, "subject", "content", Set.of()); final ProjectThread thread = service.createThread(project, author, "subject", "content", Set.of());
final ForumPost reply = service.createReply(thread, user, "reply", Set.of()); final ForumPost reply = service.createReply(thread, author, "reply", Set.of());
List<ForumPost> servicePage = service.getPosts(thread); List<ForumPost> servicePage = service.getPosts(thread);
@ -76,8 +78,8 @@ public class ProjectForumServiceImplTest extends ForumModuleTest {
@Test @Test
public void testGetThreadsByProject() { public void testGetThreadsByProject() {
final ProjectThread thread1 = service.createThread(project, user, "subject 1", "content", Set.of()); final ProjectThread thread1 = service.createThread(project, author, "subject 1", "content", Set.of());
final ProjectThread thread2 = service.createThread(project, user, "subject 2", "content", Set.of()); final ProjectThread thread2 = service.createThread(project, author, "subject 2", "content", Set.of());
List<ProjectThread> serviceThreads = service.getThreads(project); List<ProjectThread> serviceThreads = service.getThreads(project);
@ -86,6 +88,16 @@ public class ProjectForumServiceImplTest extends ForumModuleTest {
assertThat(serviceThreads, hasSize(2)); assertThat(serviceThreads, hasSize(2));
} }
@Test
public void supervisor_has_unread_forum_threads_after_student_replies() {
final ProjectThread thread = service.createThread(project, supervisor, "subject", "content", Set.of());
service.createReply(thread, author, "reply", Set.of());
boolean hasUnreadThreads = service.hasUnreadThreads(project, supervisor);
assertTrue(hasUnreadThreads);
}
private void assertNewForumThread( private void assertNewForumThread(
ProjectThread thread, Project project, User user, String subject, String content, String attachmentFileName) { ProjectThread thread, Project project, User user, String subject, String content, String attachmentFileName) {
assertEquals(project, thread.getProject(), "Thread created for the wrong project"); assertEquals(project, thread.getProject(), "Thread created for the wrong project");

View File

@ -20,7 +20,7 @@ public abstract class AbstractReadStatePanel extends Panel {
@Override @Override
public void onClick(final Optional<AjaxRequestTarget> target) { public void onClick(final Optional<AjaxRequestTarget> target) {
target.ifPresent(t -> { target.ifPresent(t -> {
toggleReadState(t); onFlagClick(t);
t.add(icon); t.add(icon);
}); });
} }
@ -33,7 +33,7 @@ public abstract class AbstractReadStatePanel extends Panel {
} }
protected abstract boolean isRead(); protected abstract boolean isRead();
protected abstract void toggleReadState(final AjaxRequestTarget target); protected abstract void onFlagClick(final AjaxRequestTarget target);
public static final String TOGGLE = "toggle"; public static final String TOGGLE = "toggle";
static final String ICON = "icon"; static final String ICON = "icon";

View File

@ -31,7 +31,7 @@ public class ThreadReadStatePanel extends AbstractReadStatePanel {
} }
@Override @Override
protected void toggleReadState(final AjaxRequestTarget target) { protected void onFlagClick(final AjaxRequestTarget target) {
boolean read = isRead(); boolean read = isRead();
basicForumService.setThreadRead(SciProSession.get().getUser(), model.getObject(), !read); basicForumService.setThreadRead(SciProSession.get().getUser(), model.getObject(), !read);

View File

@ -117,7 +117,7 @@ public class NotificationDataPanel extends Panel {
} }
@Override @Override
protected void toggleReadState(final AjaxRequestTarget target) { protected void onFlagClick(final AjaxRequestTarget target) {
Notification notification = rowModel.getObject(); Notification notification = rowModel.getObject();
notification.setUnread(!notification.isUnread()); notification.setUnread(!notification.isUnread());
notificationService.save(notification); notificationService.save(notification);

View File

@ -3,7 +3,9 @@ package se.su.dsv.scipro.supervisor.panels;
import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior; import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder; import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.LambdaColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.LambdaColumn;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
@ -11,6 +13,7 @@ import org.apache.wicket.markup.html.form.EnumChoiceRenderer;
import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.LambdaChoiceRenderer; import org.apache.wicket.markup.html.form.LambdaChoiceRenderer;
import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.model.IModel; import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LambdaModel; import org.apache.wicket.model.LambdaModel;
import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.LoadableDetachableModel;
@ -21,6 +24,9 @@ import se.su.dsv.scipro.components.datatables.UserColumn;
import se.su.dsv.scipro.dataproviders.FilteredDataProvider; import se.su.dsv.scipro.dataproviders.FilteredDataProvider;
import se.su.dsv.scipro.datatables.project.ProjectStateColumn; import se.su.dsv.scipro.datatables.project.ProjectStateColumn;
import se.su.dsv.scipro.datatables.project.ProjectTitleColumn; import se.su.dsv.scipro.datatables.project.ProjectTitleColumn;
import se.su.dsv.scipro.forum.ProjectForumService;
import se.su.dsv.scipro.forum.pages.threaded.SupervisorThreadedForumPage;
import se.su.dsv.scipro.forum.panels.AbstractReadStatePanel;
import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ProjectService;
import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.project.ProjectStatus;
@ -54,6 +60,8 @@ public class SupervisorMyProjectsPanel extends Panel {
private ProjectService projectService; private ProjectService projectService;
@Inject @Inject
private UserProfileService profileService; private UserProfileService profileService;
@Inject
private ProjectForumService projectForumService;
private ExportableDataPanel dataPanel; private ExportableDataPanel dataPanel;
private ProjectService.Filter filter = new ProjectService.Filter(); private ProjectService.Filter filter = new ProjectService.Filter();
@ -76,6 +84,7 @@ public class SupervisorMyProjectsPanel extends Panel {
private List<IColumn<Project, String>> createColumns() { private List<IColumn<Project, String>> createColumns() {
List<IColumn<Project, String>> columns = new ArrayList<>(); List<IColumn<Project, String>> columns = new ArrayList<>();
columns.add(new ProjectStateColumn(Model.of("State"), "stateOfMind")); columns.add(new ProjectStateColumn(Model.of("State"), "stateOfMind"));
columns.add(new ProjectForumStateColumn(Model.of("Forum")));
columns.add(new TemporalColumn<>(Model.of("Started"), "startDate", Project::getStartDate)); columns.add(new TemporalColumn<>(Model.of("Started"), "startDate", Project::getStartDate));
columns.add(new LambdaColumn<>(Model.of("Type"), "projectType.name", Project::getProjectTypeName)); columns.add(new LambdaColumn<>(Model.of("Type"), "projectType.name", Project::getProjectTypeName));
columns.add(new ProjectTitleColumn(Model.of("Title"), "title")); columns.add(new ProjectTitleColumn(Model.of("Title"), "title"));
@ -177,4 +186,29 @@ public class SupervisorMyProjectsPanel extends Panel {
profileService.save(userProfile); profileService.save(userProfile);
} }
} }
private class ProjectForumStateColumn extends AbstractColumn<Project, String> {
public ProjectForumStateColumn(IModel<String> label) {
super(label);
}
@Override
public void populateItem(Item<ICellPopulator<Project>> item, String id, IModel<Project> projectModel) {
item.add(new AbstractReadStatePanel(id) {
@Override
protected boolean isRead() {
return !projectForumService.hasUnreadThreads(
projectModel.getObject(),
SciProSession.get().getUser());
}
@Override
protected void onFlagClick(AjaxRequestTarget target) {
setResponsePage(
SupervisorThreadedForumPage.class,
SupervisorThreadedForumPage.getPageParameters(projectModel.getObject()));
}
});
}
}
} }