From 9c795a7576aec91d9fcc4eb75499678baa888d5f Mon Sep 17 00:00:00 2001 From: Sven Fillinger Date: Fri, 18 Oct 2024 09:33:02 +0200 Subject: [PATCH] Finalise download --- .../datamanager/export/TempDirectory.java | 25 +++--- .../export/docx/DocxFormatter.java | 32 ++++++- .../export/model/ContactPoint.java | 22 ++++- .../export/model/ResearchProject.java | 17 +++- .../export/rocrate/ROCreateBuilder.java | 85 +++++++++++++++---- .../project/info/ProjectEditEvent.java | 6 +- .../project/info/ProjectInformationMain.java | 10 +-- ...nent.java => ProjectSummaryComponent.java} | 6 +- 8 files changed, 159 insertions(+), 44 deletions(-) rename user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/{ProjectDetailsComponent.java => ProjectSummaryComponent.java} (98%) diff --git a/user-interface/src/main/java/life/qbic/datamanager/export/TempDirectory.java b/user-interface/src/main/java/life/qbic/datamanager/export/TempDirectory.java index cb8d37889..67bdf9413 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/export/TempDirectory.java +++ b/user-interface/src/main/java/life/qbic/datamanager/export/TempDirectory.java @@ -8,9 +8,10 @@ import org.springframework.stereotype.Component; /** - * - * - *

+ * Temporary Director + *

+ * Provides a temporary directory for the application to use when files need to be created for + * exporting them. * * @since */ @@ -23,15 +24,15 @@ public TempDirectory(@Value("${service.host.temp.dir}") String tempDirectoryPath throws IOException { Path tempDirPath = Path.of(tempDirectoryPath); if (!Files.exists(tempDirPath)) { - throw new IOException(tempDirectoryPath + " does not exist"); - } - if (!Files.isWritable(tempDirPath)) { - throw new IOException(tempDirectoryPath + " is not writable"); - } - if (!Files.isExecutable(tempDirPath)) { - throw new IOException(tempDirectoryPath + " is not executable"); - } - tempDir = tempDirPath; + throw new IOException(tempDirectoryPath + " does not exist"); + } + if (!Files.isWritable(tempDirPath)) { + throw new IOException(tempDirectoryPath + " is not writable"); + } + if (!Files.isExecutable(tempDirPath)) { + throw new IOException(tempDirectoryPath + " is not executable"); + } + tempDir = tempDirPath; } /** diff --git a/user-interface/src/main/java/life/qbic/datamanager/export/docx/DocxFormatter.java b/user-interface/src/main/java/life/qbic/datamanager/export/docx/DocxFormatter.java index 496eb03e7..f6139baa2 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/export/docx/DocxFormatter.java +++ b/user-interface/src/main/java/life/qbic/datamanager/export/docx/DocxFormatter.java @@ -1,10 +1,13 @@ package life.qbic.datamanager.export.docx; import java.io.File; +import java.util.List; import life.qbic.datamanager.export.Formatter; +import life.qbic.datamanager.export.model.ContactPoint; import life.qbic.datamanager.export.model.ResearchProject; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; /** * @@ -23,7 +26,12 @@ public static DocxFormatter create() { public File from(String fileName, ResearchProject researchProject) { try { var wordPackage = WordprocessingMLPackage.createPackage(); - wordPackage.getMainDocumentPart().addParagraphOfText(researchProject.name()); + var mainDocument = wordPackage.getMainDocumentPart(); + addTitle(mainDocument, researchProject); + addProjectId(mainDocument, researchProject); + addSection(mainDocument, "Description", researchProject.description()); + addSectionTitle(mainDocument, "Contact Points"); + researchProject.contactPoint().forEach(contactPoint -> addContactPoint(mainDocument, contactPoint)); File file = new File(fileName); wordPackage.save(file); return file; @@ -31,4 +39,26 @@ public File from(String fileName, ResearchProject researchProject) { throw new FormatException("Creating docx package failed. ", e); } } + + private void addTitle(MainDocumentPart mainDocumentPart, ResearchProject researchProject) { + mainDocumentPart.addStyledParagraphOfText("Title", researchProject.name()); + } + + private void addProjectId(MainDocumentPart mainDocumentPart, ResearchProject researchProject) { + mainDocumentPart.addStyledParagraphOfText("Subtitle", "Project ID: " + researchProject.identifier()); + } + + private void addSection(MainDocumentPart mainDocumentPart, String sectionTitle, String sectionContent) { + mainDocumentPart.addStyledParagraphOfText("Heading1", sectionTitle); + mainDocumentPart.addParagraphOfText(sectionContent); + } + + private void addSectionTitle(MainDocumentPart mainDocumentPart, String title) { + mainDocumentPart.addStyledParagraphOfText("Heading1", title); + } + + private void addContactPoint(MainDocumentPart mainDocumentPart, ContactPoint contactPoint) { + var contactPointFormatted = "%s (%s) - %s".formatted(contactPoint.name(), contactPoint.contactType(), contactPoint.email()); + mainDocumentPart.addParagraphOfText(contactPointFormatted); + } } diff --git a/user-interface/src/main/java/life/qbic/datamanager/export/model/ContactPoint.java b/user-interface/src/main/java/life/qbic/datamanager/export/model/ContactPoint.java index 70cb9982f..b9d6699ed 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/export/model/ContactPoint.java +++ b/user-interface/src/main/java/life/qbic/datamanager/export/model/ContactPoint.java @@ -9,8 +9,8 @@ */ public class ContactPoint { - @JsonProperty(value = "@type", defaultValue = "ContactPoint") - private String type; + @JsonProperty(value = "@type") + private final String type = "ContactPoint"; @JsonProperty(value = "name") private String name; @@ -18,11 +18,27 @@ public class ContactPoint { @JsonProperty(value = "email") private String email; - public static ContactPoint from(String name, String email) { + @JsonProperty(value = "contactType") + private String contactType; + + public static ContactPoint from(String name, String email, String contactType) { ContactPoint contactPoint = new ContactPoint(); contactPoint.name = name; contactPoint.email = email; + contactPoint.contactType = contactType; return contactPoint; } + public String name() { + return name; + } + + public String email() { + return email; + } + + public String contactType() { + return contactType; + } + } diff --git a/user-interface/src/main/java/life/qbic/datamanager/export/model/ResearchProject.java b/user-interface/src/main/java/life/qbic/datamanager/export/model/ResearchProject.java index dbe05665d..b8ac517f2 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/export/model/ResearchProject.java +++ b/user-interface/src/main/java/life/qbic/datamanager/export/model/ResearchProject.java @@ -1,6 +1,7 @@ package life.qbic.datamanager.export.model; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; /** * A schema.org ResearchProject representation in Java. @@ -10,7 +11,7 @@ public class ResearchProject { @JsonProperty(value = "@type") - private String type = "ResearchProject"; + private final String type = "ResearchProject"; @JsonProperty(value = "name") private String name; @@ -21,11 +22,15 @@ public class ResearchProject { @JsonProperty(value = "description") private String description; - public static ResearchProject from(String name, String identifier, String description) { + @JsonProperty(value = "contactPoint") + private List contactPoint; + + public static ResearchProject from(String name, String identifier, String description, List contactPoint) { ResearchProject project = new ResearchProject(); project.name = name; project.identifier = identifier; project.description = description; + project.contactPoint = contactPoint.stream().toList(); return project; } @@ -37,4 +42,12 @@ public String identifier() { return identifier; } + public String description() { + return description; + } + + public List contactPoint() { + return contactPoint.stream().toList(); + } + } diff --git a/user-interface/src/main/java/life/qbic/datamanager/export/rocrate/ROCreateBuilder.java b/user-interface/src/main/java/life/qbic/datamanager/export/rocrate/ROCreateBuilder.java index 1825d5844..d0c3da2c1 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/export/rocrate/ROCreateBuilder.java +++ b/user-interface/src/main/java/life/qbic/datamanager/export/rocrate/ROCreateBuilder.java @@ -1,24 +1,31 @@ package life.qbic.datamanager.export.rocrate; +import static life.qbic.datamanager.export.rocrate.ROCreateBuilder.ResearchProjectConstants.SUMMARY_FILENAME_DOCX; +import static life.qbic.datamanager.export.rocrate.ROCreateBuilder.ResearchProjectConstants.SUMMARY_FILENAME_YAML; + import edu.kit.datamanager.ro_crate.RoCrate; import edu.kit.datamanager.ro_crate.entities.data.FileEntity; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Objects; import life.qbic.datamanager.export.TempDirectory; import life.qbic.datamanager.export.docx.DocxFormatter; +import life.qbic.datamanager.export.model.ContactPoint; import life.qbic.datamanager.export.model.ResearchProject; import life.qbic.datamanager.export.yaml.YamlFormatter; +import life.qbic.projectmanagement.domain.model.project.Contact; import life.qbic.projectmanagement.domain.model.project.Project; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** - * - * - *

+ * RO-Crate Builder + *

+ * Builder class that helps to build a RO-Crate based on various QBiC data manager project + * information. * - * @since + * @since 1.6.0 */ @Component public class ROCreateBuilder { @@ -32,9 +39,9 @@ public ROCreateBuilder(TempDirectory tempDir) { private static RoCrate buildRoCrate(Path buildDir, ResearchProject researchProject) { var projectInfoDocx = DocxFormatter.create() - .from(buildDir.resolve("project-information.docx").toString(), researchProject); + .from(buildDir.resolve(SUMMARY_FILENAME_DOCX.value()).toString(), researchProject); var projectInfoYaml = YamlFormatter.create() - .from(buildDir.resolve("project-information.yml").toString(), researchProject); + .from(buildDir.resolve(SUMMARY_FILENAME_YAML.value()).toString(), researchProject); return new RoCrate.RoCrateBuilder( "QBiC-project-%s-ro-crate".formatted(researchProject.identifier()), "Description of the project %s with the title '%s', managed on the Data Manager, Quantitative Biology Center, University of Tübingen.".formatted( @@ -42,22 +49,22 @@ private static RoCrate buildRoCrate(Path buildDir, ResearchProject researchProje .addDataEntity( new FileEntity.FileEntityBuilder() .setSource(projectInfoDocx) - .setId("project-information.docx") - .addProperty("name", "Project Information") + .setId(SUMMARY_FILENAME_DOCX.value()) + .addProperty("name", "Project Summary") .addProperty("encodingFormat", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document") + MimeTypes.DOCX.value()) .build()) .addDataEntity( new FileEntity.FileEntityBuilder() .setSource(projectInfoYaml) - .setId("project-information.yml") - .addProperty("name", "Project Information") - .addProperty("encodingFormat", "application/yaml") + .setId(SUMMARY_FILENAME_YAML.value()) + .addProperty("name", "Project Summary") + .addProperty("encodingFormat", MimeTypes.YAML.value()) .build()) .build(); } - public RoCrate projectInformation(Project project) throws ROCrateBuildException { + public RoCrate projectSummary(Project project) throws ROCrateBuildException { var researchProject = convertToResearchProject(project); try { var assignedBuildDirectory = tempDirectory.createDirectory(); @@ -69,8 +76,57 @@ public RoCrate projectInformation(Project project) throws ROCrateBuildException } private ResearchProject convertToResearchProject(Project project) { + var contactPoints = new ArrayList(); + contactPoints.add(toContactPoint(project.getPrincipalInvestigator(), "Principal Investigator")); + contactPoints.add(toContactPoint(project.getProjectManager(), "Project Manager")); + if (project.getResponsiblePerson().isPresent()) { + contactPoints.add(toContactPoint(project.getResponsiblePerson().get(), "Responsible Person")); + } return ResearchProject.from(project.getProjectIntent().projectTitle().title(), - project.getProjectCode().value(), project.getProjectIntent().objective().objective()); + project.getProjectCode().value(), project.getProjectIntent().objective().objective(), + contactPoints); + } + + private ContactPoint toContactPoint(Contact contact, String contactType) { + return ContactPoint.from(contact.fullName(), contact.emailAddress(), contactType); + } + + enum ResearchProjectConstants implements Value { + + SUMMARY_FILENAME_DOCX("project-summary.docx"), + SUMMARY_FILENAME_YAML("project-summary.yml"); + + private final String value; + + ResearchProjectConstants(String value) { + this.value = value; + } + + public String value() { + return value; + } + } + + enum MimeTypes implements Value { + + DOCX("application/vnd.openxmlformats-officedocument.wordprocessingml.document"), + YAML("application/yaml"); + + private final String value; + + MimeTypes(String value) { + this.value = value; + } + + @Override + public String value() { + return value; + } + } + + interface Value { + + String value(); } public static class ROCrateBuildException extends RuntimeException { @@ -83,5 +139,4 @@ public ROCrateBuildException(String message, Throwable cause) { super(message, cause); } } - } diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectEditEvent.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectEditEvent.java index 4b51cdad9..d79eed491 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectEditEvent.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectEditEvent.java @@ -8,11 +8,11 @@ * Project Edit Event *

* Event that indicates that the user wants to edit a project via the - * {@link ProjectDetailsComponent} + * {@link ProjectSummaryComponent} * * @since 1.0.0 */ -public class ProjectEditEvent extends ComponentEvent { +public class ProjectEditEvent extends ComponentEvent { @Serial private static final long serialVersionUID = -4045489562991683868L; @@ -27,7 +27,7 @@ public class ProjectEditEvent extends ComponentEvent { * @param fromClient true if the event originated from the client * side, false otherwise */ - public ProjectEditEvent(ProjectDetailsComponent source, ProjectId projectId, + public ProjectEditEvent(ProjectSummaryComponent source, ProjectId projectId, boolean fromClient) { super(source, fromClient); this.projectId = projectId; diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectInformationMain.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectInformationMain.java index 0a5de67df..2042c78a3 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectInformationMain.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectInformationMain.java @@ -84,7 +84,7 @@ public class ProjectInformationMain extends Main implements BeforeEnterObserver private final transient ProjectPurchaseService projectPurchaseService; private final transient QualityControlService qualityControlService; private final transient UserPermissions userPermissions; - private final ProjectDetailsComponent projectDetailsComponent; + private final ProjectSummaryComponent projectSummaryComponent; private final ExperimentListComponent experimentListComponent; private final OfferDownload offerDownload; private final QualityControlDownload qualityControlDownload; @@ -95,7 +95,7 @@ public class ProjectInformationMain extends Main implements BeforeEnterObserver private final TerminologyService terminologyService; private Context context; - public ProjectInformationMain(@Autowired ProjectDetailsComponent projectDetailsComponent, + public ProjectInformationMain(@Autowired ProjectSummaryComponent projectSummaryComponent, @Autowired ExperimentListComponent experimentListComponent, @Autowired UserPermissions userPermissions, @Autowired AddExperimentToProjectService addExperimentToProjectService, @@ -106,7 +106,7 @@ public ProjectInformationMain(@Autowired ProjectDetailsComponent projectDetailsC @Autowired TerminologyService terminologyService, CancelConfirmationDialogFactory cancelConfirmationDialogFactory, MessageSourceNotificationFactory messageSourceNotificationFactory) { - this.projectDetailsComponent = requireNonNull(projectDetailsComponent, + this.projectSummaryComponent = requireNonNull(projectSummaryComponent, "projectDetailsComponent must not be null"); this.experimentListComponent = requireNonNull(experimentListComponent, "experimentListComponent must not be null"); @@ -140,7 +140,7 @@ public ProjectInformationMain(@Autowired ProjectDetailsComponent projectDetailsC this.experimentListComponent.addExperimentSelectionListener(this::onExperimentSelectionEvent); this.experimentListComponent.addAddButtonListener(this::onAddExperimentClicked); addClassName("project"); - add(projectDetailsComponent, offerListComponent, offerDownload, experimentListComponent, + add(projectSummaryComponent, offerListComponent, offerDownload, experimentListComponent, qualityControlListComponent, qualityControlDownload); this.terminologyService = terminologyService; } @@ -300,7 +300,7 @@ private void onExperimentSelectionEvent(ExperimentSelectionEvent event) { private void setContext(Context context) { this.context = context; - projectDetailsComponent.setContext(context); + projectSummaryComponent.setContext(context); experimentListComponent.setContext(context); refreshOffers(projectPurchaseService, context.projectId().orElseThrow().value(), offerListComponent); diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectDetailsComponent.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectSummaryComponent.java similarity index 98% rename from user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectDetailsComponent.java rename to user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectSummaryComponent.java index 37a5101aa..2fb1754c5 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectDetailsComponent.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/info/ProjectSummaryComponent.java @@ -50,7 +50,7 @@ */ @UIScope @SpringComponent -public class ProjectDetailsComponent extends PageArea { +public class ProjectSummaryComponent extends PageArea { @Serial private static final long serialVersionUID = -5781313306040217724L; @@ -79,7 +79,7 @@ public class ProjectDetailsComponent extends PageArea { private DownloadProvider downloadProvider; private Context context; - public ProjectDetailsComponent(@Autowired ProjectInformationService projectInformationService, + public ProjectSummaryComponent(@Autowired ProjectInformationService projectInformationService, @Autowired ExperimentInformationService experimentInformationService, @Autowired ContactRepository contactRepository, @Autowired UserPermissions userPermissions, @@ -246,7 +246,7 @@ private Button exortButton() { private void triggerRoCrateDownload() { ProjectId projectId = context.projectId().orElseThrow(); Optional project = projectInformationService.find(projectId); - var roCrate = roCrateBuilder.projectInformation(project.orElseThrow()); + var roCrate = roCrateBuilder.projectSummary(project.orElseThrow()); var roCrateZipWriter = new RoCrateWriter(new ZipWriter()); try { var zippedRoCrateDir = tempDirectory.createDirectory();