note and journal (model and api)

This commit is contained in:
Michele Artini 2021-04-09 12:17:11 +02:00
parent 562e56be07
commit 13fd72b25e
9 changed files with 337 additions and 2 deletions

View File

@ -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<JournalEntry> 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");
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<JournalEntry, Long> {
public List<JournalEntry> findByOrgIdOrderByDateDesc(String orgId);
}

View File

@ -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<Note, String> {
}

View File

@ -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<OpenaireDuplicate> 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));
}
}

View File

@ -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
}

View File

@ -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,

View File

@ -23,7 +23,7 @@
<div class="card-footer bg-secondary py-1">
<button class="btn btn-sm btn-outline-primary" data-toggle="modal" data-target="#addConflictModal">add</button>
<div class="btn-group" ng-if="showButtons && conflicts.length > 0">
<button class="btn btn-sm btn-primary" ng-click="prepareConflictsModal()" data-toggle="modal" data-target="#resolveConflictsModal">resolve conflicts</button>
<button class="btn btn-sm btn-primary" ng-click="prepareConflictsModal()" data-toggle="modal" data-target="#resolveConflictsModal">resolve manually</button>
<button class="btn btn-sm btn-warning" ng-click="resolveConflicts(true)">merge all</button>
<button class="btn btn-sm btn-danger" ng-click="resolveConflicts(false)">all different</button>
</div>