diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/controller/OrganizationController.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/controller/OrganizationController.java index 6eaf4549..a8f02363 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/controller/OrganizationController.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/controller/OrganizationController.java @@ -1,5 +1,6 @@ package eu.dnetlib.organizations.controller; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -25,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import eu.dnetlib.common.controller.AbstractDnetController; +import eu.dnetlib.organizations.model.JournalEntry; +import eu.dnetlib.organizations.model.Note; import eu.dnetlib.organizations.model.OpenaireDuplicate; import eu.dnetlib.organizations.model.utils.BrowseEntry; import eu.dnetlib.organizations.model.utils.OrganizationConflict; @@ -34,6 +37,8 @@ import eu.dnetlib.organizations.model.view.OpenaireDuplicateView; import eu.dnetlib.organizations.model.view.OrganizationInfoView; import eu.dnetlib.organizations.model.view.OrganizationSimpleView; import eu.dnetlib.organizations.model.view.OrganizationView; +import eu.dnetlib.organizations.repository.JournalEntryRepository; +import eu.dnetlib.organizations.repository.NoteRepository; import eu.dnetlib.organizations.repository.UserCountryRepository; import eu.dnetlib.organizations.repository.readonly.ConflictGroupViewRepository; import eu.dnetlib.organizations.repository.readonly.DuplicateGroupViewRepository; @@ -66,6 +71,10 @@ public class OrganizationController extends AbstractDnetController { @Autowired private DuplicateGroupViewRepository duplicateGroupViewRepository; @Autowired + private NoteRepository noteRepository; + @Autowired + private JournalEntryRepository journalEntryRepository; + @Autowired private DatabaseUtils databaseUtils; @PostMapping("/save") @@ -324,4 +333,44 @@ public class OrganizationController extends AbstractDnetController { } } + @GetMapping("/note") + public Note noteById(@RequestParam final String id, final Authentication authentication) { + final OrganizationView org = organizationViewRepository.findById(id).get(); + + if (UserInfo.isSuperAdmin(authentication) + || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), UserInfo.getEmail(authentication))) { + return noteRepository.findById(id).orElse(new Note(id, "", null, null)); + } else { + throw new RuntimeException("User not authorized"); + } + } + + @PostMapping("/note") + public Note saveNote(@RequestBody final Note note, final Authentication authentication) { + final OrganizationView org = organizationViewRepository.findById(note.getOrgId()).get(); + + if (UserInfo.isSuperAdmin(authentication) + || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), UserInfo.getEmail(authentication))) { + + note.setModifiedBy(UserInfo.getEmail(authentication)); + note.setModificationDate(OffsetDateTime.now()); + + return noteRepository.save(note); + } else { + throw new RuntimeException("User not authorized"); + } + } + + @GetMapping("/journal") + public List journalEntriesById(@RequestParam final String id, final Authentication authentication) { + final OrganizationView org = organizationViewRepository.findById(id).get(); + + if (UserInfo.isSuperAdmin(authentication) + || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), UserInfo.getEmail(authentication))) { + return journalEntryRepository.findByOrgIdOrderByDateDesc(id); + } else { + throw new RuntimeException("User not authorized"); + } + } + } diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/JournalEntry.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/JournalEntry.java new file mode 100644 index 00000000..79a6184d --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/JournalEntry.java @@ -0,0 +1,106 @@ +package eu.dnetlib.organizations.model; + +import java.io.Serializable; +import java.time.OffsetDateTime; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import eu.dnetlib.organizations.utils.JournalOperations; + +@Entity +@Table(name = "journal") +public class JournalEntry implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -6861395678624671324L; + + @Id + @Column(name = "jid") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer journalEntryId; + + @Column(name = "id") + private String orgId; + + @Column(name = "operation") + private String operation; + + @Column(name = "description") + private String description; + + @Column(name = "op_date") + private OffsetDateTime date; + + @Column(name = "email") + private String email; + + public JournalEntry() {} + + public JournalEntry(final String orgId, final JournalOperations operation, final String description, final String email) { + this(orgId, operation.name(), description, email); + } + + protected JournalEntry(final String orgId, final String operation, final String description, final String email) { + this.orgId = orgId; + this.operation = operation; + this.description = description; + this.date = OffsetDateTime.now(); + this.email = email; + } + + public Integer getJournalEntryId() { + return journalEntryId; + } + + public void setJournalEntryId(final Integer journalEntryId) { + this.journalEntryId = journalEntryId; + } + + public String getOrgId() { + return orgId; + } + + public void setOrgId(final String orgId) { + this.orgId = orgId; + } + + public String getOperation() { + return operation; + } + + public void setOperation(final String operation) { + this.operation = operation; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public OffsetDateTime getDate() { + return date; + } + + public void setDate(final OffsetDateTime date) { + this.date = date; + } + + public String getEmail() { + return email; + } + + public void setEmail(final String email) { + this.email = email; + } + +} diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/Note.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/Note.java new file mode 100644 index 00000000..e43c528f --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/Note.java @@ -0,0 +1,74 @@ +package eu.dnetlib.organizations.model; + +import java.io.Serializable; +import java.time.OffsetDateTime; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "notes") +public class Note implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 8193034729860131949L; + + @Id + @Column(name = "id") + private String orgId; + + @Column(name = "note") + private String note; + + @Column(name = "modified_by") + private String modifiedBy; + + @Column(name = "modification_date") + private OffsetDateTime modificationDate; + + public Note() {} + + public Note(final String orgId, final String note, final String modifiedBy, final OffsetDateTime modificationDate) { + this.orgId = orgId; + this.note = note; + this.modifiedBy = modifiedBy; + this.modificationDate = modificationDate; + } + + public String getOrgId() { + return orgId; + } + + public void setOrgId(final String orgId) { + this.orgId = orgId; + } + + public String getNote() { + return note; + } + + public void setNote(final String note) { + this.note = note; + } + + public String getModifiedBy() { + return modifiedBy; + } + + public void setModifiedBy(final String modifiedBy) { + this.modifiedBy = modifiedBy; + } + + public OffsetDateTime getModificationDate() { + return modificationDate; + } + + public void setModificationDate(final OffsetDateTime modificationDate) { + this.modificationDate = modificationDate; + } + +} diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/repository/JournalEntryRepository.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/repository/JournalEntryRepository.java new file mode 100644 index 00000000..056f7173 --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/repository/JournalEntryRepository.java @@ -0,0 +1,13 @@ +package eu.dnetlib.organizations.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import eu.dnetlib.organizations.model.JournalEntry; + +public interface JournalEntryRepository extends JpaRepository { + + public List findByOrgIdOrderByDateDesc(String orgId); + +} diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/repository/NoteRepository.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/repository/NoteRepository.java new file mode 100644 index 00000000..e2114f2a --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/repository/NoteRepository.java @@ -0,0 +1,9 @@ +package eu.dnetlib.organizations.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import eu.dnetlib.organizations.model.Note; + +public interface NoteRepository extends JpaRepository { + +} diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/DatabaseUtils.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/DatabaseUtils.java index 334b54d4..517bfaf0 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/DatabaseUtils.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/DatabaseUtils.java @@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestBody; import eu.dnetlib.organizations.controller.UserRole; import eu.dnetlib.organizations.model.Acronym; +import eu.dnetlib.organizations.model.JournalEntry; import eu.dnetlib.organizations.model.OpenaireConflictPK; import eu.dnetlib.organizations.model.OpenaireDuplicate; import eu.dnetlib.organizations.model.Organization; @@ -44,6 +45,7 @@ import eu.dnetlib.organizations.model.utils.VocabularyTerm; import eu.dnetlib.organizations.model.view.OrganizationView; import eu.dnetlib.organizations.model.view.UserView; import eu.dnetlib.organizations.repository.AcronymRepository; +import eu.dnetlib.organizations.repository.JournalEntryRepository; import eu.dnetlib.organizations.repository.OpenaireConflictRepository; import eu.dnetlib.organizations.repository.OpenaireDuplicateRepository; import eu.dnetlib.organizations.repository.OrganizationRepository; @@ -80,6 +82,8 @@ public class DatabaseUtils { private OpenaireDuplicateRepository openaireDuplicateRepository; @Autowired private OrganizationViewRepository organizationViewRepository; + @Autowired + private JournalEntryRepository journalEntryRepository; @Autowired private JdbcTemplate jdbcTemplate; @@ -164,6 +168,35 @@ public class DatabaseUtils { organizationRepository.updateModificationDate(newId, user, now); + JournalOperations op = JournalOperations.UNKNOWN; + String message = "-"; + + if (newStatus.equals(OrganizationStatus.suggested.toString())) { + if (oldStatus == null) { + op = JournalOperations.NEW_SUGG_ORG; + message = "Created a new suggested org"; + } else if (oldStatus != null) { + op = JournalOperations.EDIT_SUGG_ORG; + message = "Metadata updated"; + } + } else if (newStatus.equals(OrganizationStatus.approved.toString())) { + if (oldStatus == null) { + op = JournalOperations.NEW_ORG; + message = "Created a new organization"; + } else if (oldStatus.equals(OrganizationStatus.suggested.toString())) { + op = JournalOperations.APPROVE_SUGG_ORG; + message = "Approved the suggested org: " + oldId; + + } else { + op = JournalOperations.EDIT_ORG; + message = "Metadata updated"; + } + } else { + // IMPOSSIBLE ??? + } + + journalEntryRepository.save(new JournalEntry(newId, op, message, user)); + return newId; } @@ -184,6 +217,18 @@ public class DatabaseUtils { } }); + final String message = String.format("Duplicates updated (%s similars, %s differents, %s suggested)", list.stream() + .filter(d -> d.getRelType().equals(SimilarityType.is_similar.toString())) + .count(), list.stream() + .filter(d -> d.getRelType().equals(SimilarityType.is_different.toString())) + .count(), list.stream().filter(d -> d.getRelType().equals(SimilarityType.suggested.toString())).count()); + + list.stream() + .map(OpenaireDuplicate::getLocalId) + .distinct() + .map(id -> new JournalEntry(id, JournalOperations.DUPLICATES, message, user)) + .forEach(journalEntryRepository::save); + } private void makeRelations(final String orgId, final OrganizationView orgView, final boolean update) { @@ -375,7 +420,10 @@ public class DatabaseUtils { final String masterId = insertOrUpdateOrganization(newOrg, user, false); // I hide the merged organizations - similarIds.forEach(id -> hideConflictOrgs(masterId, id)); + similarIds.forEach(id -> { + hideConflictOrgs(masterId, id); + journalEntryRepository.save(new JournalEntry(masterId, JournalOperations.FIX_CONFLICT, "The org has been hidded and merged in " + masterId, user)); + }); // I reassign the duplicates to the new org final List newDuplicates = similarIds.stream() @@ -400,6 +448,9 @@ public class DatabaseUtils { } } + journalEntryRepository + .save(new JournalEntry(masterId, JournalOperations.FIX_CONFLICT, "New org created merging: " + StringUtils.join(similarIds, ", "), user)); + return masterId; } @@ -408,11 +459,14 @@ public class DatabaseUtils { final OffsetDateTime now = OffsetDateTime.now(); + final String message = "Mark the following orgs as different: " + StringUtils.join(differentsIds, ", "); + for (int i = 0; i < differentsIds.size(); i++) { for (int j = i + 1; j < differentsIds.size(); j++) { openaireConflictRepository .updateStatusAndResetGroup(differentsIds.get(i), differentsIds.get(j), SimilarityType.is_different.toString(), user, now); } + journalEntryRepository.save(new JournalEntry(differentsIds.get(i), JournalOperations.NO_CONFLICT, message, user)); } } diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/JournalOperations.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/JournalOperations.java new file mode 100644 index 00000000..d2db9715 --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/JournalOperations.java @@ -0,0 +1,13 @@ +package eu.dnetlib.organizations.utils; + +public enum JournalOperations { + NEW_ORG, + NEW_SUGG_ORG, + APPROVE_SUGG_ORG, + EDIT_ORG, + EDIT_SUGG_ORG, + DUPLICATES, + FIX_CONFLICT, + NO_CONFLICT, + UNKNOWN +} diff --git a/apps/dnet-orgs-database-application/src/main/resources/sql/schema.sql b/apps/dnet-orgs-database-application/src/main/resources/sql/schema.sql index 51db3db3..c7ba1414 100644 --- a/apps/dnet-orgs-database-application/src/main/resources/sql/schema.sql +++ b/apps/dnet-orgs-database-application/src/main/resources/sql/schema.sql @@ -391,6 +391,23 @@ CREATE TABLE urls ( ); CREATE INDEX urls_id_idx ON urls(id); +CREATE TABLE notes ( + id text PRIMARY KEY REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, + note text, + modified_by text, + modification_date timestamp +); + +CREATE TABLE journal ( + jid SERIAL PRIMARY KEY, + id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, + operation text, + description text, + op_date timestamp DEFAULT NOW(), + email text +); +CREATE INDEX journal_id_idx ON journal(id); + CREATE TABLE oa_duplicates ( local_id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, oa_original_id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_conflicts.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_conflicts.html index 969a2efd..f825aa73 100644 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_conflicts.html +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_conflicts.html @@ -23,7 +23,7 @@