2874 Allow authors to give consent to publish their thesis

This commit is contained in:
Andreas Svanberg 2022-11-14 14:35:44 +01:00
parent 973addf196
commit 4fc1a661f7
16 changed files with 251 additions and 2 deletions

@ -2,6 +2,7 @@ package modules;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.OptionalBinder;
import se.su.dsv.scipro.activityplan.*;
import se.su.dsv.scipro.checklist.*;
import se.su.dsv.scipro.date.DateService;
@ -10,6 +11,8 @@ import se.su.dsv.scipro.events.EventModule;
import se.su.dsv.scipro.finalseminar.*;
import se.su.dsv.scipro.finalthesis.FinalThesisService;
import se.su.dsv.scipro.finalthesis.FinalThesisServiceImpl;
import se.su.dsv.scipro.finalthesis.PublishingConsentService;
import se.su.dsv.scipro.finalthesis.PublishingConsentUnavailable;
import se.su.dsv.scipro.firstmeeting.FirstMeetingService;
import se.su.dsv.scipro.firstmeeting.FirstMeetingServiceImpl;
import se.su.dsv.scipro.forum.ForumModule;
@ -110,6 +113,8 @@ public class CoreModule extends AbstractModule {
bind(PeerReviewService.class).to(PeerReviewServiceImpl.class);
bind(MilestoneActivityTemplateService.class).to(MilestoneActivityTemplateServiceImpl.class);
bind(FinalThesisService.class).to(FinalThesisServiceImpl.class);
OptionalBinder.newOptionalBinder(binder(), PublishingConsentService.class)
.setDefault().to(PublishingConsentUnavailable.class);
bind(ChecklistTemplateService.class).to(ChecklistTemplateServiceImpl.class);
bind(PeerPortal.class).to(PeerPortalImpl.class);
bind(FinalSeminarRespondentService.class).to(FinalSeminarRespondentServiceImpl.class);

@ -71,4 +71,8 @@ public interface DaisyAPI {
List<Employee> listEmployees(int departmentId);
OrganisationalUnit orgunit(int unitId);
PublishingConsent getPublishingConsent(int projectId, int personId);
boolean setPublishingConsent(int projectId, int personId, PublishingConsentLevel publishingConsentLevel);
}

@ -5,6 +5,7 @@ import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.MediaType;
@ -407,6 +408,32 @@ public class DaisyAPIImpl implements DaisyAPI {
.get(new GenericType<>() {});
}
@Override
public PublishingConsent getPublishingConsent(int projectId, int personId) {
return thesis()
.path(Integer.toString(projectId))
.path("author")
.path(Integer.toString(personId))
.path("publishingConsent")
.request(MediaType.APPLICATION_XML_TYPE)
.get(PublishingConsent.class);
}
@Override
public boolean setPublishingConsent(int projectId, int personId, PublishingConsentLevel publishingConsentLevel) {
final Invocation.Builder request = thesis()
.path(Integer.toString(projectId))
.path("author")
.path(Integer.toString(personId))
.path("publishingConsent")
.request(MediaType.APPLICATION_XML_TYPE);
// For some reason XML does not work
try (Response response = request.post(Entity.json(publishingConsentLevel))) {
return response.getStatus() == Response.Status.OK.getStatusCode();
}
}
private WebTarget program() {
return target()
.path(PROGRAM);

@ -7,7 +7,7 @@ import se.su.dsv.scipro.system.FilteredService;
import se.su.dsv.scipro.system.GenericService;
import java.io.Serializable;
import java.util.Date;
import java.util.*;
public interface FinalThesisService extends GenericService<FinalThesis, Long>,
FilteredService<FinalThesis, Long, FinalThesisService.Filter> {

@ -0,0 +1,20 @@
package se.su.dsv.scipro.finalthesis;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.User;
import java.util.List;
public interface PublishingConsentService {
boolean setPublishingConsent(Project project, User author, Level publishingConsent);
PublishingConsent getPublishingConsent(Project project, User author);
record PublishingConsent(Level selected, List<Level> available) {}
enum Level {
DO_NOT_PUBLISH,
PUBLISH_INTERNALLY,
PUBLISH
}
}

@ -0,0 +1,18 @@
package se.su.dsv.scipro.finalthesis;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.User;
import java.util.List;
public class PublishingConsentUnavailable implements PublishingConsentService {
@Override
public boolean setPublishingConsent(Project project, User author, Level publishingConsent) {
return false;
}
@Override
public PublishingConsent getPublishingConsent(Project project, User author) {
return new PublishingConsent(null, List.of());
}
}

@ -0,0 +1,58 @@
package se.su.dsv.scipro.integration.daisy;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.WebApplicationException;
import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
import se.su.dsv.scipro.finalthesis.PublishingConsentService;
import se.su.dsv.scipro.io.dto.PublishingConsentLevel;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.User;
import javax.inject.Inject;
import java.util.List;
import java.util.stream.Collectors;
class DaisyConsentService implements PublishingConsentService {
private static final PublishingConsent UNAVAILABLE = new PublishingConsent(null, List.of());
private final DaisyAPI daisyAPI;
@Inject
DaisyConsentService(DaisyAPI daisyAPI) {
this.daisyAPI = daisyAPI;
}
@Override
public boolean setPublishingConsent(Project project, User author, Level publishingConsent) {
final PublishingConsentLevel publishingConsentLevel = switch (publishingConsent) {
case DO_NOT_PUBLISH -> PublishingConsentLevel.DO_NOT_PUBLISH;
case PUBLISH_INTERNALLY -> PublishingConsentLevel.PUBLISH_INTERNALLY;
case PUBLISH -> PublishingConsentLevel.PUBLIC;
};
return daisyAPI.setPublishingConsent(project.getIdentifier(), author.getIdentifier(), publishingConsentLevel);
}
@Override
public PublishingConsent getPublishingConsent(Project project, User author) {
try {
final se.su.dsv.scipro.io.dto.PublishingConsent publishingConsent = daisyAPI.getPublishingConsent(project.getIdentifier(), author.getIdentifier());
final Level selected = fromDaisyLevel(publishingConsent.getSelected());
final List<Level> available = publishingConsent.getAvailables()
.stream()
.map(DaisyConsentService::fromDaisyLevel)
.sorted()
.collect(Collectors.toList());
return new PublishingConsent(selected, available);
} catch (ProcessingException | WebApplicationException e) {
return UNAVAILABLE;
}
}
private static Level fromDaisyLevel(final PublishingConsentLevel level) {
return switch (level) {
case DO_NOT_PUBLISH -> Level.DO_NOT_PUBLISH;
case PUBLISH_INTERNALLY -> Level.PUBLISH_INTERNALLY;
case PUBLIC -> Level.PUBLISH;
};
}
}

@ -1,11 +1,13 @@
package se.su.dsv.scipro.integration.daisy;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.OptionalBinder;
import com.google.inject.servlet.ServletModule;
import se.su.dsv.scipro.daisyExternal.ExternalImporter;
import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
import se.su.dsv.scipro.daisyExternal.impl.ExternalImporterDaisyImpl;
import se.su.dsv.scipro.daisyExternal.impl.ImporterTransactionsImpl;
import se.su.dsv.scipro.finalthesis.PublishingConsentService;
import se.su.dsv.scipro.integration.daisy.workers.ProjectExporter;
import se.su.dsv.scipro.integration.daisy.workers.UserImportWorker;
import se.su.dsv.scipro.io.ExternalExporter;
@ -33,6 +35,9 @@ public class DaisyModule extends ServletModule {
Multibinder.newSetBinder(binder(), UserSearchService.class)
.addBinding().to(DaisyUserSearchService.class);
OptionalBinder.newOptionalBinder(binder(), PublishingConsentService.class)
.setBinding().to(DaisyConsentService.class);
}
}

@ -11,6 +11,23 @@
<div wicket:id="revisionPanel"></div>
<div wicket:id="approvedPanel"></div>
<div wicket:id="formPanel"></div>
<wicket:enclosure child="consent_form">
<h5 class="mt-3">Publishing consent</h5>
<form wicket:id="consent_form">
<div wicket:id="feedback"></div>
<p>
Giving consent to publish does not mean the thesis <em>will</em> be published,
only that it <em>may</em> be published.
</p>
<div class="mb-3">
<label wicket:for="consent_level" class="col-form-label">
How may your thesis be published
</label>
<select wicket:id="consent_level" class="form-select"></select>
</div>
<button type="submit" class="btn btn-success btn-sm">Select</button>
</form>
</wicket:enclosure>
</wicket:panel>
</body>
</html>

@ -1,7 +1,11 @@
package se.su.dsv.scipro.finalthesis;
import org.apache.wicket.feedback.FencedFeedbackPanel;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.EnumLabel;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.EnumChoiceRenderer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.markup.html.panel.Panel;
@ -15,12 +19,16 @@ import se.su.dsv.scipro.finalseminar.FinalSeminarService;
import se.su.dsv.scipro.forum.pages.ProjectForumBasePage;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.project.pages.ProjectFinalSurveyPage;
import se.su.dsv.scipro.reflection.ReflectionService;
import se.su.dsv.scipro.security.auth.ProjectModuleComponent;
import se.su.dsv.scipro.session.SciProSession;
import se.su.dsv.scipro.system.ProjectModule;
import se.su.dsv.scipro.util.PageParameterKeys;
import javax.inject.Inject;
import java.util.List;
import static se.su.dsv.scipro.finalthesis.FinalThesis.Status;
@ProjectModuleComponent(ProjectModule.GRADING)
@ -38,6 +46,10 @@ public class FinalThesisPanel extends GenericPanel<Project> {
private FinalThesisService finalThesisService;
@Inject
private FinalSeminarService finalSeminarService;
@Inject
private PublishingConsentService publishingConsentService;
@Inject
private ReflectionService reflectionService;
public FinalThesisPanel(String id, IModel<Project> project) {
super(id, project);
@ -64,6 +76,7 @@ public class FinalThesisPanel extends GenericPanel<Project> {
setResponsePage(ProjectFinalSurveyPage.class, ProjectFinalSurveyPage.getPageParameters(getModelObject()));
}
});
add(new ConsentForm("consent_form", project));
}
@Override
@ -125,4 +138,47 @@ public class FinalThesisPanel extends GenericPanel<Project> {
};
}
private class ConsentForm extends Form<Project> {
final IModel<PublishingConsentService.Level> levelModel;
private final IModel<List<PublishingConsentService.Level>> availableLevels;
public ConsentForm(final String id, IModel<Project> project) {
super(id, project);
IModel<PublishingConsentService.PublishingConsent> publishingConsent = LoadableDetachableModel.of(
() -> publishingConsentService.getPublishingConsent(project.getObject(), SciProSession.get().getUser()));
availableLevels = publishingConsent.map(PublishingConsentService.PublishingConsent::available);
levelModel = LoadableDetachableModel.of(() -> publishingConsent.getObject().selected());
add(new DropDownChoice<>(
"consent_level",
levelModel,
availableLevels,
new EnumChoiceRenderer<>(this)));
add(new FencedFeedbackPanel("feedback", this));
}
@Override
protected void onConfigure() {
super.onConfigure();
boolean submittedReflection = !reflectionService.hasToFillInReflection(getModelObject(), SciProSession.get().getUser());
setVisibilityAllowed(finalThesisService.hasFinalThesis(getModelObject()) && !availableLevels.getObject().isEmpty() && submittedReflection);
}
@Override
protected void onSubmit() {
super.onSubmit();
final boolean success = publishingConsentService.setPublishingConsent(
getModelObject(),
SciProSession.get().getUser(),
levelModel.getObject());
if (success) {
success(getString("publishing_consent_level_changed"));
} else {
error(getString("failed_to_update_consent_level"));
levelModel.detach();
}
}
}
}

