diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/MainApplication.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/MainApplication.java index f845ceb1..7d251f88 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/MainApplication.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/MainApplication.java @@ -32,7 +32,7 @@ public class MainApplication { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) - .paths(p -> p.startsWith("/api/")) + .paths(p -> p.startsWith("/api/") || p.startsWith("/oa_api")) .build() .apiInfo(new ApiInfoBuilder() .title("D-Net Organizations Service APIs") diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/MyAccessDeniedHandler.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/MyAccessDeniedHandler.java index 396659c7..23f02159 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/MyAccessDeniedHandler.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/MyAccessDeniedHandler.java @@ -23,12 +23,12 @@ public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(final HttpServletRequest req, final HttpServletResponse res, final AccessDeniedException e) - throws IOException, ServletException { + throws IOException, ServletException { final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { - logger.warn(String.format("User '%s' attempted to access the protected URL: %s", auth.getName(), req.getRequestURI())); + logger.warn(String.format("User '%s' (%s) attempted to access the protected URL: %s", auth.getName(), req.getRemoteAddr(), req.getRequestURI())); } if (UserInfo.isNotAuthorized(auth)) { diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/controller/OpenaireInternalApiController.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/controller/OpenaireInternalApiController.java index 65228be5..e16667fa 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/controller/OpenaireInternalApiController.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/controller/OpenaireInternalApiController.java @@ -33,7 +33,8 @@ public class OpenaireInternalApiController { log.warn("Call received by blaklisted ip (https proxy): " + req.getRemoteAddr()); throw new RuntimeException("Call received by blaklisted ip (https proxy): " + req.getRemoteAddr()); } - new Thread(databaseUtils::importDedupEvents).run(); + new Thread(databaseUtils::importDedupEvents).start(); + return Arrays.asList("Importing simrels (request from " + req.getRemoteAddr() + ") ..."); } } 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 1a8aaefe..6345958c 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 @@ -9,7 +9,6 @@ import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; @@ -26,7 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import eu.dnetlib.organizations.model.OpenaireDuplicate; -import eu.dnetlib.organizations.model.Relationship; +import eu.dnetlib.organizations.model.Organization; import eu.dnetlib.organizations.model.utils.BrowseEntry; import eu.dnetlib.organizations.model.utils.OrganizationConflict; import eu.dnetlib.organizations.model.view.ConflictGroupView; @@ -35,6 +34,7 @@ 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.OrganizationRepository; import eu.dnetlib.organizations.repository.UserCountryRepository; import eu.dnetlib.organizations.repository.readonly.ConflictGroupViewRepository; import eu.dnetlib.organizations.repository.readonly.DuplicateGroupViewRepository; @@ -49,6 +49,8 @@ import eu.dnetlib.organizations.utils.DatabaseUtils; @RequestMapping("/api/organizations") public class OrganizationController { + @Autowired + private OrganizationRepository organizationRepository; @Autowired private OrganizationViewRepository organizationViewRepository; @Autowired @@ -78,7 +80,7 @@ public class OrganizationController { } else if (StringUtils.isBlank(org.getType())) { throw new RuntimeException("Missing field: type"); } else if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) { - final String orgId = databaseUtils.insertOrUpdateOrganization(org, authentication.getName()); + final String orgId = databaseUtils.insertOrUpdateOrganization(org, authentication.getName(), UserInfo.isSimpleUser(authentication)); return Arrays.asList(orgId); } else { throw new RuntimeException("User not authorized"); @@ -118,6 +120,30 @@ public class OrganizationController { } } + @GetMapping("/delete") + public OrganizationView deleteById(@RequestParam final String id, final Authentication authentication) { + final Organization org = organizationRepository.findById(id).get(); + + if (UserInfo.isSuperAdmin(authentication) || UserInfo.isNationalAdmin(authentication) && + userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) { + return databaseUtils.markAsDeleted(id, authentication.getName()); + } else { + throw new RuntimeException("User not authorized"); + } + } + + @GetMapping("/discard") + public OrganizationView discardById(@RequestParam final String id, final Authentication authentication) { + final Organization org = organizationRepository.findById(id).get(); + + if (UserInfo.isSuperAdmin(authentication) || UserInfo.isNationalAdmin(authentication) && + userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) { + return databaseUtils.markAsDiscarded(id, authentication.getName()); + } else { + throw new RuntimeException("User not authorized"); + } + } + @GetMapping("/conflicts") public List conflicts(@RequestParam final String id, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(id, authentication.getName())) { @@ -139,7 +165,7 @@ public class OrganizationController { @GetMapping("/conflicts/byCountry/{country}") public Collection> findConflictsByCountry(@PathVariable final String country, final Authentication authentication) { - databaseUtils.verifyConflictGroups(false); + // databaseUtils.verifyConflictGroups(false); if (UserInfo.isSuperAdmin(authentication)) { return groupConflicts(conflictGroupViewRepository.findByCountry1OrCountry2(country, country).stream()); @@ -210,26 +236,33 @@ public class OrganizationController { public Page search(@PathVariable final int page, @PathVariable final int size, @RequestParam final String q, + @RequestParam(required = false, defaultValue = "suggested,approved") final String status, final Authentication authentication) { + return UserInfo.isSuperAdmin(authentication) - ? organizationSimpleViewRepository.search(q, PageRequest.of(page, size)) - : organizationSimpleViewRepository.searchForUser(q, authentication.getName(), PageRequest.of(page, size)); + ? organizationSimpleViewRepository.search(q, Arrays.asList(status.split(",")), PageRequest.of(page, size)) + : organizationSimpleViewRepository.searchForUser(q, authentication.getName(), Arrays.asList(status.split(",")), PageRequest.of(page, size)); } - @GetMapping("/byCountry/{code}/{page}/{size}") - public Page findByCountry(@PathVariable final String code, + @GetMapping("/byCountry/{status}/{code}/{page}/{size}") + public Page findByCountry(@PathVariable final String status, + @PathVariable final String code, @PathVariable final int page, @PathVariable final int size, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, authentication.getName())) { - return organizationSimpleViewRepository.findByCountryOrderByName(code, PageRequest.of(page, size)); + if (status.equalsIgnoreCase("all")) { + return organizationSimpleViewRepository.findByCountryOrderByName(code, PageRequest.of(page, size)); + } else { + return organizationSimpleViewRepository.findByCountryAndStatusOrderByName(code, status, PageRequest.of(page, size)); + } } else { throw new RuntimeException("User not authorized"); } } @GetMapping("/byCountry/{status}/{code}") - public Iterable findPendingOrgsByCountry(@PathVariable final String status, + public Iterable findOrgsByStatusAndCountry(@PathVariable final String status, @PathVariable final String code, final Authentication authentication) { if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, authentication.getName())) { @@ -243,14 +276,27 @@ public class OrganizationController { } } - @GetMapping("/byType/{type}/{page}/{size}") - public Page findByType(@PathVariable final String type, + @GetMapping("/byType/{status}/{type}/{page}/{size}") + public Page findByType(@PathVariable final String status, + @PathVariable final String type, @PathVariable final int page, @PathVariable final int size, final Authentication authentication) { - return UserInfo.isSuperAdmin(authentication) - ? organizationSimpleViewRepository.findByTypeOrderByName(type, PageRequest.of(page, size)) - : organizationSimpleViewRepository.findByTypeForUser(type, authentication.getName(), PageRequest.of(page, size)); + + if (UserInfo.isSuperAdmin(authentication)) { + if (status.equalsIgnoreCase("all")) { + return organizationSimpleViewRepository.findByTypeOrderByName(type, PageRequest.of(page, size)); + } else { + return organizationSimpleViewRepository.findByTypeAndStatusOrderByName(type, status, PageRequest.of(page, size)); + } + } else { + if (status.equalsIgnoreCase("all")) { + return organizationSimpleViewRepository.findByTypeForUser(type, authentication.getName(), PageRequest.of(page, size)); + } else { + return organizationSimpleViewRepository.findByTypeAndStatusForUser(type, status, authentication.getName(), PageRequest.of(page, size)); + } + } + } @GetMapping("/browse/countries") @@ -268,18 +314,17 @@ public class OrganizationController { } @PostMapping("/conflicts/fix/{masterId}") - public List fixConflicts(final Authentication authentication, @PathVariable final String masterId, @RequestBody final List otherIds) { + public List fixConflicts(final Authentication authentication, @PathVariable final String masterId, @RequestBody final List otherIds) { if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(masterId, authentication.getName())) { + final List list = new ArrayList<>(); + list.add(masterId); + list.addAll(otherIds); + final String newOrgId = databaseUtils.fixConflict(list, authentication.getName()); - return otherIds.stream() - .filter(id -> UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForId(id, authentication.getName())) - .map(id -> databaseUtils.fixDuplicate(masterId, id)) - .flatMap(List::stream) - .collect(Collectors.toList()); + return Arrays.asList(newOrgId); } else { return new ArrayList<>(); } } - } diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/OpenaireDuplicate.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/OpenaireDuplicate.java index de546cc0..10b7129f 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/OpenaireDuplicate.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/OpenaireDuplicate.java @@ -29,6 +29,14 @@ public class OpenaireDuplicate implements Serializable { @Column(name = "reltype") private String relType; + public OpenaireDuplicate() {} + + public OpenaireDuplicate(final String localId, final String oaOriginalId, final String relType) { + this.localId = localId; + this.oaOriginalId = oaOriginalId; + this.relType = relType; + } + public String getLocalId() { return localId; } diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/utils/BrowseEntry.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/utils/BrowseEntry.java index 8647374c..86a71797 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/utils/BrowseEntry.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/utils/BrowseEntry.java @@ -1,6 +1,8 @@ package eu.dnetlib.organizations.model.utils; import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; public class BrowseEntry implements Serializable { @@ -9,17 +11,16 @@ public class BrowseEntry implements Serializable { */ private static final long serialVersionUID = 8854955977257064470L; - private String value; + private String code; private String name; - private int approved; - private int pending; + private Map values = new LinkedHashMap<>(); - public String getValue() { - return value; + public String getCode() { + return code; } - public void setValue(final String value) { - this.value = value; + public void setCode(final String code) { + this.code = code; } public String getName() { @@ -30,20 +31,12 @@ public class BrowseEntry implements Serializable { this.name = name; } - public int getApproved() { - return approved; + public Map getValues() { + return values; } - public void setApproved(final int approved) { - this.approved = approved; - } - - public int getPending() { - return pending; - } - - public void setPending(final int pending) { - this.pending = pending; + public void setValues(final Map values) { + this.values = values; } } diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/utils/TempBrowseEntry.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/utils/TempBrowseEntry.java new file mode 100644 index 00000000..e9a42a2c --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/utils/TempBrowseEntry.java @@ -0,0 +1,48 @@ +package eu.dnetlib.organizations.model.utils; + +import java.io.Serializable; + +public class TempBrowseEntry implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -2233409680550512318L; + + private String code; + private String name; + private String group; + private Integer count; + + public String getCode() { + return code; + } + + public void setCode(final String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getGroup() { + return group; + } + + public void setGroup(final String group) { + this.group = group; + } + + public Integer getCount() { + return count; + } + + public void setCount(final Integer count) { + this.count = count; + } +} diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/view/OrganizationSimpleView.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/view/OrganizationSimpleView.java index fc9d00db..e30a32df 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/view/OrganizationSimpleView.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/model/view/OrganizationSimpleView.java @@ -46,6 +46,10 @@ public class OrganizationSimpleView implements Serializable, Comparable { // SEARCH - @Query(value = "select o.* from organizations_simple_view o left outer join org_index_search idx on (idx.id = o.id) where idx.txt @@ plainto_tsquery(:text) order by o.name", nativeQuery = true) - Page search(@Param("text") String text, Pageable pageable); + @Query(value = "select o.* from organizations_simple_view o left outer join org_index_search idx on (idx.id = o.id) where idx.txt @@ plainto_tsquery(:text) and o.status in :statuses order by o.name", nativeQuery = true) + Page search(@Param("text") String text, @Param("statuses") List statuses, Pageable pageable); // SEARCH FOR USER - @Query(value = "select o.* from organizations_simple_view o left outer join org_index_search idx on (idx.id = o.id) left outer join user_countries uc on (uc.country = o.country) where idx.txt @@ plainto_tsquery(:text) and uc.email = :email order by o.name", nativeQuery = true) - Page searchForUser(@Param("text") String text, @Param("email") String email, Pageable pageable); + @Query(value = "select o.* from organizations_simple_view o left outer join org_index_search idx on (idx.id = o.id) left outer join user_countries uc on (uc.country = o.country) where idx.txt @@ plainto_tsquery(:text) and uc.email = :email and o.status in :statuses order by o.name", nativeQuery = true) + Page searchForUser(@Param("text") String text, + @Param("email") String email, + @Param("statuses") List statuses, + Pageable pageable); Page findByCountryOrderByName(String country, Pageable pageable); @@ -25,9 +30,16 @@ public interface OrganizationSimpleViewRepository extends ReadOnlyRepository findByCountryAndStatusOrderByName(String code, String status); + Page findByCountryAndStatusOrderByName(String code, String status, Pageable pageable); + Page findByTypeOrderByName(String type, Pageable pageable); + Page findByTypeAndStatusOrderByName(String type, String status, Pageable pageable); + @Query(value = "select o.* from organizations_simple_view o left outer join user_countries uc on (uc.country = o.country) where uc.email = ?2 and o.type = ?1 order by o.name", nativeQuery = true) Page findByTypeForUser(String type, String name, Pageable pageable); + @Query(value = "select o.* from organizations_simple_view o left outer join user_countries uc on (uc.country = o.country) where o.type = ?1 and o.status = ?2 and uc.email = ?3 order by o.name", nativeQuery = true) + Page findByTypeAndStatusForUser(String type, String status, String name, Pageable pageable); + } 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 61af9b8e..49b22108 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 @@ -1,19 +1,21 @@ package eu.dnetlib.organizations.utils; import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; import java.util.Set; -import java.util.TreeSet; -import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import javax.transaction.Transactional; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -38,6 +40,7 @@ import eu.dnetlib.organizations.model.User; import eu.dnetlib.organizations.model.UserCountry; import eu.dnetlib.organizations.model.utils.BrowseEntry; import eu.dnetlib.organizations.model.utils.OrganizationConflict; +import eu.dnetlib.organizations.model.utils.TempBrowseEntry; import eu.dnetlib.organizations.model.utils.VocabularyTerm; import eu.dnetlib.organizations.model.view.OrganizationView; import eu.dnetlib.organizations.model.view.UserView; @@ -51,6 +54,7 @@ import eu.dnetlib.organizations.repository.RelationshipRepository; import eu.dnetlib.organizations.repository.UrlRepository; import eu.dnetlib.organizations.repository.UserCountryRepository; import eu.dnetlib.organizations.repository.UserRepository; +import eu.dnetlib.organizations.repository.readonly.OrganizationViewRepository; @Component public class DatabaseUtils { @@ -75,6 +79,9 @@ public class DatabaseUtils { private OpenaireConflictRepository openaireConflictRepository; @Autowired private OpenaireDuplicateRepository openaireDuplicateRepository; + @Autowired + private OrganizationViewRepository organizationViewRepository; + @Autowired private JdbcTemplate jdbcTemplate; @@ -90,19 +97,29 @@ public class DatabaseUtils { } @Transactional - public String insertOrUpdateOrganization(final OrganizationView orgView, final String user) { - final boolean alreadyApproved = StringUtils.equals(orgView.getStatus(), OrganizationStatus.approved.toString()); + public String insertOrUpdateOrganization(final OrganizationView orgView, final String user, final boolean isSimpleUser) { - final String oldId = orgView.getId(); + final String oldStatus = orgView.getId() != null ? organizationRepository.findById(orgView.getId()) + .map(Organization::getStatus) + .orElse(null) : null; - if (StringUtils.isBlank(orgView.getId())) { - orgView.setId(null); - } else if (!alreadyApproved) { - cleanOldRelations(oldId); - organizationRepository.deleteById(oldId); - orgView.setId(null); + final boolean alreadyApproved = StringUtils.equals(oldStatus, OrganizationStatus.approved.toString()); + + final String newStatus; + if (!isSimpleUser) { // IS ADMIN + newStatus = OrganizationStatus.approved.toString(); + } else if (isSimpleUser && oldStatus == null) { + newStatus = OrganizationStatus.suggested.toString(); + } else if (isSimpleUser && alreadyApproved) { + newStatus = OrganizationStatus.approved.toString(); } else { - cleanOldRelations(orgView.getId()); + throw new RuntimeException("User not authorized"); + } + + final String oldId = StringUtils.isNotBlank(orgView.getId()) ? new String(orgView.getId()) : null; + + if (oldId == null || !oldId.startsWith(OpenOrgsConstants.OPENORGS_PREFIX)) { + orgView.setId(null); } final Organization org = new Organization(orgView.getId(), @@ -110,39 +127,67 @@ public class DatabaseUtils { orgView.getType(), orgView.getLat(), orgView.getLng(), orgView.getCity(), orgView.getCountry(), - OrganizationStatus.approved.toString()); + newStatus); final String newId = organizationRepository.save(org).getId(); - makeNewRelations(newId, orgView); + final OffsetDateTime now = OffsetDateTime.now(); - updateHistoryFields(newId, user, alreadyApproved); + organizationRepository.updateModificationDate(newId, user, now); + + if (StringUtils.equals(newId, oldId)) { + makeRelations(newId, orgView, true); + } else { + organizationRepository.updateCreationDate(newId, user, now); + makeRelations(newId, orgView, false); + if (oldId != null) { + + final List dups = new ArrayList<>(); + + dups.add(new OpenaireDuplicate(newId, oldId, SimilarityType.is_similar.toString())); + dups.addAll(openaireDuplicateRepository.findByLocalId(oldId) + .stream() + .map(d -> new OpenaireDuplicate(newId, d.getOaOriginalId(), SimilarityType.suggested.toString())) + .collect(Collectors.toList())); + + openaireDuplicateRepository.saveAll(dups); + dups.forEach(d -> openaireDuplicateRepository.updateModificationDate(d.getLocalId(), d.getOaOriginalId(), user, now)); + + organizationRepository.updateStatus(oldId, OrganizationStatus.duplicate.toString()); + organizationRepository.updateModificationDate(oldId, user, now); + } + } return newId; } - private void updateHistoryFields(final String id, final String user, final boolean update) { - - final OffsetDateTime now = OffsetDateTime.now(); - if (update) { - organizationRepository.updateModificationDate(id, user, now); - } else { - organizationRepository.updateCreationDate(id, user, now); - organizationRepository.updateModificationDate(id, user, now); - } - } - @Transactional - public void saveDuplicates(final List simrels, final String email) { + public void saveDuplicates(final List simrels, final String user) { final OffsetDateTime now = OffsetDateTime.now(); final List list = openaireDuplicateRepository.saveAll(simrels); - list.forEach(d -> openaireDuplicateRepository.updateModificationDate(d.getLocalId(), d.getOaOriginalId(), email, now)); + list.forEach(d -> { + openaireDuplicateRepository.updateModificationDate(d.getLocalId(), d.getOaOriginalId(), user, now); + + if (d.getRelType().equals(SimilarityType.is_different.toString())) { + updateStatus(d.getOaOriginalId(), OrganizationStatus.suggested, user, now); + } else { + updateStatus(d.getOaOriginalId(), OrganizationStatus.duplicate, user, now); + } + }); } - private void makeNewRelations(final String orgId, final OrganizationView orgView) { + private void makeRelations(final String orgId, final OrganizationView orgView, final boolean update) { + if (update) { + acronymRepository.deleteByOrgId(orgId); + otherNameRepository.deleteByOrgId(orgId); + otherIdentifierRepository.deleteByOrgId(orgId); + urlRepository.deleteByOrgId(orgId); + relationshipRepository.deleteById1(orgId); + relationshipRepository.deleteById2(orgId); + } orgView.getAcronyms().forEach(s -> acronymRepository.save(new Acronym(orgId, s))); orgView.getOtherNames().forEach(n -> otherNameRepository.save(new OtherName(orgId, n.getName(), n.getLang()))); orgView.getOtherIdentifiers().forEach(id -> otherIdentifierRepository.save(new OtherIdentifier(orgId, id.getId(), id.getType()))); @@ -150,15 +195,6 @@ public class DatabaseUtils { orgView.getRelations().forEach(r -> makeRelation(orgId, r.getRelatedOrgId(), RelationType.valueOf(r.getType()))); } - private void cleanOldRelations(final String id) { - acronymRepository.deleteByOrgId(id); - otherNameRepository.deleteByOrgId(id); - otherIdentifierRepository.deleteByOrgId(id); - urlRepository.deleteByOrgId(id); - relationshipRepository.deleteById1(id); - relationshipRepository.deleteById2(id); - } - @Cacheable("vocs") public List listValuesOfVocabularyTable(final VocabularyTable table) { final String sql = "select val as value, name as name from " + table; @@ -203,54 +239,30 @@ public class DatabaseUtils { } } - @Transactional - public void verifyConflictGroups(final boolean forceUpdate) { - - if (forceUpdate || openaireConflictRepository.countByGroupNull() > 0) { - - log.info("Recreating conflicts group..."); - - openaireConflictRepository.resetGroupIds(); - - final Map> groups = new HashMap<>(); - for (final OpenaireConflict w : openaireConflictRepository.findAll()) { - final List list = findExistingGroupsForRel(w, groups); - if (list.isEmpty()) { - final String idGroup = generateGroupId(); - groups.put(idGroup, new HashSet<>()); - addToGroup(groups, idGroup, w); - } else if (list.size() == 1) { - addToGroup(groups, list.get(0), w); - } else { - final String idGroup = generateGroupId(); - groups.put(idGroup, new TreeSet<>()); - list.forEach(id -> groups.get(idGroup).addAll(groups.get(id))); - list.forEach(id -> groups.remove(id)); - addToGroup(groups, idGroup, w); - } - } - - for (final Entry> e : groups.entrySet()) { - final String gid = e.getKey(); - for (final String orgId : e.getValue()) { - for (final OpenaireConflict oc : openaireConflictRepository.findById1AndGroupIsNull(orgId)) { - oc.setGroup(gid); - openaireConflictRepository.save(oc); - } - for (final OpenaireConflict oc : openaireConflictRepository.findById2AndGroupIsNull(orgId)) { - oc.setGroup(gid); - openaireConflictRepository.save(oc); - } - } - } - - log.info("...conflicts group recreated"); - } - } - - private String generateGroupId() { - return "group::" + UUID.randomUUID(); - } + /* + * @Transactional public void verifyConflictGroups(final boolean forceUpdate) { + * + * if (forceUpdate || openaireConflictRepository.countByGroupNull() > 0) { + * + * log.info("Recreating conflicts group..."); + * + * openaireConflictRepository.resetGroupIds(); + * + * final Map> groups = new HashMap<>(); for (final OpenaireConflict w : openaireConflictRepository.findAll()) { + * final List list = findExistingGroupsForRel(w, groups); if (list.isEmpty()) { final String idGroup = generateGroupId(); + * groups.put(idGroup, new HashSet<>()); addToGroup(groups, idGroup, w); } else if (list.size() == 1) { addToGroup(groups, list.get(0), + * w); } else { final String idGroup = generateGroupId(); groups.put(idGroup, new TreeSet<>()); list.forEach(id -> + * groups.get(idGroup).addAll(groups.get(id))); list.forEach(id -> groups.remove(id)); addToGroup(groups, idGroup, w); } } + * + * for (final Entry> e : groups.entrySet()) { final String gid = e.getKey(); for (final String orgId : e.getValue()) + * { for (final OpenaireConflict oc : openaireConflictRepository.findById1AndGroupIsNull(orgId)) { oc.setGroup(gid); + * openaireConflictRepository.save(oc); } for (final OpenaireConflict oc : openaireConflictRepository.findById2AndGroupIsNull(orgId)) { + * oc.setGroup(gid); openaireConflictRepository.save(oc); } } } + * + * log.info("...conflict groups recreated"); } } + * + * private String generateGroupId() { return "group::" + UUID.randomUUID(); } + */ private List findExistingGroupsForRel(final OpenaireConflict w, final Map> groups) { return groups.entrySet() @@ -280,36 +292,51 @@ public class DatabaseUtils { // BROWSE BY COUNTRY public List browseCountries() { final String sql = - "select o.country as value, c.name as name, sum(case when status='approved' then 1 else 0 end) as approved, sum(case when status='pending' then 1 else 0 end) as pending from organizations o left outer join countries c on (o.country = c.val) group by o.country, c.name order by approved desc"; - return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(BrowseEntry.class)); + "select o.country as code, c.name as name, o.status as group, count(o.status) as count from organizations o left outer join countries c on (o.country = c.val) group by o.country, c.name, o.status"; + return listBrowseEntries(sql); } // BROWSE BY COUNTRY FOR USER public List browseCountriesForUser(final String email) { final String sql = - "select o.country as value, c.name as name, sum(case when status='approved' then 1 else 0 end) as approved, sum(case when status='pending' then 1 else 0 end) as pending from user_countries uc left outer join organizations o on (uc.country = o.country) left outer join countries c on (o.country = c.val) where uc.email=? group by o.country, c.name order by approved desc"; - return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(BrowseEntry.class), email); + "select o.country as code, c.name as name, o.status as group, count(o.status) as count from user_countries uc left outer join organizations o on (uc.country = o.country) left outer join countries c on (o.country = c.val) where uc.email=? group by o.country, c.name, o.status"; + return listBrowseEntries(sql, email); } // BROWSE BY ORG TYPE public List browseTypes() { final String sql = - "select type as value, type as name, sum(case when status='approved' then 1 else 0 end) as approved, sum(case when status='pending' then 1 else 0 end) as pending from organizations group by type order by approved desc"; - return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(BrowseEntry.class)); + "select type as code, type as name, status as group, count(status) as count from organizations group by type, status"; + return listBrowseEntries(sql); } // BROWSE BY ORG TYPE FOR USER public List browseTypesForUser(final String email) { - final String sql = "select o.type as value, o.type as name," - + "sum(case when status='approved' then 1 else 0 end) as approved, " - + "sum(case when status='pending' then 1 else 0 end) as pending " + final String sql = "select o.type as code, o.type as name," + + "o.status as group, count(o.status) as count " + "from organizations o " + "left outer join user_countries uc on (uc.country = o.country) " + "where uc.email=? " - + "group by o.type " - + "order by approved desc;"; - return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(BrowseEntry.class), email); + + "group by o.type, o.status"; + return listBrowseEntries(sql, email); + } + private List listBrowseEntries(final String sql, final Object... params) { + final Map map = new HashMap<>(); + + for (final TempBrowseEntry t : jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TempBrowseEntry.class), params)) { + if (StringUtils.isNotBlank(t.getCode())) { + if (!map.containsKey(t.getCode())) { + final BrowseEntry e = new BrowseEntry(); + e.setCode(t.getCode()); + e.setName(t.getName()); + map.put(t.getCode(), e); + } + map.get(t.getCode()).getValues().put(t.getGroup(), t.getCount()); + } + } + + return map.values().stream().sorted((o1, o2) -> StringUtils.compare(o1.getName(), o2.getName())).collect(Collectors.toList()); } public List listConflictsForId(final String id) { @@ -321,9 +348,10 @@ public class DatabaseUtils { @Transactional public void importDedupEvents() { try { - // log.info("Importing conflicts and duplicates..."); - // jdbcTemplate.update(IOUtils.toString(getClass().getResourceAsStream("/sql/importNewRels.sql"))); - // log.info("...done"); + log.info("Importing conflicts and duplicates..."); + jdbcTemplate.update(IOUtils.toString(getClass().getResourceAsStream("/sql/importDedupEvents.sql"))); + log.info("...done"); + // verifyConflictGroups(true); } catch (final Exception e) { log.error("Error importing conflicts and duplicates", e); @@ -331,13 +359,98 @@ public class DatabaseUtils { } @Transactional - public List fixDuplicate(final String masterId, final String otherId) { + public OrganizationView markAsDeleted(final String id, final String user) { + final OffsetDateTime now = OffsetDateTime.now(); + updateStatus(id, OrganizationStatus.deleted, user, now); + return organizationViewRepository.findById(id).get(); + } + + @Transactional + public OrganizationView markAsDiscarded(final String id, final String user) { + final OffsetDateTime now = OffsetDateTime.now(); + updateStatus(id, OrganizationStatus.discarded, user, now); + + openaireDuplicateRepository.findByLocalId(id).forEach(d -> updateStatus(d.getOaOriginalId(), OrganizationStatus.suggested, user, now)); + + return organizationViewRepository.findById(id).get(); + } + + private void updateStatus(final String id, final OrganizationStatus status, final String user, final OffsetDateTime now) { + organizationRepository.updateStatus(id, status.toString()); + organizationRepository.updateModificationDate(id, user, now); + } + + @Transactional + public String fixConflict(final List ids, final String user) { + + final List views = + ids.stream().map(organizationViewRepository::findById).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); + + // I create a new org + final OrganizationView newOrg = new OrganizationView(); + newOrg.setId(null); + newOrg.setStatus(null); + newOrg.setName(findFirstString(views, OrganizationView::getName)); + newOrg.setType(findFirstString(views, OrganizationView::getType)); + newOrg.setLat(findFirstNumber(views, OrganizationView::getLat)); + newOrg.setLng(findFirstNumber(views, OrganizationView::getLng)); + newOrg.setCity(findFirstString(views, OrganizationView::getCity)); + newOrg.setCountry(findFirstString(views, OrganizationView::getCountry)); + newOrg.setOtherIdentifiers(findAll(views, OrganizationView::getOtherIdentifiers)); + newOrg.setOtherNames(findAll(views, OrganizationView::getOtherNames)); + newOrg.setAcronyms(findAll(views, OrganizationView::getAcronyms)); + newOrg.setUrls(findAll(views, OrganizationView::getUrls)); + newOrg.setRelations(findAll(views, OrganizationView::getRelations)); + + newOrg.getOtherNames() + .addAll(views.stream() + .map(OrganizationView::getName) + .filter(StringUtils::isNotBlank) + .filter(s -> StringUtils.equalsIgnoreCase(s, newOrg.getName())) + .map(s -> new eu.dnetlib.organizations.model.view.OtherName(s, "UNKNOWN")) + .collect(Collectors.toList())); + + final String masterId = insertOrUpdateOrganization(newOrg, user, false); + + // I hide the merged organizations + ids.forEach(id -> hideConflictOrgs(masterId, id)); + + // I reassign the duplicated to the new org + final List newDuplicates = ids.stream() + .map(openaireDuplicateRepository::findByLocalId) + .flatMap(l -> l.stream()) + .filter(d -> d.getRelType().equals(SimilarityType.is_similar.toString())) + .map(d -> new OpenaireDuplicate(masterId, d.getOaOriginalId(), d.getRelType())) + .collect(Collectors.toList()); + newDuplicates.forEach(d -> d.setLocalId(masterId)); + + final OffsetDateTime now = OffsetDateTime.now(); + + for (int i = 0; i < ids.size(); i++) { + for (int j = i + 1; j < ids.size(); j++) { + openaireConflictRepository.updateStatus(ids.get(i), ids.get(j), SimilarityType.is_similar.toString(), user, now); + } + } + + return masterId; + } + + private String findFirstString(final List views, final Function mapper) { + return views.stream().map(mapper).filter(StringUtils::isNotBlank).findFirst().orElse(null); + } + + private Double findFirstNumber(final List views, final Function mapper) { + return views.stream().map(mapper).filter(Objects::nonNull).filter(n -> n != 0).findFirst().orElse(0.0); + } + + private Set findAll(final List views, final Function> mapper) { + return views.stream().map(mapper).flatMap(s -> s.stream()).collect(Collectors.toCollection(LinkedHashSet::new)); + } + + private List hideConflictOrgs(final String masterId, final String otherId) { organizationRepository.updateStatus(otherId, OrganizationStatus.hidden.toString()); openaireConflictRepository.findById(new OpenaireConflictPK(masterId, otherId)).ifPresent(openaireConflictRepository::delete); openaireConflictRepository.findById(new OpenaireConflictPK(otherId, masterId)).ifPresent(openaireConflictRepository::delete); - - // TODO Merge the organizations ??? - return makeRelation(masterId, otherId, RelationType.Merges); } diff --git a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/OrganizationStatus.java b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/OrganizationStatus.java index 6ae54092..0b135680 100644 --- a/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/OrganizationStatus.java +++ b/apps/dnet-orgs-database-application/src/main/java/eu/dnetlib/organizations/utils/OrganizationStatus.java @@ -1,10 +1,10 @@ package eu.dnetlib.organizations.utils; public enum OrganizationStatus { - pending, - approved, - discarded, - hidden, - deleted, - duplicate + suggested, // from user or dedup depends by created_by field + approved, // normal status of valid organizations + discarded, // suggested organization that have been rejected by an administrator + hidden, // hidden organizations after the fix of a conflict + deleted, // organizations that are virtually deleted + duplicate // organizations that duplicate of a valid organization (their id is not an openorgs id) } diff --git a/apps/dnet-orgs-database-application/src/main/resources/application.properties b/apps/dnet-orgs-database-application/src/main/resources/application.properties index f1ff84e8..878c1797 100644 --- a/apps/dnet-orgs-database-application/src/main/resources/application.properties +++ b/apps/dnet-orgs-database-application/src/main/resources/application.properties @@ -13,9 +13,9 @@ spring.jpa.properties.hibernate.hbm2dll.extra_physical_table_types = MATERIALIZE spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.open-in-view=true -spring.jpa.properties.hibernate.show_sql=true -spring.jpa.properties.hibernate.use_sql_comments=true -spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.show_sql=false +spring.jpa.properties.hibernate.use_sql_comments=false +spring.jpa.properties.hibernate.format_sql=false # the ICM private network openaire.api.valid.subnet = 10.19.65.0/24 diff --git a/apps/dnet-orgs-database-application/src/main/resources/sql/importDedupEvents.sql b/apps/dnet-orgs-database-application/src/main/resources/sql/importDedupEvents.sql new file mode 100644 index 00000000..9df9e8dd --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/sql/importDedupEvents.sql @@ -0,0 +1,43 @@ +BEGIN; + +DELETE FROM oa_conflicts WHERE created_by = 'dedupWf' and reltype = 'suggested'; +DELETE FROM oa_duplicates WHERE created_by = 'dedupWf' and reltype = 'suggested'; +DELETE FROM organizations WHERE created_by = 'dedupWf' and modified_by = 'dedupWf'; + +-- FIX IMPORT DATA +UPDATE tmp_dedup_events SET oa_country = 'UNKNOWN' WHERE oa_country = '' OR oa_country IS NULL; + +-- NEW ORGANIZATIONS +INSERT INTO organizations(id, name, country, status, created_by, modified_by) +SELECT oa_original_id, oa_name, oa_country, 'suggested', 'dedupWf', 'dedupWf' +FROM tmp_dedup_events +WHERE oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' AND oa_original_id = local_id +ON CONFLICT DO NOTHING; + +INSERT INTO organizations(id, name, country, status, created_by, modified_by) +SELECT oa_original_id, oa_name, oa_country, 'duplicate', 'dedupWf', 'dedupWf' +FROM tmp_dedup_events +WHERE oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' AND oa_original_id != local_id +ON CONFLICT DO NOTHING; + + +INSERT INTO acronyms(id, acronym) SELECT oa_original_id, oa_acronym FROM tmp_dedup_events WHERE oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' ON CONFLICT DO NOTHING; +INSERT INTO urls(id, url) SELECT oa_original_id, oa_url FROM tmp_dedup_events WHERE oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' ON CONFLICT DO NOTHING; + +-- DUPLICATES +INSERT INTO oa_duplicates (local_id, oa_original_id, oa_collectedfrom, created_by, modified_by) +SELECT local_id, oa_original_id, oa_collectedfrom, 'dedupWf', 'dedupWf' +FROM tmp_dedup_events +WHERE local_id IS NOT NULL AND local_id != '' AND oa_original_id NOT LIKE 'openorgs\_\_\_\_::%' AND local_id != oa_original_id +ON CONFLICT DO NOTHING; + + +-- CONFLICTS +INSERT INTO oa_conflicts (id1, id2, idgroup, created_by, modified_by) +SELECT local_id, oa_original_id, group_id, 'dedupWf', 'dedupWf' +FROM tmp_dedup_events +WHERE local_id LIKE 'openorgs\_\_\_\_::%' AND oa_original_id LIKE 'openorgs\_\_\_\_::%' AND local_id != oa_original_id AND group_id IS NOT NULL AND group_id != '' +ON CONFLICT DO NOTHING; + + +COMMIT; diff --git a/apps/dnet-orgs-database-application/src/main/resources/sql/importNewRels.sql b/apps/dnet-orgs-database-application/src/main/resources/sql/importNewRels.sql deleted file mode 100644 index 305f1d8e..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/sql/importNewRels.sql +++ /dev/null @@ -1,15 +0,0 @@ -DELETE FROM oa_duplicates WHERE reltype = 'suggested'; -DELETE FROM oa_conflicts WHERE reltype = 'suggested'; -UPDATE oa_conflicts SET idgroup = NULL; - -INSERT INTO oa_duplicates (local_id, oa_original_id, oa_name, oa_acronym, oa_country, oa_url, oa_collectedfrom) -SELECT local_id, oa_original_id, oa_name, oa_acronym, oa_country, oa_url, oa_collectedfrom -FROM tmp_simrels -WHERE oa_original_id NOT LIKE 'openorgs____::%' -ON CONFLICT DO NOTHING; - -INSERT INTO oa_conflicts (id1, id2) -SELECT local_id, oa_original_id -FROM tmp_simrels -WHERE oa_original_id LIKE 'openorgs____::%' -ON CONFLICT DO NOTHING; 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 116e328d..1456fcd0 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 @@ -335,13 +335,13 @@ CREATE TABLE organizations ( creation_date timestamp with time zone DEFAULT now(), modified_by text, modification_date timestamp with time zone DEFAULT now(), - status text NOT NULL DEFAULT 'pending' + status text NOT NULL DEFAULT 'suggested' ); CREATE INDEX organizations_type_idx ON organizations(type); CREATE INDEX organizations_country_idx ON organizations(country); CREATE TABLE other_ids ( - id text REFERENCES organizations(id) ON UPDATE CASCADE, + id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, otherid text, type text REFERENCES id_types(val), PRIMARY KEY (id, otherid, type) @@ -349,7 +349,7 @@ CREATE TABLE other_ids ( CREATE INDEX other_ids_id_idx ON other_ids(id); CREATE TABLE other_names ( - id text REFERENCES organizations(id) ON UPDATE CASCADE, + id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, name text, lang text REFERENCES languages(val), PRIMARY KEY (id, name, lang) @@ -357,31 +357,31 @@ CREATE TABLE other_names ( CREATE INDEX other_names_id_idx ON other_names(id); CREATE TABLE acronyms ( - id text REFERENCES organizations(id) ON UPDATE CASCADE, + id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, acronym text, PRIMARY KEY (id, acronym) ); CREATE INDEX acronyms_id_idx ON acronyms(id); CREATE TABLE relationships ( - id1 text REFERENCES organizations(id) ON UPDATE CASCADE, + id1 text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, reltype text, - id2 text REFERENCES organizations(id) ON UPDATE CASCADE, + id2 text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, PRIMARY KEY (id1, reltype, id2) ); CREATE INDEX relationships_id1_idx ON relationships(id1); CREATE INDEX relationships_id2_idx ON relationships(id2); CREATE TABLE urls ( - id text REFERENCES organizations(id) ON UPDATE CASCADE, + id text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, url text, PRIMARY KEY (id, url) ); CREATE INDEX urls_id_idx ON urls(id); CREATE TABLE oa_duplicates ( - local_id text REFERENCES organizations(id) ON UPDATE CASCADE, - oa_original_id text REFERENCES organizations(id) ON UPDATE CASCADE, + 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, oa_name text NOT NULL, oa_acronym text, oa_country text, @@ -389,6 +389,7 @@ CREATE TABLE oa_duplicates ( oa_collectedfrom text, reltype text NOT NULL DEFAULT 'suggested', creation_date timestamp DEFAULT NOW(), + created_by text, modification_date timestamp, modified_by text, PRIMARY KEY (local_id, oa_original_id) @@ -419,11 +420,12 @@ GROUP BY d.reltype; CREATE TABLE oa_conflicts ( - id1 text REFERENCES organizations(id) ON UPDATE CASCADE, - id2 text REFERENCES organizations(id) ON UPDATE CASCADE, + id1 text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, + id2 text REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE, reltype text NOT NULL DEFAULT 'suggested', idgroup text, creation_date timestamp DEFAULT NOW(), + created_by text, modification_date timestamp, modified_by text, PRIMARY KEY (id1, id2) @@ -485,10 +487,12 @@ CREATE VIEW organizations_simple_view AS SELECT org.city, org.country, org.status, - array_remove(array_agg(DISTINCT a.acronym), NULL) AS acronyms + array_remove(array_agg(DISTINCT a.acronym), NULL) AS acronyms, + array_remove(array_agg(DISTINCT u.url), NULL) AS urls FROM organizations org LEFT OUTER JOIN acronyms a ON (org.id = a.id) + LEFT OUTER JOIN urls u ON (org.id = u.id) GROUP BY org.id, org.name, @@ -513,9 +517,9 @@ CREATE VIEW suggestions_info_by_country_view AS SELECT c.val AS country, coalesce(t2.n_conflicts, 0) AS n_conflicts, coalesce(t3.n_pending_orgs, 0) AS n_pending_orgs FROM countries c - LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT d.*) AS n_duplicates FROM oa_duplicates d LEFT OUTER JOIN organizations o ON (d.local_id = o.id) WHERE d.reltype = 'suggested' GROUP BY o.country) AS t1 ON (t1.country = c.val) - LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT c.idgroup) AS n_conflicts FROM oa_conflicts c LEFT OUTER JOIN organizations o ON (c.id1 = o.id) WHERE c.reltype = 'suggested' GROUP BY o.country) AS t2 ON (t2.country = c.val) - LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT o.id) AS n_pending_orgs FROM organizations o WHERE o.status = 'pending' GROUP BY o.country) AS t3 ON (t3.country = c.val); + LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT d.*) AS n_duplicates FROM oa_duplicates d LEFT OUTER JOIN organizations o ON (d.local_id = o.id) WHERE d.reltype = 'suggested' AND o.status = 'approved' GROUP BY o.country) AS t1 ON (t1.country = c.val) + LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT c.idgroup) AS n_conflicts FROM oa_conflicts c LEFT OUTER JOIN organizations o ON (c.id1 = o.id) WHERE c.reltype = 'suggested' AND o.status = 'approved' GROUP BY o.country) AS t2 ON (t2.country = c.val) + LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT o.id) AS n_pending_orgs FROM organizations o WHERE o.status = 'suggested' GROUP BY o.country) AS t3 ON (t3.country = c.val); CREATE VIEW conflict_groups_view AS SELECT c.idgroup AS idgroup, @@ -534,7 +538,12 @@ FROM LEFT OUTER JOIN organizations o1 ON (c.id1 = o1.id) LEFT OUTER JOIN organizations o2 ON (c.id2 = o2.id) WHERE - o1.id IS NOT NULL AND O2.id IS NOT NULL AND c.idgroup IS NOT NULL; + o1.id IS NOT NULL + AND o2.id IS NOT NULL + AND o1.status = 'approved' + AND o2.status = 'approved' + AND c.idgroup IS NOT NULL + AND c.reltype = 'suggested'; CREATE VIEW duplicate_groups_view AS SELECT o.id, @@ -546,7 +555,7 @@ FROM oa_duplicates d LEFT OUTER JOIN organizations o ON (o.id = d.local_id) WHERE - d.reltype = 'suggested' + d.reltype = 'suggested' AND o.status = 'approved' GROUP BY o.id, o.name, o.city, o.country ORDER BY o.name; diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/browse.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/browse.html deleted file mode 100644 index f58a31ea..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/browse.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - -
{{field}}# approved# pending
{{e.name}} ({{e.value}}){{e.approved}}{{e.pending}}
diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/edit.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/edit.html deleted file mode 100644 index c9a2038e..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/edit.html +++ /dev/null @@ -1,18 +0,0 @@ -

{{info.name}}

- -
{{message}}
- -

- ID: {{info.id}}
Created at {{info.creationDate | date:'MMMM d, y HH:mm:ss'}} by {{info.createdBy}}
Modified at {{info.modificationDate | date:'MMMM d, y HH:mm:ss'}} by - {{info.modifiedBy}} -

- -
- - - - - - - -
diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/all_conflicts.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/all_conflicts.html deleted file mode 100644 index 49f72213..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/all_conflicts.html +++ /dev/null @@ -1,42 +0,0 @@ -
- -
No suggestions
- -
- -
- Country: - - -
-
- -
-
Group {{$index+1}}
- - - - - - - -
#{{$index+1}}{{o.name}} {{o.city || '-'}}, {{o.country}}{{o.type}}
- -
- -
- - diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/all_duplicates.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/all_duplicates.html deleted file mode 100644 index 84754aa5..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/all_duplicates.html +++ /dev/null @@ -1,62 +0,0 @@ -
- -
No suggestions
- -
- -
- Country: - - -
-
- - - - - - - - - - - - - - - - -
OrganizationPlace# pending duplicates
- {{d.name}} - - {{d.city || '-'}}, {{d.country}}{{d.numberOfDuplicates}}
-
- - - diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_conflicts.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_conflicts.html deleted file mode 100644 index 199e7001..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_conflicts.html +++ /dev/null @@ -1,28 +0,0 @@ -
-
Conflicts
- -
No conflicts
- - - - - - - - - - - - - - - - -
Related organizationTypePlace
{{sr.name}}
{{sr.id}}
{{sr.type}} {{sr.city || '-'}}, {{sr.country}}
- - -
- - \ No newline at end of file diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_metadata.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_metadata.html deleted file mode 100644 index f56d2b7f..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_metadata.html +++ /dev/null @@ -1,297 +0,0 @@ -
-
- -
- This is a pending organization. Please evaluate it before approving. -
-
- This organization is managed by the system. You can not edit. -
- - -
- Official name and type - -
-
-
-
name
-
- -
-
type
-
- -
-
-
-
- Geographical location - -
-
-
-
city
-
- -
-
country
-
- - -
-
lat
-
- -
-
lng
-
- -
-
-
- -
- Other names and identifiers - -
-
-
-
Acronyms
- - - - - - - - - - - - - -
{{a}} - -
- -
-
-
-
- -
-
-
-
Aliases
- - - - - - - - - - - - - - - - - - - - - - -
namelanguage
{{n.name}}{{n.lang}} - -
- -
-
-
-
- -
-
-
-
Identifiers
- - - - - - - - - - - - - - - - - - - - - - -
idtype
{{id.id}}{{id.type}} - -
- -
-
-
-
- -
-
-
-
Urls
- - - - - - - - - - - - - -
{{u}} - -
- -
-
-
-
-
- -
- Relations -
-
-
-
Relations
- - - - - - - - - - - - - - - - - - - - - - - - - -
Related organizationType
No relations
{{r.relatedOrgName}}{{r.type}} - -
- - - - - - -
-
-
-
-
- - - -
-
- - diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/pending_orgs.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/pending_orgs.html deleted file mode 100644 index 69a0eaf2..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/pending_orgs.html +++ /dev/null @@ -1,43 +0,0 @@ -
- -
No suggestions
- -
- -
- Country: - - -
-
- - - - - - - - - - - - - - - - - - -
Organization namePlaceAcronymsType
- {{o.name}} - {{o.city || '-'}}, {{o.country}}{{o.acronyms.join()}}{{o.type}}
- -
diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/menu/org_tabs_menu.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/menu/org_tabs_menu.html deleted file mode 100644 index 8bde2804..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/menu/org_tabs_menu.html +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/new.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/new.html deleted file mode 100644 index 31f8a1b7..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/new.html +++ /dev/null @@ -1,8 +0,0 @@ -
-
- -
- -
diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/users.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/admin/users.html similarity index 100% rename from apps/dnet-orgs-database-application/src/main/resources/static/resources/html/users.html rename to apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/admin/users.html diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/conflicts.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/conflicts.html new file mode 100644 index 00000000..24205bfd --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/conflicts.html @@ -0,0 +1,44 @@ +

Conflicts

+ +

No suggestions

+ +
+ +
+ Country: + + +
+
+ +
+
Group {{$index+1}}
+ + + + + + + +
#{{$index+1}}{{o.name}} {{o.city || '-'}}, {{o.country}}{{o.type}}
+ +
+ + + + + diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/duplicates.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/duplicates.html new file mode 100644 index 00000000..536db91e --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/duplicates.html @@ -0,0 +1,60 @@ +

Duplicates

+ +

No duplicates

+ +
+ +
+ Country: + + +
+
+ + + + + + + + + + + + + + + + +
OrganizationPlace# pending duplicates
+ {{d.name}} + + {{d.city || '-'}}, {{d.country}}{{d.numberOfDuplicates}}
+ + diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/new.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/new.html new file mode 100644 index 00000000..65fda180 --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/new.html @@ -0,0 +1,11 @@ +
+
+ +
+
+ + +
+
diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/pendingOrgs.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/pendingOrgs.html new file mode 100644 index 00000000..27a88f55 --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/advanced/pendingOrgs.html @@ -0,0 +1,42 @@ +

Pending Organizations

+ +

No pending organizations

+ +
+ +
+ Country: + + +
+
+ + + + + + + + + + + + + + + + + + +
Organization namePlaceAcronymsType
+ {{o.name}} + {{o.city || '-'}}, {{o.country}}{{o.acronyms.join()}}{{o.type}}
+ diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/edit/edit.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/edit/edit.html new file mode 100644 index 00000000..5a6239ca --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/edit/edit.html @@ -0,0 +1,45 @@ +

{{info.name}}

+ +
{{message}}
+ +

+ ID: {{info.id}}
Created at {{info.creationDate | date:'MMMM d, y HH:mm:ss'}} by {{info.createdBy}}
Modified at {{info.modificationDate | date:'MMMM d, y HH:mm:ss'}} by + {{info.modifiedBy}} +

+ +
+ + +
+ + + + + + + + +
+ +
+ + +
+ +
+ +
+ +
diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/browse.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/browse.html new file mode 100644 index 00000000..f2eac3fb --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/browse.html @@ -0,0 +1,50 @@ +

{{title}}

+ +

No entries

+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + +
{{field}}# approved# suggested# deleted# duplicated# discarded# hidden
{{e.name}} ({{e.code}}) + {{e.values.approved}} + - + + {{e.values.suggested}} + - + + {{e.values.deleted}} + - + + {{e.values.duplicate}} + - + + {{e.values.discarded}} + - + + {{e.values.hidden}} + - +
diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/resultsByCountry.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/resultsByCountry.html similarity index 100% rename from apps/dnet-orgs-database-application/src/main/resources/static/resources/html/resultsByCountry.html rename to apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/resultsByCountry.html diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/resultsByType.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/resultsByType.html similarity index 100% rename from apps/dnet-orgs-database-application/src/main/resources/static/resources/html/resultsByType.html rename to apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/resultsByType.html diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/search.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/search.html similarity index 100% rename from apps/dnet-orgs-database-application/src/main/resources/static/resources/html/search.html rename to apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/search.html diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/searchResults.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/searchResults.html similarity index 100% rename from apps/dnet-orgs-database-application/src/main/resources/static/resources/html/searchResults.html rename to apps/dnet-orgs-database-application/src/main/resources/static/resources/html/pages/search/searchResults.html 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 new file mode 100644 index 00000000..503a23bc --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_conflicts.html @@ -0,0 +1,31 @@ +
+
Conflicts
+ +
No conflicts
+ + + + + + + + + + + + + + + + +
#1{{org.name}} current {{org.city || '-'}}, {{org.country}}{{org.type}}
#{{$index+2}}{{sr.name}} {{sr.city || '-'}}, {{sr.country}}{{sr.type}}
+ + +
+ + + + diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_dedup_events.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_dedup_events.html deleted file mode 100644 index e35c6531..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_dedup_events.html +++ /dev/null @@ -1,11 +0,0 @@ -
- - - - - - - -
- - diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_details.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_details.html index ef37c4d7..7ee7dfed 100644 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_details.html +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_details.html @@ -5,7 +5,7 @@ 'text-white bg-info' : show == 'info', 'bg-secondary' : show == 'secondary', }">{{orgTitle}} - +
diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_duplicates.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_duplicates.html similarity index 86% rename from apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_duplicates.html rename to apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_duplicates.html index bbc6a5fc..66fac712 100644 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/forms/org_duplicates.html +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_duplicates.html @@ -38,8 +38,11 @@
Name {{org.name}}
- + + diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_metadata.form.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_metadata.form.html new file mode 100644 index 00000000..291d6c32 --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/org_metadata.form.html @@ -0,0 +1,312 @@ +
+ +
+ This is a pending organization. Please evaluate it before approving. +
+
+ {{org.status}} This organization is managed by the system. You can not edit. +
+
+ {{org.status}} You are not authorized to modify this organization. +
+ +
+ Official name and type + +
+
+
+
name
+
+ +
+
type
+
+ +
+
+
+
+ Geographical location + +
+
+
+
city
+
+ +
+
country
+
+ + +
+
lat
+
+ +
+
lng
+
+ +
+
+
+ +
+ Other names and identifiers + +
+
+
+
Acronyms
+ + + + + + + + + + + + + +
{{a}} + +
+ +
+
+
+
+ +
+
+
+
Aliases
+ + + + + + + + + + + + + + + + + + + + + + +
namelanguage
{{n.name}}{{n.lang}} + +
+ +
+
+
+
+ +
+
+
+
Identifiers
+ + + + + + + + + + + + + + + + + + + + + + +
idtype
{{id.id}}{{id.type}} + +
+ +
+
+
+
+ +
+
+
+
Urls
+ + + + + + + + + + + + + +
{{u}} + +
+ +
+
+
+
+
+ +
+ Relations +
+
+
+
Relations
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Related organizationType
No relations
{{r.relatedOrgName}}{{r.type}} + +
+ + + + + + +
+
+
+
+
+ + +
+ + +
+
+ +
+
+ + +
+
+ +
+
+ +
+ +
+ + + diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/modals/resolve_conflicts.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/resolve_conflicts.modal.html similarity index 100% rename from apps/dnet-orgs-database-application/src/main/resources/static/resources/html/modals/resolve_conflicts.html rename to apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/resolve_conflicts.modal.html diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/modals/select_org.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/select_org.modal.html similarity index 97% rename from apps/dnet-orgs-database-application/src/main/resources/static/resources/html/modals/select_org.html rename to apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/select_org.modal.html index 9acc5a0f..243eede8 100644 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/modals/select_org.html +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/parts/select_org.modal.html @@ -16,6 +16,7 @@ diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/suggestions.html b/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/suggestions.html deleted file mode 100644 index 0d947cd0..00000000 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/html/suggestions.html +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/js/organizations.js b/apps/dnet-orgs-database-application/src/main/resources/static/resources/js/organizations.js index 67ea4246..90003079 100644 --- a/apps/dnet-orgs-database-application/src/main/resources/static/resources/js/organizations.js +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/js/organizations.js @@ -4,12 +4,9 @@ orgsModule.service('vocabulariesService', function($http) { this.vocs = {}; this.getVocs = function(f) { if (Object.keys(this.vocs).length === 0) { - $http.get('api/vocabularies').then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } + call_http_get($http, 'api/vocabularies', function(res) { this.vocs = res.data; f(angular.copy(this.vocs)); - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); }); } else { f(this.vocs); @@ -17,29 +14,66 @@ orgsModule.service('vocabulariesService', function($http) { }; }); -orgsModule.directive('selectOrgModal', function($http) { +orgsModule.factory('suggestionInfo', function($http) { + var info = { data : {} }; + + var getInfo = function() { return info; }; + + var updateInfo = function(callback) { + call_http_get($http, 'api/organizations/suggestionsInfo', function(res) { + info.data = res.data; + if (callback) { callback(info); } + }); + }; + + return { + getInfo: getInfo, + updateInfo: updateInfo + }; + +}); + +orgsModule.controller('menuCtrl', function ($scope, suggestionInfo) { + $scope.info = suggestionInfo.getInfo(); + suggestionInfo.updateInfo(null); +}); + +orgsModule.directive('selectOrgModal', function($http, $timeout) { return { restrict: 'E', scope: { - 'modalId' : '@', - 'selectedOrg' : '=' + 'modalId' : '@', + 'filterStatus' : '@', + 'selectedOrg' : '=', + 'onSelect' : '&' }, - templateUrl: 'resources/html/modals/select_org.html', + templateUrl: 'resources/html/parts/select_org.modal.html', link: function(scope, element, attrs, ctrl) { scope.searchOrgs = {}; - scope.searchValue = ''; + scope.searchText = ''; + scope.searchValue = ''; scope.search = function(text, page, size) { scope.searchOrgs = {}; - $http.get('api/organizations/search/' + page + '/' + size + '?q=' + text).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } + call_http_get($http, 'api/organizations/search/' + page + '/' + size + '?status='+ scope.filterStatus + '&q=' + text, function(res) { scope.searchValue = text; scope.searchOrgs = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); }); } + + scope.selectOrg = function() { + $timeout(function() { + scope.searchText = ''; + scope.searchValue = ''; + scope.searchOrgs = {}; + }, 1000); + + if (scope.onSelect) { + scope.onSelect(); + } + } + } } }); @@ -52,7 +86,7 @@ orgsModule.directive('resolveConflictsModal', function($http, $route) { 'orgs' : '=', 'selectedOrgs' : '=' }, - templateUrl: 'resources/html/modals/resolve_conflicts.html', + templateUrl: 'resources/html/parts/resolve_conflicts.modal.html', link: function(scope, element, attrs, ctrl) { scope.selectOrg = function(org) { var sel = angular.copy(org); @@ -75,15 +109,10 @@ orgsModule.directive('resolveConflictsModal', function($http, $route) { else { otherIds.push(o.id); } }); if (masterId && otherIds.length > 0) { - $http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8"; - $http.post('api/organizations/conflicts/fix/' + masterId, otherIds).then(function successCallback(res) { - if ((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } + + call_http_post($http, 'api/organizations/conflicts/fix/' + masterId, otherIds, function(res) { $('#' + scope.modalId).modal('hide'); - $('#' + scope.modalId).on('hidden.bs.modal', function (e) { - $route.reload(); - }); - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); + $('#' + scope.modalId).on('hidden.bs.modal', function (e) { $route.reload(); }); }); } } @@ -91,67 +120,17 @@ orgsModule.directive('resolveConflictsModal', function($http, $route) { } }); -orgsModule.directive('orgTabsMenu', function($http) { - return { - restrict: 'E', - scope: { - 'orgId' : '@', - 'info' : '=', - 'selected' : '=', - 'org' : '=', - 'events' : '=', - }, - templateUrl: 'resources/html/menu/org_tabs_menu.html', - link: function(scope, element, attrs, ctrl) { - - scope.loadOrg = function() { - scope.org = {}; - $http.get('api/organizations/get?id=' + scope.orgId).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - scope.org = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - scope.selected = 1; - } - - scope.loadDedupEvents = function() { - scope.events = {}; - - $http.get('api/organizations/conflicts?id=' + scope.orgId).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - scope.events.conflicts = res.data; - $http.get('api/organizations/duplicates?id=' + scope.orgId).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - scope.events.duplicates = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - scope.selected = 2; - }; - - scope.loadOrg(); - } - } -}); - - orgsModule.directive('orgFormMetadata', function($http, $location, $route, $routeParams) { return { restrict: 'E', scope: { - 'orgId' : '@', 'org' : '=', 'vocabularies' : '=', - 'mode' : '@', // insert, update or approve + 'mode' : '@', // insert_full, insert_pending, update_simple, update_full, approve, readonly, not_authorized 'infoMethod' : '&' }, - templateUrl: 'resources/html/forms/org_metadata.html', + templateUrl: 'resources/html/parts/org_metadata.form.html', link: function(scope, element, attrs, ctrl) { - scope.newRelation = {}; scope.newRelType = ''; @@ -170,62 +149,34 @@ orgsModule.directive('orgFormMetadata', function($http, $location, $route, $rout } scope.save = function() { - $http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8"; - $http.post('api/organizations/save', scope.org).then(function successCallback(res) { - if ((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - else if (scope.mode == 'insert') { $location.url('/edit/1/' + res.data[0]); } + call_http_post($http, 'api/organizations/save', scope.org, function(res) { + if (scope.mode == 'insert') { $location.url('/edit/1/' + res.data[0]); } else if (scope.mode == 'approve') { $location.url('/edit/3/' + res.data[0]); } else if ($routeParams.msg == 2) { $route.reload(); } else { $location.url('/edit/2/' + res.data[0]); } - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); }); } + + scope.deleteOrg = function() { + if (confirm("Are you sure?")) { + call_http_get($http, 'api/organizations/delete?id=' + scope.org.id, function(res) { + alert("Organization marked as deleted !!!"); + $route.reload(); + }); + } + }; + scope.saveAsDiscarded = function() { + if (confirm("Are you sure?")) { + call_http_get($http, 'api/organizations/discard?id=' + scope.org.id, function(res) { + alert("Organization marked as discarded !!!"); + $route.reload(); + }); + } + }; } } }); -orgsModule.directive('orgDedupEvents', function($http, $location, $route) { - return { - restrict: 'E', - scope: { - 'orgId' : '@', - 'events' : '=', - 'vocabularies' : '=', - 'infoMethod' : '&' - }, - templateUrl: 'resources/html/parts/org_dedup_events.html', - link: function(scope, element, attrs, ctrl) { - - scope.currentOrgDetails = {}; - - $http.get('api/organizations/get?id=' + scope.orgId).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - scope.currentOrgDetails = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - - scope.saveDuplicates = function() { - $http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8"; - $http.post('api/organizations/duplicates', scope.events.duplicates).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - if (scope.infoMethod) { scope.infoMethod(); } - alert('Events updated !!!'); - scope.events.duplicates = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - } - - scope.saveConflicts = function() { - alert('todo'); - } - } - } -}); - - orgsModule.directive('orgDetails', function($http, $location, $route) { return { restrict: 'E', @@ -247,6 +198,7 @@ orgsModule.directive('orgResultsPage', function($http, $location, $route) { 'orgs' : '=', 'nextFunction' : '&', 'prevFunction' : '&', + 'onSelect' : '&', 'selectedOrg' : '=', 'mode' : '@' }, @@ -255,94 +207,90 @@ orgsModule.directive('orgResultsPage', function($http, $location, $route) { scope.selectOrg = function(o) { scope.selectedOrg.id = o.id; scope.selectedOrg.name = o.name; - scope.selectedOrg.type = o.name; - scope.selectedOrg.city = o.name; - scope.selectedOrg.country = o.name; - scope.selectedOrg.acronyms = o.name; - scope.selectedOrg.approved = o.name; + scope.selectedOrg.type = o.type; + scope.selectedOrg.city = o.city; + scope.selectedOrg.country = o.country; + scope.selectedOrg.acronyms = o.acronyms; + scope.selectedOrg.urls = o.urls; + scope.selectedOrg.status = o.status; + + if (scope.onSelect) { + scope.onSelect(); + } + } } } }); -orgsModule.directive('allConflicts', function($http, $location, $route, $q) { - return { - restrict: 'E', - scope: { - 'conflicts' : '=', - 'country' : '@', - 'info' : '=', - 'infoMethod' : '&' - }, - templateUrl: 'resources/html/forms/all_conflicts.html', - link: function(scope, element, attrs, ctrl) { - scope.orgs = []; - - scope.prepareConflictsModal = function(list) { - scope.orgs = []; - scope.selectedOrgs = []; - - var gets = list.map((o) => $http.get('api/organizations/get?id=' + o.id)); - - $q.all(gets).then(function(responses) { - scope.orgs = responses.map((resp) => resp.data); - angular.forEach(scope.orgs, function(org) { org.show = 'secondary'; }); - }); - } - - } - } -}); - - -orgsModule.directive('pendingOrgs', function($http, $location, $route, $q) { - return { - restrict: 'E', - scope: { - 'orgs' : '=', - 'country' : '@', - 'info' : '=', - 'infoMethod' : '&' - }, - templateUrl: 'resources/html/forms/pending_orgs.html', - link: function(scope, element, attrs, ctrl) { - - } - } -}); - -orgsModule.directive('orgFormDuplicates', function($http, $location, $route) { +orgsModule.directive('orgDuplicates', function($http, $location, $route) { return { restrict: 'E', scope: { + 'orgId' : '@', 'duplicates' : '=', 'showSaveButton' : '@', 'saveFunction' : '&' }, - templateUrl: 'resources/html/forms/org_duplicates.html', - link: function(scope, element, attrs, ctrl) {} + templateUrl: 'resources/html/parts/org_duplicates.html', + link: function(scope, element, attrs, ctrl) { + scope.newDuplicate = {}; + + scope.addDuplicate = function() { + scope.duplicates.push({ + 'localId' : scope.orgId, + 'oaOriginalId' : scope.newDuplicate.id, + 'oaName' : scope.newDuplicate.name, + 'oaAcronym' : scope.newDuplicate.acronyms.join(), + 'oaCountry' : scope.newDuplicate.country, + 'oaUrl' : scope.newDuplicate.urls.join(), + 'oaCollectedFrom' : 'user', + 'relType' : 'is_similar' + }); + + + call_http_get($http, 'api/organizations/duplicates?id=' + scope.newDuplicate.id, function(res) { + angular.forEach(res.data, function(dup) { + dup.localId = scope.orgId; + scope.duplicates.push(dup); + }); + }); + + } + + } } }); -orgsModule.directive('orgFormConflicts', function($http, $location, $route, $q) { +orgsModule.directive('orgConflicts', function($http, $location, $route, $q) { return { restrict: 'E', scope: { - 'orgId' : '@', + 'org' : '=', 'conflicts' : '=', - 'showSaveButton' : '@', - 'saveFunction' : '&' + 'showSaveButton' : '@' }, - templateUrl: 'resources/html/forms/org_conflicts.html', + templateUrl: 'resources/html/parts/org_conflicts.html', link: function(scope, element, attrs, ctrl) { scope.candidateConflicts = []; scope.selectedConflicts = []; + scope.newConflict = {}; + + scope.addConflict = function() { + scope.conflicts.push({ + 'id' : scope.newConflict.id, + 'name' : scope.newConflict.name, + 'type' : scope.newConflict.type, + 'city' : scope.newConflict.city, + 'country': scope.newConflict.country + }); + } scope.prepareConflictsModal = function() { scope.candidateConflicts = []; scope.selectedConflicts = []; - var gets = [ $http.get('api/organizations/get?id=' + scope.orgId) ]; + var gets = [ $http.get('api/organizations/get?id=' + scope.org.id) ]; angular.forEach(scope.conflicts, function(c) { gets.push($http.get('api/organizations/get?id=' + c.id)); }); $q.all(gets).then(function(responses) { @@ -354,83 +302,21 @@ orgsModule.directive('orgFormConflicts', function($http, $location, $route, $q) } }); -orgsModule.directive('allDuplicates', function($http, $location, $route, $timeout) { - return { - restrict: 'E', - scope: { - 'duplicates' : '=', - 'country' : '@', - 'info' : '=', - 'infoMethod' : '&' - }, - templateUrl: 'resources/html/forms/all_duplicates.html', - link: function(scope, element, attrs, ctrl) { - scope.currentOrg = {}; - scope.currentOrgDetails = {}; - scope.currentDuplicates = []; - - scope.prepareDuplicatesModal = function(org) { - scope.currentOrg = org; - scope.currentOrgDetails = {}; - scope.currentDuplicates = []; - - $http.get('api/organizations/get?id=' + org.id).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - scope.currentOrgDetails = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - - $http.get('api/organizations/duplicates?id=' + org.id).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - scope.currentDuplicates = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - }; - - scope.saveCurrentDuplicates = function() { - $http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8"; - $http.post('api/organizations/duplicates', scope.currentDuplicates).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - if (scope.infoMethod) { scope.infoMethod(); } - scope.currentOrg.numberOfDuplicates = 0; - for (var i=0; i 1) { - $route.reload(); - } else { - $location.url('/suggestions/_/1'); - } - }, 600); - - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - }; - } - } -}); - orgsModule.config(function($routeProvider) { $routeProvider - .when('/new', { templateUrl: 'resources/html/new.html', controller: 'newOrgCtrl' }) - .when('/search', { templateUrl: 'resources/html/search.html', controller: 'searchCtrl' }) - .when('/searchResults/:page/:size/:text*', { templateUrl: 'resources/html/searchResults.html', controller: 'searchResultsCtrl' }) - .when('/countries', { templateUrl: 'resources/html/browse.html', controller: 'countriesCtrl' }) - .when('/byCountry/:page/:size/:code*', { templateUrl: 'resources/html/resultsByCountry.html', controller: 'byCountryCtrl' }) - .when('/types', { templateUrl: 'resources/html/browse.html', controller: 'typesCtrl' }) - .when('/byType/:page/:size/:type*', { templateUrl: 'resources/html/resultsByType.html', controller: 'byTypeCtrl' }) - .when('/edit/:msg/:id', { templateUrl: 'resources/html/edit.html', controller: 'showEditCtrl' }) - .when('/suggestions/:country/:tab', { templateUrl: 'resources/html/suggestions.html', controller: 'showSuggestionsCtrl' }) - .when('/users', { templateUrl: 'resources/html/users.html', controller: 'usersCtrl' }) - .otherwise({ redirectTo: '/suggestions/_/0' }); + .when('/search', { templateUrl: 'resources/html/pages/search/search.html', controller: 'searchCtrl' }) + .when('/searchResults/:page/:size/:text*', { templateUrl: 'resources/html/pages/search/searchResults.html', controller: 'searchResultsCtrl' }) + .when('/countries', { templateUrl: 'resources/html/pages/search/browse.html', controller: 'countriesCtrl' }) + .when('/byCountry/:page/:size/:status/:code*', { templateUrl: 'resources/html/pages/search/resultsByCountry.html', controller: 'byCountryCtrl' }) + .when('/types', { templateUrl: 'resources/html/pages/search/browse.html', controller: 'typesCtrl' }) + .when('/byType/:page/:size/:status/:type*', { templateUrl: 'resources/html/pages/search/resultsByType.html', controller: 'byTypeCtrl' }) + .when('/edit/:msg/:id', { templateUrl: 'resources/html/pages/edit/edit.html', controller: 'showEditCtrl' }) + .when('/new', { templateUrl: 'resources/html/pages/advanced/new.html', controller: 'newOrgCtrl' }) + .when('/pendings/:country', { templateUrl: 'resources/html/pages/advanced/pendingOrgs.html', controller: 'pendingOrgsCtrl' }) + .when('/duplicates/:country', { templateUrl: 'resources/html/pages/advanced/duplicates.html', controller: 'duplicatesCtrl' }) + .when('/conflicts/:country', { templateUrl: 'resources/html/pages/advanced/conflicts.html', controller: 'conflictsCtrl' }) + .when('/users', { templateUrl: 'resources/html/pages/admin/users.html', controller: 'usersCtrl' }) + .otherwise({ redirectTo: '/search' }); }); orgsModule.filter('escape', function() { @@ -462,10 +348,9 @@ orgsModule.controller('newOrgCtrl', function ($scope, $http, $routeParams, $loca "urls": [], "relations": [] }; - + $scope.adminMode = adminMode(); $scope.vocabularies = {}; vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; }); - }); orgsModule.controller('searchCtrl', function ($scope, $location) { @@ -486,12 +371,7 @@ orgsModule.controller('searchResultsCtrl', function ($scope, $http, $routeParams } $scope.orgs = {}; - $http.get('api/organizations/search/' + $routeParams.page + '/' + $routeParams.size + '?q=' + $scope.searchText).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.orgs = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); + call_http_get($http, 'api/organizations/search/' + $routeParams.page + '/' + $routeParams.size + '?q=' + $scope.searchText, function(res) { $scope.orgs = res.data; }); $scope.prev = function() { if ($scope.searchText) { @@ -511,16 +391,13 @@ orgsModule.controller('searchResultsCtrl', function ($scope, $http, $routeParams }); orgsModule.controller('countriesCtrl', function ($scope, $http, $routeParams) { + + $scope.title = 'Countries'; $scope.field = 'Country'; $scope.resultsBasePath = '/byCountry' $scope.entries = []; - $http.get('api/organizations/browse/countries').then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.entries = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); + call_http_get($http, 'api/organizations/browse/countries', function(res) { $scope.entries = res.data; }); }); @@ -528,35 +405,25 @@ orgsModule.controller('byCountryCtrl', function ($scope, $http, $routeParams, $l $scope.fieldValue = decodeURIComponent($routeParams.code); $scope.orgs = {}; - $http.get('api/organizations/byCountry/' + $routeParams.code + '/' + $routeParams.page + '/' + $routeParams.size).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.orgs = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); + call_http_get($http, 'api/organizations/byCountry/' + $routeParams.status + '/' + $routeParams.code + '/' + $routeParams.page + '/' + $routeParams.size, function(res) { $scope.orgs = res.data; }); $scope.prev = function() { - $location.url('/byCountry/' + ($scope.orgs.number - 1) + '/' + $scope.orgs.size + '/' + encodeURIComponent($scope.fieldValue)); + $location.url('/byCountry/' + ($scope.orgs.number - 1) + '/' + $scope.orgs.size + '/' + $routeParams.status + '/' + encodeURIComponent($scope.fieldValue)); } $scope.next = function() { - $location.url('/byCountry/' + ($scope.orgs.number + 1) + '/' + $scope.orgs.size + '/' + encodeURIComponent($scope.fieldValue)); + $location.url('/byCountry/' + ($scope.orgs.number + 1) + '/' + $scope.orgs.size + '/' + $routeParams.status + '/' + encodeURIComponent($scope.fieldValue)); } }); orgsModule.controller('typesCtrl', function ($scope, $http, $routeParams) { + $scope.title = 'Organization types'; $scope.field = 'Organization type'; $scope.resultsBasePath = '/byType' $scope.entries = []; - $http.get('api/organizations/browse/types').then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.entries = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - + call_http_get($http, 'api/organizations/browse/types', function(res) { $scope.entries = res.data; }); }); orgsModule.controller('byTypeCtrl', function ($scope, $http, $routeParams, $location) { @@ -565,19 +432,14 @@ orgsModule.controller('byTypeCtrl', function ($scope, $http, $routeParams, $loca $scope.orgs = {}; - $http.get('api/organizations/byType/' + $routeParams.type + '/' + $routeParams.page + '/' + $routeParams.size).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.orgs = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); + call_http_get($http, 'api/organizations/byType/' + $routeParams.status + '/' + $routeParams.type + '/' + $routeParams.page + '/' + $routeParams.size, function(res) { $scope.orgs = res.data; }); $scope.prev = function() { - $location.url('/byType/' + ($scope.orgs.number - 1) + '/' + $scope.orgs.size + '/' + encodeURIComponent($scope.fieldValue)); + $location.url('/byType/' + ($scope.orgs.number - 1) + '/' + $scope.orgs.size + '/' + $routeParams.status + '/' + encodeURIComponent($scope.fieldValue)); } $scope.next = function() { - $location.url('/byType/' + ($scope.orgs.number + 1) + '/' + $scope.orgs.size + '/' + encodeURIComponent($scope.fieldValue)); + $location.url('/byType/' + ($scope.orgs.number + 1) + '/' + $scope.orgs.size + '/' + $routeParams.status + '/' + encodeURIComponent($scope.fieldValue)); } }); @@ -585,89 +447,81 @@ orgsModule.controller('byTypeCtrl', function ($scope, $http, $routeParams, $loca orgsModule.controller('showEditCtrl', function ($scope, $http, $routeParams, $route, $location, $timeout, $window, vocabulariesService) { $scope.orgId = $routeParams.id; $scope.org = {}; - $scope.events = {}; + $scope.duplicates = []; + $scope.conflicts = []; $scope.info = {}; + $scope.currentTab = 1 + $scope.vocabularies = {}; + $scope.adminMode = adminMode(); $scope.getInfo = function() { - $http.get('api/organizations/info?id=' + $scope.orgId).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.info = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); + call_http_get($http, 'api/organizations/info?id=' + $scope.orgId, function(res) { $scope.info = res.data; }); }; - $scope.getInfo(); + $scope.gotoTab = function(tab) { + $scope.org = {}; + call_http_get($http, 'api/organizations/get?id=' + $scope.orgId, function(res) { $scope.org = res.data; }); + + if (tab == 2) { + $scope.duplicates = []; + call_http_get($http, 'api/organizations/duplicates?id=' + $scope.orgId, function(res) { $scope.duplicates = res.data; }); + } else if (tab == 3) { + $scope.conflicts = []; + call_http_get($http, 'api/organizations/conflicts?id=' + $scope.orgId, function(res) { $scope.conflicts = res.data; }); + } + $scope.currentTab = tab; + } + + + + + $scope.saveDuplicates = function() { + + call_http_post($http, 'api/organizations/duplicates', $scope.duplicates, function(res) { + $scope.getInfo(); + alert('Events updated !!!'); + $scope.duplicates = res.data; + }); + } - $scope.vocabularies = {}; vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; }); - + $scope.getInfo(); + $scope.gotoTab(1); + if ($routeParams.msg == 1) { $scope.message = 'New organization registered !!!'; } else if ($routeParams.msg == 2) { $scope.message = 'Organization updated !!!'; } else if ($routeParams.msg == 3) { $scope.message = 'Pending organization registered !!!'; } else { $scope.message = ''; } $window.scrollTo(0, 0); - $timeout(function() { $scope.message = ''; }, 3000) - + $timeout(function() { $scope.message = ''; }, 3000); + }); -orgsModule.controller('showSuggestionsCtrl', function ($scope, $http, $routeParams, $location) { - $scope.info = {}; - $scope.pendingOrgs = []; - $scope.duplicates = []; - $scope.conflicts = []; - $scope.currentTab = $routeParams.tab; +orgsModule.controller('pendingOrgsCtrl', function ($scope, $http, $routeParams, $location, suggestionInfo) { + $scope.info = suggestionInfo.getInfo(); + $scope.orgs = []; $scope.country = $routeParams.country; $scope.getInfo = function() { - $http.get('api/organizations/suggestionsInfo').then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.info = res.data; - if ($scope.country == '_') { - var found = ''; - - angular.forEach($scope.info.byCountry, function(values, c) { - if (!found && (($scope.currentTab == 0 && values.nPendingOrgs > 0) || ($scope.currentTab == 1 && values.nDuplicates > 0) || ($scope.currentTab == 2 && values.nConflicts > 0))) { - found = c; - } - }); - if (found) { $location.url('/suggestions/' + found + '/' + $scope.currentTab); } - } - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); + suggestionInfo.updateInfo(function(info) { + if ($scope.country == '_') { + var found = ''; + angular.forEach(info.data.byCountry, function(values, c) { + if (!found && values.nPendingOrgs > 0) { + found = c; + } + }); + if (found) { $location.url('/pendings/' + found); } + } }); }; - $scope.refresh = function() { - $scope.pendingOrgs = []; - $scope.duplicates = []; - $scope.conflicts = []; + $scope.orgs = []; if ($scope.country != '_') { - if ($scope.currentTab == 0) { - $http.get('api/organizations/byCountry/pending/' + $scope.country).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.pendingOrgs = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - } else if ($scope.currentTab == 1) { - $http.get('api/organizations/duplicates/byCountry/' + $scope.country).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.duplicates = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - } else if ($scope.currentTab == 2) { - $http.get('api/organizations/conflicts/byCountry/' + $scope.country).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.conflicts = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); - } else { } + call_http_get($http, 'api/organizations/byCountry/suggested/' + $scope.country, function(res) { $scope.orgs = res.data; }); } $scope.getInfo(); } @@ -675,6 +529,135 @@ orgsModule.controller('showSuggestionsCtrl', function ($scope, $http, $routePara $scope.refresh(); }); +orgsModule.controller('duplicatesCtrl', function ($scope, $http, $routeParams, $location, $timeout, $route, suggestionInfo) { + $scope.info = suggestionInfo.getInfo(); + $scope.duplicates = []; + $scope.country = $routeParams.country; + $scope.currentOrg = {}; + $scope.currentOrgDetails = {}; + + $scope.prepareDuplicatesModal = function(org) { + $scope.currentOrg = org; + $scope.currentOrgDetails = {}; + $scope.currentDuplicates = []; + + call_http_get($http, 'api/organizations/get?id=' + org.id, function(res) { $scope.currentOrgDetails = res.data; }); + call_http_get($http, 'api/organizations/duplicates?id=' + org.id, function(res) { $scope.currentDuplicates = res.data; }); + }; + + $scope.getInfo = function() { + suggestionInfo.updateInfo(function(info) { + if ($scope.country == '_') { + var found = ''; + angular.forEach(info.data.byCountry, function(values, c) { + if (!found && values.nDuplicates > 0) { + found = c; + } + }); + if (found) { $location.url('/duplicates/' + found); } + } + }); + }; + + $scope.saveCurrentDuplicates = function() { + + call_http_post($http, 'api/organizations/duplicates', $scope.currentDuplicates, function(res) { + + $scope.getInfo(); + + $scope.currentOrg.numberOfDuplicates = 0; + for (var i=0; i 1) { + $route.reload(); + } else { + $location.url('/duplicates/_'); + } + }, 600); + + }); + }; + + $scope.refresh = function() { + $scope.duplicates = []; + + if ($scope.country != '_') { + call_http_get($http, 'api/organizations/duplicates/byCountry/' + $scope.country, function(res) { $scope.duplicates = res.data; }); + } + $scope.getInfo(); + } + + $scope.refresh(); +}); + +orgsModule.controller('conflictsCtrl', function ($scope, $http, $routeParams, $location, $route, $q, suggestionInfo) { + $scope.info = suggestionInfo.getInfo(); + $scope.conflicts = []; + $scope.country = $routeParams.country; + $scope.orgs = []; + $scope.newConflict = {}; + $scope.currentGroup = []; + + $scope.addConflict = function() { + $scope.currentGroup.push({ + 'id' : $scope.newConflict.id, + 'name' : $scope.newConflict.name, + 'type' : $scope.newConflict.type, + 'city' : $scope.newConflict.city, + 'country': $scope.newConflict.country + }); + } + + $scope.prepareAddConflictModal = function(list) { + $scope.currentGroup = list; + }; + + $scope.prepareConflictsModal = function(list) { + $scope.orgs = []; + $scope.selectedOrgs = []; + + var gets = list.map((o) => $http.get('api/organizations/get?id=' + o.id)); + + $q.all(gets).then(function(responses) { + $scope.orgs = responses.map((resp) => resp.data); + angular.forEach($scope.orgs, function(org) { org.show = 'secondary'; }); + }); + } + + $scope.getInfo = function() { + suggestionInfo.updateInfo(function(info) { + if ($scope.country == '_') { + var found = ''; + + angular.forEach(info.data.byCountry, function(values, c) { + if (!found && values.nConflicts > 0) { + found = c; + } + }); + if (found) { $location.url('/conflicts/' + found); } + } + }); + }; + + $scope.refresh = function() { + $scope.conflicts = []; + + if ($scope.country != '_') { + call_http_get($http, 'api/organizations/conflicts/byCountry/' + $scope.country, function(res) { $scope.conflicts = res.data; }); + } + $scope.getInfo(); + } + + $scope.refresh(); +}); + + orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route, vocabulariesService) { $scope.users = []; @@ -685,12 +668,7 @@ orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route, vo $scope.vocabularies = {}; vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; }); - $http.get('api/users').then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.users = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); + call_http_get($http, 'api/users', function(res) { $scope.users = res.data; }); $scope.setCurrentUser = function(user) { angular.copy(user, $scope.currentUser); @@ -700,23 +678,12 @@ orgsModule.controller('usersCtrl', function ($scope, $http, $timeout, $route, vo } $scope.saveUser = function(user) { - $http.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8"; - $http.post('api/users', user).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.users = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); + call_http_post($http, 'api/users', user, function(res) { $scope.users = res.data; }); } $scope.deleteUser = function(email) { if (confirm("Are you sure ?")) { - $http.delete('api/users?email=' + email).then(function successCallback(res) { - if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } - $scope.users = res.data; - }, function errorCallback(res) { - alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); - }); + call_http_delete($http, 'api/users?email=' + email, function(res) { $scope.users = res.data; }); } } }); diff --git a/apps/dnet-orgs-database-application/src/main/resources/static/resources/js/spin.js b/apps/dnet-orgs-database-application/src/main/resources/static/resources/js/spin.js new file mode 100644 index 00000000..0127c85d --- /dev/null +++ b/apps/dnet-orgs-database-application/src/main/resources/static/resources/js/spin.js @@ -0,0 +1,348 @@ +/** + * Copyright (c) 2011-2014 Felix Gnass + * Licensed under the MIT license + */ +(function(root, factory) { + + /* CommonJS */ + if (typeof exports == 'object') module.exports = factory() + + /* AMD module */ + else if (typeof define == 'function' && define.amd) define(factory) + + /* Browser global */ + else root.Spinner = factory() +} +(this, function() { + "use strict"; + + var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */ + , animations = {} /* Animation rules keyed by their name */ + , useCssAnimations /* Whether to use CSS animations or setTimeout */ + + /** + * Utility function to create elements. If no tag name is given, + * a DIV is created. Optionally properties can be passed. + */ + function createEl(tag, prop) { + var el = document.createElement(tag || 'div') + , n + + for(n in prop) el[n] = prop[n] + return el + } + + /** + * Appends children and returns the parent. + */ + function ins(parent /* child1, child2, ...*/) { + for (var i=1, n=arguments.length; i>1) + 'px' + }) + } + + for (; i < o.lines; i++) { + seg = css(createEl(), { + position: 'absolute', + top: 1+~(o.width/2) + 'px', + transform: o.hwaccel ? 'translate3d(0,0,0)' : '', + opacity: o.opacity, + animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite' + }) + + if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'})) + ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)'))) + } + return el + }, + + /** + * Internal method that adjusts the opacity of a single line. + * Will be overwritten in VML fallback mode below. + */ + opacity: function(el, i, val) { + if (i < el.childNodes.length) el.childNodes[i].style.opacity = val + } + + }) + + + function initVML() { + + /* Utility function to create a VML tag */ + function vml(tag, attr) { + return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr) + } + + // No CSS transforms but VML support, add a CSS rule for VML elements: + sheet.addRule('.spin-vml', 'behavior:url(#default#VML)') + + Spinner.prototype.lines = function(el, o) { + var r = o.length+o.width + , s = 2*r + + function grp() { + return css( + vml('group', { + coordsize: s + ' ' + s, + coordorigin: -r + ' ' + -r + }), + { width: s, height: s } + ) + } + + var margin = -(o.width+o.length)*2 + 'px' + , g = css(grp(), {position: 'absolute', top: margin, left: margin}) + , i + + function seg(i, dx, filter) { + ins(g, + ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}), + ins(css(vml('roundrect', {arcsize: o.corners}), { + width: r, + height: o.width, + left: o.radius, + top: -o.width>>1, + filter: filter + }), + vml('fill', {color: getColor(o.color, i), opacity: o.opacity}), + vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change + ) + ) + ) + } + + if (o.shadow) + for (i = 1; i <= o.lines; i++) + seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)') + + for (i = 1; i <= o.lines; i++) seg(i) + return ins(el, g) + } + + Spinner.prototype.opacity = function(el, i, val, o) { + var c = el.firstChild + o = o.shadow && o.lines || 0 + if (c && i+o < c.childNodes.length) { + c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild + if (c) c.opacity = val + } + } + } + + var probe = css(createEl('group'), {behavior: 'url(#default#VML)'}) + + if (!vendor(probe, 'transform') && probe.adj) initVML() + else useCssAnimations = vendor(probe, 'animation') + + return Spinner + +})); diff --git a/apps/dnet-orgs-database-application/src/main/resources/templates/home.html b/apps/dnet-orgs-database-application/src/main/resources/templates/home.html index f7a6a0de..af174a31 100644 --- a/apps/dnet-orgs-database-application/src/main/resources/templates/home.html +++ b/apps/dnet-orgs-database-application/src/main/resources/templates/home.html @@ -15,15 +15,27 @@ @@ -33,7 +45,12 @@ fieldset > legend { -