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

View File

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

View File

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

View File

@ -117,7 +117,7 @@ public class NotificationDataPanel extends Panel {
}
@Override
protected void toggleReadState(final AjaxRequestTarget target) {
protected void onFlagClick(final AjaxRequestTarget target) {
Notification notification = rowModel.getObject();
notification.setUnread(!notification.isUnread());
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.form.AjaxFormChoiceComponentUpdatingBehavior;
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.table.AbstractColumn;
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.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.LambdaChoiceRenderer;
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.LambdaModel;
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.datatables.project.ProjectStateColumn;
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.ProjectService;
import se.su.dsv.scipro.project.ProjectStatus;
@ -54,6 +60,8 @@ public class SupervisorMyProjectsPanel extends Panel {
private ProjectService projectService;
@Inject
private UserProfileService profileService;
@Inject
private ProjectForumService projectForumService;
private ExportableDataPanel dataPanel;
private ProjectService.Filter filter = new ProjectService.Filter();
@ -76,6 +84,7 @@ public class SupervisorMyProjectsPanel extends Panel {
private List<IColumn<Project, String>> createColumns() {
List<IColumn<Project, String>> columns = new ArrayList<>();
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 LambdaColumn<>(Model.of("Type"), "projectType.name", Project::getProjectTypeName));
columns.add(new ProjectTitleColumn(Model.of("Title"), "title"));
@ -177,4 +186,29 @@ public class SupervisorMyProjectsPanel extends Panel {
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()));
}
});
}
}
}