@ -1 +1,3 @@
notYet= Final seminar must be conducted first.
notYet= Final seminar must be conducted first.
publishing_consent_level_changed=Publishing consent updated.
failed_to_update_consent_level=Failed to change publishing consent

@ -32,6 +32,12 @@
</label>
<textarea class="form-control" wicket:id="reflection" id="reflection" rows="10" required></textarea>
</div>
<fieldset class="mb-3">
<legend>
<wicket:message key="publishing_consent"/>
</legend>
<wicket:container wicket:id="publishingConsentLevel"></wicket:container>
</fieldset>
<button type="submit" class="btn btn-success btn-sm"><wicket:message key="submit">[Submit]</wicket:message></button>
</form>
</wicket:panel>

@ -2,22 +2,30 @@ package se.su.dsv.scipro.project.panels;
import org.apache.wicket.feedback.FencedFeedbackPanel;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.EnumChoiceRenderer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import se.su.dsv.scipro.components.BootstrapRadioChoice;
import se.su.dsv.scipro.finalthesis.PublishingConsentService;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.reflection.ReflectionService;
import se.su.dsv.scipro.session.SciProSession;
import javax.inject.Inject;
import java.io.Serializable;
public class ReflectionPanel extends GenericPanel<Project> {
@Inject
private ReflectionService reflectionService;
@Inject
private PublishingConsentService publishingConsentService;
public ReflectionPanel(String id, IModel<Project> projectModel) {
super(id, projectModel);
@ -39,6 +47,7 @@ public class ReflectionPanel extends GenericPanel<Project> {
private class ReflectionForm extends Form<Project> {
private IModel<String> reflectionModel;
private IModel<PublishingConsentService.Level> levelModel;
public ReflectionForm(String id, IModel<Project> projectModel) {
super(id, projectModel);
@ -48,6 +57,18 @@ public class ReflectionPanel extends GenericPanel<Project> {
TextArea<String> reflectionTextArea = new TextArea<>("reflection", reflectionModel);
reflectionTextArea.setRequired(true);
add(reflectionTextArea);
IModel<PublishingConsentService.PublishingConsent> publishingConsent = LoadableDetachableModel.of(() ->
publishingConsentService.getPublishingConsent(getModelObject(), SciProSession.get().getUser()));
levelModel = new Model<>();
FormComponent<PublishingConsentService.Level> publishingConsentLevel = new BootstrapRadioChoice<>(
"publishingConsentLevel",
levelModel,
publishingConsent.map(PublishingConsentService.PublishingConsent::available),
new EnumChoiceRenderer<>(this));
publishingConsentLevel.setRequired(true);
add(publishingConsentLevel);
}
@Override
@ -60,6 +81,7 @@ public class ReflectionPanel extends GenericPanel<Project> {
protected void onSubmit() {
super.onSubmit();
reflectionService.submitReflection(getModelObject(), SciProSession.get().getUser(), reflectionModel.getObject());
publishingConsentService.setPublishingConsent(getModelObject(), SciProSession.get().getUser(), levelModel.getObject());
success(getString("reflection_submitted"));
}
}

@ -2,3 +2,5 @@ reflection_submitted = Reflection submitted
explain_reflection = You've had your final seminar, and it is now time to reflect back on the entire thesis process.
reflection = Reflection
submit = Submit
publishing_consent=How may your thesis be published
publishingConsentLevel.Required=You must decide how your thesis may be published.

@ -115,3 +115,7 @@ nullValid=Choose one
research_area_inactive=${title} (inactive)
research_area_active=${title}
Level.DO_NOT_PUBLISH=Do not publish
Level.PUBLISH_INTERNALLY=Available to students within the department
Level.PUBLISH=Public

@ -42,6 +42,7 @@ import se.su.dsv.scipro.finalseminar.FinalSeminarService;
import se.su.dsv.scipro.finalseminar.FinalSeminarSettingsService;
import se.su.dsv.scipro.finalseminar.FinalSeminarUploadController;
import se.su.dsv.scipro.finalthesis.FinalThesisService;
import se.su.dsv.scipro.finalthesis.PublishingConsentService;
import se.su.dsv.scipro.forum.BasicForumService;
import se.su.dsv.scipro.forum.GroupForumService;
import se.su.dsv.scipro.forum.ProjectForumService;
@ -342,6 +343,8 @@ public abstract class SciProTest {
protected Reporter reporter;
@Mock
protected ReflectionService reflectionService;
@Mock
protected PublishingConsentService publishingConsentService;
protected WicketTester tester;