From d185b291873dd11f065b8b86db6640b9e40448d5 Mon Sep 17 00:00:00 2001 From: chrisala Date: Mon, 5 Aug 2024 13:23:34 +1000 Subject: [PATCH 01/16] Progress commit for #994 --- .../au/org/ala/ecodata/AssociatedOrg.groovy | 20 ++++++ .../au/org/ala/ecodata/Organisation.groovy | 19 +++++ .../au/org/ala/ecodata/ProjectService.groovy | 4 +- .../5.0/setupAssociatedOrgsForProjects.js | 70 +++++++++++++++++++ scripts/utils/audit.js | 15 ++++ 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 scripts/releases/5.0/setupAssociatedOrgsForProjects.js create mode 100644 scripts/utils/audit.js diff --git a/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy b/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy index eebb4855f..8ff5fe629 100644 --- a/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy +++ b/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy @@ -10,11 +10,29 @@ import groovy.transform.ToString @JsonIgnoreProperties(['metaClass', 'errors', 'expandoMetaClass']) class AssociatedOrg { + /** Reference to the Organisation entity if ecodata has a record of the Organisation */ String organisationId + + /** + * The name of the organisation in the context of the relationship. e.g. it could be a name used + * in a contract with a project that is different from the current business name of the organisation + */ String name String logo String url + /** + * The date the association started. A null date indicates the relationship started at the same + * time as the related entity. e.g. the start of a Project + */ + Date fromDate + + /** + * The date the association e ended. A null date indicates the relationship ended at the same + * time as the related entity. e.g. the end of a Project + */ + Date toDate + /** A description of the association - e.g. Service Provider, Grantee, Sponsor */ String description @@ -27,6 +45,8 @@ class AssociatedOrg { logo nullable: true url nullable: true description nullable: true + fromDate nullable: true + toDate nullable: true } } diff --git a/grails-app/domain/au/org/ala/ecodata/Organisation.groovy b/grails-app/domain/au/org/ala/ecodata/Organisation.groovy index 266d37ac1..fbf534a57 100644 --- a/grails-app/domain/au/org/ala/ecodata/Organisation.groovy +++ b/grails-app/domain/au/org/ala/ecodata/Organisation.groovy @@ -22,6 +22,16 @@ class Organisation { String description String announcements String abn + String abnStatus // N/A, Active, Cancelled + String entityName + String entityType // From ABN register + String businessName + String tradingName + String state + Integer postcode + List externalIds // For financial system vendor codes/reference + List indigenousOrganisationRegistration + List associatedOrgs // e.g. parent organisation such as for NSW LLS group String status = 'active' @@ -42,6 +52,15 @@ class Organisation { announcements nullable: true description nullable: true collectoryInstitutionId nullable: true + abnStatus nullable: true + entityName nullable: true + entityType nullable: true + businessName nullable: true + tradingName nullable: true + state nullable: true + postcode nullable: true + indigenousOrganisationRegistration nullable: true + associatedOrgs nullable: true abn nullable: true hubId nullable: true, validator: { String hubId, Organisation organisation, Errors errors -> GormMongoUtil.validateWriteOnceProperty(organisation, 'organisationId', 'hubId', errors) diff --git a/grails-app/services/au/org/ala/ecodata/ProjectService.groovy b/grails-app/services/au/org/ala/ecodata/ProjectService.groovy index 32f627c6d..94f6667c3 100644 --- a/grails-app/services/au/org/ala/ecodata/ProjectService.groovy +++ b/grails-app/services/au/org/ala/ecodata/ProjectService.groovy @@ -285,7 +285,9 @@ class ProjectService { if (it.organisationId) { Organisation org = Organisation.findByOrganisationId(it.organisationId) if (org) { - it.name = org.name + if (!it.name) { // Is this going to cause BioCollect an issue? + it.name = org.name + } it.url = org.url it.logo = Document.findByOrganisationIdAndRoleAndStatus(it.organisationId, "logo", ACTIVE)?.thumbnailUrl } diff --git a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js new file mode 100644 index 000000000..bb3cd283b --- /dev/null +++ b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js @@ -0,0 +1,70 @@ +load('../../utils/audit.js'); +let adminUserId = 'system' +let meritHubId = db.hub.findOne({urlPath: "merit"}).hubId; +let projects = db.project.find({hubId:meritHubId, status:{$ne:'deleted'}}); +while (projects.hasNext()) { + let project = projects.next(); + + project.associatedOrgs = []; + + // generally orgs are called service providers in the context of procurements and grantees in the + // context of grants + let description = null; + + let program = db.program.findOne({programId:project.programId}); + if (!program) { + print("No program found for project "+project.projectId+" name:"+project.name); + } + else { + if (program.fundingType) { + switch (program.fundingType) { + case 'SPP': + description = 'Recipient'; + break; + case 'Grant': + description = 'Grantee'; + case 'Procurement': + description = 'Service provider'; + + } + } else if (program.config && program.config.organisationRelationship) { + description = program.config.organisationRelationship; + } + } + + if (!description) { + if (project.plannedStartDate.getTime() > ISODate('2018-07-01T00:00:00+10:00').getDate()) { + description = 'Service provider' + } + else { + description = 'Recipient' + } + } + let associatedOrg = {name:project.organisationName, organisationId:project.organisationId, description:description}; + + if (!associatedOrg.name) { + print("No organisation for project "+project.projectId+" name:"+project.name+" organisationId: "+project.organisationId); + + } + else { + project.associatedOrgs.push(associatedOrg); + + // For now leave these fields as is to not cause issues when switching branches + // and to allow this script to be run repeatedly + //project.organisationId = null; + //project.organisationName = null; + } + + if (project.orgIdSvcProvider) { + let associatedOrg = {name:project.serviceProviderName, organisationId:project.orgIdSvcProvider, description:'Service provider'}; + project.associatedOrgs.push(associatedOrg); + // For now leave these fields as is to not cause issues when switching branches + // and to allow this script to be run repeatedly + //project.orgIdSvcProvider = null; + //project.serviceProviderName = null; + } + + db.project.replaceOne({projectId:project.projectId}, project); + audit(project, project.projectId, 'org.ala.ecodata.Project', adminUserId); + +} \ No newline at end of file diff --git a/scripts/utils/audit.js b/scripts/utils/audit.js new file mode 100644 index 000000000..0f77d026f --- /dev/null +++ b/scripts/utils/audit.js @@ -0,0 +1,15 @@ +/** Inserts a document into the auditMessage collection */ +function audit(entity, entityId, type, userId, projectId, eventType) { + var auditMessage = { + date: ISODate(), + entity: entity, + eventType: eventType || 'Update', + entityType: type, + entityId: entityId, + userId: userId + }; + if (entity.projectId || projectId) { + auditMessage.projectId = (entity.projectId || projectId); + } + db.auditMessage.insertOne(auditMessage); +} \ No newline at end of file From df5ab746e0a927716bbf18b0770f4c1264a5bd06 Mon Sep 17 00:00:00 2001 From: chrisala Date: Wed, 21 Aug 2024 13:52:19 +1000 Subject: [PATCH 02/16] Search associated orgs for project organisationId search #994 --- grails-app/conf/data/mapping.json | 15 +++++----- .../au/org/ala/ecodata/AssociatedOrg.groovy | 4 +++ .../ala/ecodata/OrganisationService.groovy | 1 - .../au/org/ala/ecodata/ProjectService.groovy | 29 ++++++++++++++++--- .../5.0/setupAssociatedOrgsForProjects.js | 21 ++++++++++++++ 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/grails-app/conf/data/mapping.json b/grails-app/conf/data/mapping.json index 39e8c094a..26fcf577a 100644 --- a/grails-app/conf/data/mapping.json +++ b/grails-app/conf/data/mapping.json @@ -70,10 +70,6 @@ "organisationId": { "type" : "keyword" }, - "orgIdSvcProvider": { - "type" : "keyword", - "copy_to": ["organisationId"] - }, "organisationName": { "type" : "text", "copy_to": ["organisationFacet", "organisationSort"] @@ -81,6 +77,9 @@ "organisationFacet": { "type": "keyword" }, + "linkedOrganisationFacet": { + "type": "keyword" + }, "organisationSort": { "type": "keyword", "normalizer" : "case_insensitive_sort" @@ -92,10 +91,6 @@ "dateCreatedSort" : { "type" : "keyword", "normalizer" : "case_insensitive_sort" }, - "serviceProviderName": { - "type" : "text", - "copy_to": ["organisationName","organisationFacet", "organisationSort"] - }, "associatedOrgs": { "properties" : { "name" : { @@ -109,6 +104,10 @@ "organisationId": { "type": "keyword", "copy_to": ["organisationId"] + }, + "organisationName": { + "type": "keyword", + "copy_to": ["organisationName"] } } }, diff --git a/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy b/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy index 8ff5fe629..901d0017d 100644 --- a/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy +++ b/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy @@ -13,6 +13,8 @@ class AssociatedOrg { /** Reference to the Organisation entity if ecodata has a record of the Organisation */ String organisationId + /** The name of the organisation as referenced via the organisationId */ + String organisationName /** * The name of the organisation in the context of the relationship. e.g. it could be a name used * in a contract with a project that is different from the current business name of the organisation @@ -43,10 +45,12 @@ class AssociatedOrg { organisationId nullable: true name nullable: true logo nullable: true + url nullable: true description nullable: true fromDate nullable: true toDate nullable: true + organisationName nullable: true } } diff --git a/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy b/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy index f80825d9a..503c5b37e 100644 --- a/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy +++ b/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy @@ -159,7 +159,6 @@ class OrganisationService { if ('projects' in levelOfDetail) { mapOfProperties.projects = [] mapOfProperties.projects += projectService.search([organisationId: org.organisationId], ['flat']) - mapOfProperties.projects += projectService.search([orgIdSvcProvider: org.organisationId], ['flat']) } if ('documents' in levelOfDetail) { mapOfProperties.documents = documentService.findAllByOwner('organisationId', org.organisationId) diff --git a/grails-app/services/au/org/ala/ecodata/ProjectService.groovy b/grails-app/services/au/org/ala/ecodata/ProjectService.groovy index 94f6667c3..56573518c 100644 --- a/grails-app/services/au/org/ala/ecodata/ProjectService.groovy +++ b/grails-app/services/au/org/ala/ecodata/ProjectService.groovy @@ -690,15 +690,36 @@ class ProjectService { List search(Map searchCriteria, levelOfDetail = []) { def criteria = Project.createCriteria() + def projects = criteria.list { ne("status", DELETED) searchCriteria.each { prop, value -> + // Special case for organisationId - also included embedded associatedOrg relationships. + if (prop == 'organisationId') { + or { + if (value instanceof List) { + inList(prop, value) + } else { + eq(prop, value) + } - if (value instanceof List) { - inList(prop, value) - } else { - eq(prop, value) + associatedOrgs { + if (value instanceof List) { + inList(prop, value) + } else { + eq(prop, value) + } + } + } + } + else { + if (value instanceof List) { + inList(prop, value) + } else { + eq(prop, value) + } } + } } diff --git a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js index bb3cd283b..85cc58ac1 100644 --- a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js +++ b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js @@ -42,6 +42,17 @@ while (projects.hasNext()) { } let associatedOrg = {name:project.organisationName, organisationId:project.organisationId, description:description}; + if (project.organisationId) { + let organisation = db.organisation.findOne({organisationId:project.organisationId}); + if (!organisation) { + print("OrganisationId "+project.organisationId+" not found for project "+project.projectId+" name:"+project.name); + } + else { + associatedOrg.organisationName = organisation.name; + } + } + + if (!associatedOrg.name) { print("No organisation for project "+project.projectId+" name:"+project.name+" organisationId: "+project.organisationId); @@ -57,6 +68,16 @@ while (projects.hasNext()) { if (project.orgIdSvcProvider) { let associatedOrg = {name:project.serviceProviderName, organisationId:project.orgIdSvcProvider, description:'Service provider'}; + + let organisation = db.organisation.findOne({organisationId:project.orgIdSvcProvider}); + if (!organisation) { + print("OrganisationId "+project.orgIdSvcProvider+" not found for project "+project.projectId+" name:"+project.name); + } + else { + associatedOrg.organisationName = organisation.name; + } + + project.associatedOrgs.push(associatedOrg); // For now leave these fields as is to not cause issues when switching branches // and to allow this script to be run repeatedly From f51472f4892998216401b349c74fa508b46da50a Mon Sep 17 00:00:00 2001 From: chrisala Date: Wed, 21 Aug 2024 14:44:42 +1000 Subject: [PATCH 03/16] Updated tests #994 --- .../au/org/ala/ecodata/OrganisationControllerSpec.groovy | 4 ++-- .../org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy index d6ce0b57e..36d7b3064 100644 --- a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy +++ b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy @@ -119,14 +119,14 @@ class OrganisationControllerSpec extends IntegrationTestHelper { } - void "projects can be associated with an organisation by the serviceProviderOrganisationId property"() { + void "projects can be associated with an organisation by the associatedOrgs property"() { setup: // Create some data for the database. def organisation = TestDataHelper.buildOrganisation([name: 'org 1']) def projects = [] (1..2).each { - projects << TestDataHelper.buildProject([orgIdSvcProvider: organisation.organisationId, name:'svc project '+it]) + projects << TestDataHelper.buildProject([associatedOrgs: [[organisationId:organisation.organisationId, name:'org project '+it]]]) } projects << TestDataHelper.buildProject([organisationId: organisation.organisationId, name:'org project']) (1..3).each { diff --git a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy index 4e7688fcd..c087e7577 100644 --- a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy +++ b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy @@ -112,7 +112,7 @@ class OrganisationServiceIntegrationSpec extends IntegrationTestHelper { } - void "projects can be associated with an organisation by the serviceProviderOrganisationId property"() { + void "projects can be associated with an organisation by the associatedOrgs property"() { setup: def organisation @@ -122,7 +122,7 @@ class OrganisationServiceIntegrationSpec extends IntegrationTestHelper { organisation = TestDataHelper.buildOrganisation([name: 'Test Organisation2']) def projects = [] (1..2).each { - projects << TestDataHelper.buildProject([orgIdSvcProvider: organisation.organisationId]) + projects << TestDataHelper.buildProject([associatedOrgs: [[organisationId:organisation.organisationId, name:'org project '+it]]]) } projects << TestDataHelper.buildProject([organisationId: organisation.organisationId]) (1..3).each { From 7852d3370db59b41c386dacc3b0be8dca160607b Mon Sep 17 00:00:00 2001 From: chrisala Date: Mon, 26 Aug 2024 14:53:57 +1000 Subject: [PATCH 04/16] Added business/contract names #994 --- grails-app/domain/au/org/ala/ecodata/Organisation.groovy | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/grails-app/domain/au/org/ala/ecodata/Organisation.groovy b/grails-app/domain/au/org/ala/ecodata/Organisation.groovy index fbf534a57..d077cea0a 100644 --- a/grails-app/domain/au/org/ala/ecodata/Organisation.groovy +++ b/grails-app/domain/au/org/ala/ecodata/Organisation.groovy @@ -25,14 +25,13 @@ class Organisation { String abnStatus // N/A, Active, Cancelled String entityName String entityType // From ABN register - String businessName - String tradingName + List businessNames String state Integer postcode List externalIds // For financial system vendor codes/reference List indigenousOrganisationRegistration List associatedOrgs // e.g. parent organisation such as for NSW LLS group - + List contractNames // When contracts are written for projects with this organisation with a name that doesn't match the organisation name String status = 'active' String collectoryInstitutionId // Reference to the Collectory @@ -55,8 +54,8 @@ class Organisation { abnStatus nullable: true entityName nullable: true entityType nullable: true - businessName nullable: true - tradingName nullable: true + businessNames nullable: true + contractNames nullable: true state nullable: true postcode nullable: true indigenousOrganisationRegistration nullable: true From 71707f696a811c23693467eb15d3b15a48a01d0c Mon Sep 17 00:00:00 2001 From: chrisala Date: Wed, 28 Aug 2024 08:37:53 +1000 Subject: [PATCH 05/16] Changed OrganisationService to use data binding #994 --- .../au/org/ala/ecodata/AssociatedOrg.groovy | 3 -- .../au/org/ala/ecodata/Organisation.groovy | 16 ++++++++-- .../ala/ecodata/OrganisationService.groovy | 31 ++++++++++++------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy b/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy index 901d0017d..f052ad682 100644 --- a/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy +++ b/grails-app/domain/au/org/ala/ecodata/AssociatedOrg.groovy @@ -13,8 +13,6 @@ class AssociatedOrg { /** Reference to the Organisation entity if ecodata has a record of the Organisation */ String organisationId - /** The name of the organisation as referenced via the organisationId */ - String organisationName /** * The name of the organisation in the context of the relationship. e.g. it could be a name used * in a contract with a project that is different from the current business name of the organisation @@ -50,7 +48,6 @@ class AssociatedOrg { description nullable: true fromDate nullable: true toDate nullable: true - organisationName nullable: true } } diff --git a/grails-app/domain/au/org/ala/ecodata/Organisation.groovy b/grails-app/domain/au/org/ala/ecodata/Organisation.groovy index d077cea0a..635282c83 100644 --- a/grails-app/domain/au/org/ala/ecodata/Organisation.groovy +++ b/grails-app/domain/au/org/ala/ecodata/Organisation.groovy @@ -22,9 +22,12 @@ class Organisation { String description String announcements String abn + String url String abnStatus // N/A, Active, Cancelled String entityName - String entityType // From ABN register + String sourceSystem // MERIT or Collectory + String entityType // Type code from the ABN register + String orgType // Type name as selected in BioCollect/ Name from the ABN register List businessNames String state Integer postcode @@ -32,16 +35,21 @@ class Organisation { List indigenousOrganisationRegistration List associatedOrgs // e.g. parent organisation such as for NSW LLS group List contractNames // When contracts are written for projects with this organisation with a name that doesn't match the organisation name - String status = 'active' + String status = Status.ACTIVE + + /** Stores configuration information for how reports should be generated for this organisation (if applicable) */ + Map config String collectoryInstitutionId // Reference to the Collectory Date dateCreated Date lastUpdated + static embedded = ['externalIds', 'associatedOrgs'] static mapping = { organisationId index: true + name index:true version false } @@ -54,6 +62,7 @@ class Organisation { abnStatus nullable: true entityName nullable: true entityType nullable: true + orgType nullable: true businessNames nullable: true contractNames nullable: true state nullable: true @@ -61,6 +70,9 @@ class Organisation { indigenousOrganisationRegistration nullable: true associatedOrgs nullable: true abn nullable: true + url nullable: true + config nullable: true + sourceSystem nullable: true hubId nullable: true, validator: { String hubId, Organisation organisation, Errors errors -> GormMongoUtil.validateWriteOnceProperty(organisation, 'organisationId', 'hubId', errors) } diff --git a/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy b/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy index a8283b687..c4d1a3479 100644 --- a/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy +++ b/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy @@ -3,6 +3,7 @@ package au.org.ala.ecodata import com.mongodb.client.MongoCollection import com.mongodb.client.model.Filters import grails.validation.ValidationException +import grails.web.databinding.DataBinder import org.bson.conversions.Bson import static au.org.ala.ecodata.Status.DELETED @@ -10,11 +11,13 @@ import static au.org.ala.ecodata.Status.DELETED /** * Works with Organisations, mostly CRUD operations at this point. */ -class OrganisationService { +class OrganisationService implements DataBinder { /** Use to include related projects in the toMap method */ public static final String PROJECTS = 'projects' + private static final List EXCLUDE_FROM_BINDING = ['organisationId', 'collectoryInstitutionId', 'status', 'id'] + static transactional = 'mongo' static final FLAT = 'flat' @@ -40,7 +43,7 @@ class OrganisationService { } def list(levelOfDetail = []) { - return Organisation.findAllByStatusNotEqual('deleted').collect{toMap(it, levelOfDetail)} + return Organisation.findAllByStatusNotEqual(DELETED).collect{toMap(it, levelOfDetail)} } def create(Map props, boolean createInCollectory = true) { @@ -51,12 +54,8 @@ class OrganisationService { organisation.collectoryInstitutionId = createCollectoryInstitution(props) } try { - // name is a mandatory property and hence needs to be set before dynamic properties are used (as they trigger validations) + bindData(organisation, props, [exclude:EXCLUDE_FROM_BINDING]) organisation.save(failOnError: true, flush:true) - props.remove('id') - props.remove('organisationId') - props.remove('collectoryInstitutionId') - commonService.updateProperties(organisation, props) // Assign the creating user as an admin. permissionService.addUserAsRoleToOrganisation(userService.getCurrentUserDetails()?.userId, AccessLevel.admin, organisation.organisationId) @@ -97,17 +96,25 @@ class OrganisationService { if (organisation) { try { - String oldName = organisation.name - commonService.updateProperties(organisation, props) // if no collectory institution exists for this organisation, create one + // We shouldn't be doing this unless the org is attached to a project that exports data + // to the ALA. if (!organisation.collectoryInstitutionId || organisation.collectoryInstitutionId == 'null' || organisation.collectoryInstitutionId == '') { - props.collectoryInstitutionId = createCollectoryInstitution(props) + organisation.collectoryInstitutionId = createCollectoryInstitution(props) } +œ + String oldName = organisation.name + bindData(organisation, props, [exclude:EXCLUDE_FROM_BINDING]) - getCommonService().updateProperties(organisation, props) if (props.name && (oldName != props.name)) { projectService.updateOrganisationName(organisation.organisationId, props.name) } + props.contractNames?.each { + if (!it in organisation.contractNames) { + + } + } + organisation.save(failOnError:true) return [status:'ok'] } catch (Exception e) { Organisation.withSession { session -> session.clear() } @@ -136,7 +143,7 @@ class OrganisationService { if (destroy) { organisation.delete() } else { - organisation.status = 'deleted' + organisation.status = DELETED organisation.save(flush: true, failOnError: true) } return [status: 'ok'] From d2e7dbd3778b2e53e661ba88b8a2a304f9018a2e Mon Sep 17 00:00:00 2001 From: chrisala Date: Wed, 28 Aug 2024 08:57:28 +1000 Subject: [PATCH 06/16] Remove dynamic property check from test #994 --- .../ecodata/OrganisationServiceIntegrationSpec.groovy | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy index c087e7577..56fe95365 100644 --- a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy +++ b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy @@ -55,18 +55,17 @@ class OrganisationServiceIntegrationSpec extends IntegrationTestHelper { Organisation.withTransaction { savedOrganisation = organisationService.get(organisationId) } - //organisationController.response.reset() - // organisationController.get(organisationId) - - // def savedOrganisation = extractJson(organisationController.response.text) then: "ensure the properties are the same as the original" savedOrganisation.organisationId == organisationId savedOrganisation.name == org.name savedOrganisation.description == org.description - savedOrganisation.dynamicProperty == org.dynamicProperty savedOrganisation.collectoryInstitutionId == institutionId + and: "The OrganisationService no longer supports dynamic properties" + savedOrganisation.dynamicProperty == null + + and: "the user who created the organisation is an admin of the new organisation" def orgPermissions = UserPermission.findAllByEntityIdAndEntityType(savedOrganisation.organisationId, Organisation.class.name) orgPermissions.size() == 1 From 8e638cab037a78ea08e2c71cd86a21328f401f13 Mon Sep 17 00:00:00 2001 From: chrisala Date: Mon, 9 Sep 2024 15:12:04 +1000 Subject: [PATCH 07/16] Update org names when contract names changed #994 --- .../ala/ecodata/OrganisationService.groovy | 9 +++-- .../au/org/ala/ecodata/ProjectService.groovy | 26 +++++++++++--- .../5.0/setupAssociatedOrgsForProjects.js | 3 +- .../org/ala/ecodata/ProjectServiceSpec.groovy | 35 +++++++++++++++++++ 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy b/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy index c4d1a3479..053a03c14 100644 --- a/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy +++ b/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy @@ -104,15 +104,14 @@ class OrganisationService implements DataBinder { } œ String oldName = organisation.name + List contractNameChanges = props.remove('contractNameChanges') bindData(organisation, props, [exclude:EXCLUDE_FROM_BINDING]) if (props.name && (oldName != props.name)) { - projectService.updateOrganisationName(organisation.organisationId, props.name) + projectService.updateOrganisationName(organisation.organisationId, oldName, props.name) } - props.contractNames?.each { - if (!it in organisation.contractNames) { - - } + contractNameChanges?.each { Map change -> + projectService.updateOrganisationName(organisation.organisationId, change.oldName, change.newName) } organisation.save(failOnError:true) return [status:'ok'] diff --git a/grails-app/services/au/org/ala/ecodata/ProjectService.groovy b/grails-app/services/au/org/ala/ecodata/ProjectService.groovy index 433c94bb0..92483046e 100644 --- a/grails-app/services/au/org/ala/ecodata/ProjectService.groovy +++ b/grails-app/services/au/org/ala/ecodata/ProjectService.groovy @@ -743,10 +743,28 @@ class ProjectService { * @param orgId identifies the organsation that has changed name * @param orgName the new organisation name */ - void updateOrganisationName(orgId, orgName) { - Project.findAllByOrganisationIdAndStatusNotEqual(orgId, DELETED).each { project -> - project.organisationName = orgName - project.save() + void updateOrganisationName(String orgId, String oldName, String newName) { + Project.findAllByOrganisationIdAndOrganisationNameAndStatusNotEqual(orgId, oldName, DELETED).each { project -> + project.organisationName = newName + project.save(flush:true) + } + + List projects = Project.where { + status != DELETED + associatedOrgs { + organisationId == orgId + name == oldName + } + }.list() + + + projects?.each { Project project -> + project.associatedOrgs.each { org -> + if (org.organisationId == orgId && org.name == oldName) { + org.name = newName + } + } + project.save(flush:true) } } diff --git a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js index 85cc58ac1..e0268d539 100644 --- a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js +++ b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js @@ -22,7 +22,8 @@ while (projects.hasNext()) { description = 'Recipient'; break; case 'Grant': - description = 'Grantee'; + description = 'Recipient'; + break; case 'Procurement': description = 'Service provider'; diff --git a/src/test/groovy/au/org/ala/ecodata/ProjectServiceSpec.groovy b/src/test/groovy/au/org/ala/ecodata/ProjectServiceSpec.groovy index 7924276c7..5f134a0a6 100644 --- a/src/test/groovy/au/org/ala/ecodata/ProjectServiceSpec.groovy +++ b/src/test/groovy/au/org/ala/ecodata/ProjectServiceSpec.groovy @@ -1064,4 +1064,39 @@ class ProjectServiceSpec extends MongoSpec implements ServiceUnitTest Date: Mon, 16 Sep 2024 15:49:58 +1000 Subject: [PATCH 08/16] Disable org collectory sync by default #2382 --- .../services/au/org/ala/ecodata/OrganisationService.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy b/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy index 053a03c14..a6031f1b1 100644 --- a/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy +++ b/grails-app/services/au/org/ala/ecodata/OrganisationService.groovy @@ -46,7 +46,7 @@ class OrganisationService implements DataBinder { return Organisation.findAllByStatusNotEqual(DELETED).collect{toMap(it, levelOfDetail)} } - def create(Map props, boolean createInCollectory = true) { + def create(Map props, boolean createInCollectory = false) { def organisation = new Organisation(organisationId: Identifiers.getNew(true, ''), name:props.name) @@ -90,7 +90,7 @@ class OrganisationService implements DataBinder { return institutionId } - def update(String id, props) { + def update(String id, props, boolean createInCollectory = false) { def organisation = Organisation.findByOrganisationId(id) if (organisation) { @@ -99,7 +99,7 @@ class OrganisationService implements DataBinder { // if no collectory institution exists for this organisation, create one // We shouldn't be doing this unless the org is attached to a project that exports data // to the ALA. - if (!organisation.collectoryInstitutionId || organisation.collectoryInstitutionId == 'null' || organisation.collectoryInstitutionId == '') { + if (createInCollectory && (!organisation.collectoryInstitutionId || organisation.collectoryInstitutionId == 'null' || organisation.collectoryInstitutionId == '')) { organisation.collectoryInstitutionId = createCollectoryInstitution(props) } œ From a83a3f76b4d17a6af873113da1b5fd7fb41fffe5 Mon Sep 17 00:00:00 2001 From: chrisala Date: Tue, 17 Sep 2024 11:43:34 +1000 Subject: [PATCH 09/16] Ensure BioCollect associated orgs use org name to preserve behaviour #994 --- .../releases/5.0/updateAssociatedOrgNames.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 scripts/releases/5.0/updateAssociatedOrgNames.js diff --git a/scripts/releases/5.0/updateAssociatedOrgNames.js b/scripts/releases/5.0/updateAssociatedOrgNames.js new file mode 100644 index 000000000..43ed54988 --- /dev/null +++ b/scripts/releases/5.0/updateAssociatedOrgNames.js @@ -0,0 +1,30 @@ +load('../../utils/audit.js'); +let projects = db.project.find({status:{$ne:'deleted'}, associatedOrgs:{$exists:true}, isMERIT:false}); +while (projects.hasNext()) { + let changed = false; + + let project = projects.next(); + let associatedOrgs = project.associatedOrgs; + if (associatedOrgs) { + for (let i = 0; i < associatedOrgs.length; i++) { + if (associatedOrgs[i].organisationId) { + let org = db.organisation.findOne({organisationId: associatedOrgs[i].organisationId}); + if (org) { + if (org.name != associatedOrgs[i].name) { + print("Updating associated org for project " + project.projectId + " from " + associatedOrgs[i].name + " to " + org.name); + associatedOrgs[i].name = org.name; + changed = true; + } + } else { + print("No organisation found for associated org " + associatedOrgs[i].organisationId + " in project " + project.projectId); + } + + } + } + if (changed) { + db.project.replaceOne({projectId: project.projectId}, project); + audit(project, project.projectId, 'au.org.ala.ecodata.Project', 'system'); + } + + } +} \ No newline at end of file From e62a3565806d844e06366a5fe9ab5e9e973dd9a6 Mon Sep 17 00:00:00 2001 From: chrisala Date: Tue, 17 Sep 2024 13:59:15 +1000 Subject: [PATCH 10/16] Updated tests #994 --- .../ecodata/OrganisationServiceSpec.groovy | 60 +------------------ 1 file changed, 2 insertions(+), 58 deletions(-) diff --git a/src/test/groovy/au/org/ala/ecodata/OrganisationServiceSpec.groovy b/src/test/groovy/au/org/ala/ecodata/OrganisationServiceSpec.groovy index 95275f532..aeaabf062 100644 --- a/src/test/groovy/au/org/ala/ecodata/OrganisationServiceSpec.groovy +++ b/src/test/groovy/au/org/ala/ecodata/OrganisationServiceSpec.groovy @@ -42,7 +42,7 @@ class OrganisationServiceSpec extends Specification implements ServiceUnitTest> projects - projectService.search([orgIdSvcProvider: orgId]) >> [] - - - when: - def result - // print (orgId) - // Organisation.withNewTransaction { - result = service.get(orgId) - // print result - // } - // def result = service.toMap(org) - - then: - result.organisationId == orgId - result.name == org.name - result.description == org.description - result.projects == null - - when: - // Organisation.withNewTransaction { - // result = service.get(orgId, [OrganisationService.PROJECTS]) - // print result - // } - def result1 = service.toMap(org, [OrganisationService.PROJECTS]) - - then: - result1.organisationId == orgId - result1.name == org.name - result1.description == org.description - result1.dynamicProperty == org['dynamicProperty'] - result1.projects == projects - - } -*/ - - - - } From 3fc5fa9600c832ce405050108b76b30571280319 Mon Sep 17 00:00:00 2001 From: chrisala Date: Tue, 17 Sep 2024 14:22:52 +1000 Subject: [PATCH 11/16] Updated tests #994 --- .../groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy index 36d7b3064..c989089f1 100644 --- a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy +++ b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationControllerSpec.groovy @@ -75,8 +75,6 @@ class OrganisationControllerSpec extends IntegrationTestHelper { savedOrganisation.organisationId == organisationId savedOrganisation.name == org.name savedOrganisation.description == org.description - // savedOrganisation.dynamicProperty == org.dynamicProperty (dynamic properties not working in tests) - savedOrganisation.collectoryInstitutionId == institutionId and: "the user who created the organisation is an admin of the new organisation" def orgPermissions = UserPermission.findAllByEntityIdAndEntityType(savedOrganisation.organisationId, Organisation.class.name) From 9f470e4ecae49045fd0d676ccc5c8836dad68d18 Mon Sep 17 00:00:00 2001 From: chrisala Date: Tue, 17 Sep 2024 14:54:30 +1000 Subject: [PATCH 12/16] Updated tests #994 --- .../org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy index 56fe95365..14d565659 100644 --- a/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy +++ b/src/integration-test/groovy/au/org/ala/ecodata/OrganisationServiceIntegrationSpec.groovy @@ -41,7 +41,7 @@ class OrganisationServiceIntegrationSpec extends IntegrationTestHelper { // setupPost(organisationController.request, org) when: "creating an organisation" - def result = organisationService.create(org) + def result = organisationService.create(org, true) then: "ensure we get a response including an organisationId" // def resp = extractJson(organisationController.response.text) From 10310f9a05e6c08869ff68a752c16323b35b0ef9 Mon Sep 17 00:00:00 2001 From: chrisala Date: Fri, 4 Oct 2024 09:44:25 +1000 Subject: [PATCH 13/16] Use "Recipient" for default org relationship #994 --- .../5.0/setupAssociatedOrgsForProjects.js | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js index e0268d539..6dba6cf90 100644 --- a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js +++ b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js @@ -9,38 +9,7 @@ while (projects.hasNext()) { // generally orgs are called service providers in the context of procurements and grantees in the // context of grants - let description = null; - - let program = db.program.findOne({programId:project.programId}); - if (!program) { - print("No program found for project "+project.projectId+" name:"+project.name); - } - else { - if (program.fundingType) { - switch (program.fundingType) { - case 'SPP': - description = 'Recipient'; - break; - case 'Grant': - description = 'Recipient'; - break; - case 'Procurement': - description = 'Service provider'; - - } - } else if (program.config && program.config.organisationRelationship) { - description = program.config.organisationRelationship; - } - } - - if (!description) { - if (project.plannedStartDate.getTime() > ISODate('2018-07-01T00:00:00+10:00').getDate()) { - description = 'Service provider' - } - else { - description = 'Recipient' - } - } + let description = 'Recipient'; let associatedOrg = {name:project.organisationName, organisationId:project.organisationId, description:description}; if (project.organisationId) { From 5af34df5a35e786e1955f7f15857f695f90a8b01 Mon Sep 17 00:00:00 2001 From: chrisala Date: Fri, 4 Oct 2024 16:06:59 +1000 Subject: [PATCH 14/16] Moved script to merit #994 --- .../5.0/setupAssociatedOrgsForProjects.js | 61 ------------------- 1 file changed, 61 deletions(-) delete mode 100644 scripts/releases/5.0/setupAssociatedOrgsForProjects.js diff --git a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js b/scripts/releases/5.0/setupAssociatedOrgsForProjects.js deleted file mode 100644 index 6dba6cf90..000000000 --- a/scripts/releases/5.0/setupAssociatedOrgsForProjects.js +++ /dev/null @@ -1,61 +0,0 @@ -load('../../utils/audit.js'); -let adminUserId = 'system' -let meritHubId = db.hub.findOne({urlPath: "merit"}).hubId; -let projects = db.project.find({hubId:meritHubId, status:{$ne:'deleted'}}); -while (projects.hasNext()) { - let project = projects.next(); - - project.associatedOrgs = []; - - // generally orgs are called service providers in the context of procurements and grantees in the - // context of grants - let description = 'Recipient'; - let associatedOrg = {name:project.organisationName, organisationId:project.organisationId, description:description}; - - if (project.organisationId) { - let organisation = db.organisation.findOne({organisationId:project.organisationId}); - if (!organisation) { - print("OrganisationId "+project.organisationId+" not found for project "+project.projectId+" name:"+project.name); - } - else { - associatedOrg.organisationName = organisation.name; - } - } - - - if (!associatedOrg.name) { - print("No organisation for project "+project.projectId+" name:"+project.name+" organisationId: "+project.organisationId); - - } - else { - project.associatedOrgs.push(associatedOrg); - - // For now leave these fields as is to not cause issues when switching branches - // and to allow this script to be run repeatedly - //project.organisationId = null; - //project.organisationName = null; - } - - if (project.orgIdSvcProvider) { - let associatedOrg = {name:project.serviceProviderName, organisationId:project.orgIdSvcProvider, description:'Service provider'}; - - let organisation = db.organisation.findOne({organisationId:project.orgIdSvcProvider}); - if (!organisation) { - print("OrganisationId "+project.orgIdSvcProvider+" not found for project "+project.projectId+" name:"+project.name); - } - else { - associatedOrg.organisationName = organisation.name; - } - - - project.associatedOrgs.push(associatedOrg); - // For now leave these fields as is to not cause issues when switching branches - // and to allow this script to be run repeatedly - //project.orgIdSvcProvider = null; - //project.serviceProviderName = null; - } - - db.project.replaceOne({projectId:project.projectId}, project); - audit(project, project.projectId, 'org.ala.ecodata.Project', adminUserId); - -} \ No newline at end of file From 480a254c3183f8fb85e92c9c58c7c3b112cd1121 Mon Sep 17 00:00:00 2001 From: chrisala Date: Tue, 15 Oct 2024 14:35:18 +1100 Subject: [PATCH 15/16] Use associatedOrgs for project organisation data on export #994 --- .../reporting/ProjectXlsExporter.groovy | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/groovy/au/org/ala/ecodata/reporting/ProjectXlsExporter.groovy b/src/main/groovy/au/org/ala/ecodata/reporting/ProjectXlsExporter.groovy index 0c2b060b4..a702f2d13 100644 --- a/src/main/groovy/au/org/ala/ecodata/reporting/ProjectXlsExporter.groovy +++ b/src/main/groovy/au/org/ala/ecodata/reporting/ProjectXlsExporter.groovy @@ -1,6 +1,7 @@ package au.org.ala.ecodata.reporting import au.org.ala.ecodata.ActivityForm +import au.org.ala.ecodata.AssociatedOrg import au.org.ala.ecodata.DataDescription import au.org.ala.ecodata.ExternalId import au.org.ala.ecodata.ManagementUnit @@ -38,11 +39,11 @@ class ProjectXlsExporter extends ProjectExporter { List configurableIntersectionHeaders = getIntersectionHeaders() List configurableIntersectionProperties = getIntersectionProperties() - List commonProjectHeadersWithoutSites = ['Project ID', 'Grant ID', 'External ID', 'Internal order number', 'Work order id', 'Organisation', 'Service Provider', 'Management Unit', 'Name', 'Description', 'Program', 'Sub-program', 'Start Date', 'End Date', 'Contracted Start Date', 'Contracted End Date', 'Funding', 'Funding Type', 'Status', "Last Modified"] + configurableIntersectionHeaders - List commonProjectPropertiesRaw = ['grantId', 'externalId', 'internalOrderId', 'workOrderId', 'organisationName', 'serviceProviderName', 'managementUnitName', 'name', 'description', 'associatedProgram', 'associatedSubProgram', 'plannedStartDate', 'plannedEndDate', 'contractStartDate', 'contractEndDate', 'funding', 'fundingType', 'status', 'lastUpdated'] + configurableIntersectionProperties + List commonProjectHeadersWithoutSites = ['Project ID', 'Grant ID', 'External ID', 'Internal order number', 'Work order id', 'Recipient (Contract name)', 'Recipient (ID)', 'Management Unit', 'Name', 'Description', 'Program', 'Sub-program', 'Start Date', 'End Date', 'Contracted Start Date', 'Contracted End Date', 'Funding', 'Funding Type', 'Status', "Last Modified"] + configurableIntersectionHeaders + List commonProjectPropertiesRaw = ['grantId', 'externalId', 'internalOrderId', 'workOrderId', 'organisationName', 'organisationId', 'managementUnitName', 'name', 'description', 'associatedProgram', 'associatedSubProgram', 'plannedStartDate', 'plannedEndDate', 'contractStartDate', 'contractEndDate', 'funding', 'fundingType', 'status', 'lastUpdated'] + configurableIntersectionProperties - List projectHeadersWithTerminationReason = ['Project ID', 'Grant ID', 'External ID', 'Internal order number', 'Work order id', 'Organisation', 'Service Provider', 'Management Unit', 'Name', 'Description', 'Program', 'Sub-program', 'Start Date', 'End Date', 'Contracted Start Date', 'Contracted End Date', 'Funding', 'Funding Type', 'Status'] + configurableIntersectionHeaders + ['Termination Reason', 'Last Modified'] - List projectPropertiesTerminationReason = ['grantId', 'externalId', 'internalOrderId', 'workOrderId', 'organisationName', 'serviceProviderName', 'managementUnitName', 'name', 'description', 'associatedProgram', 'associatedSubProgram', 'plannedStartDate', 'plannedEndDate', 'contractStartDate', 'contractEndDate', 'funding', 'fundingType', 'status'] + configurableIntersectionProperties + List projectHeadersWithTerminationReason = ['Project ID', 'Grant ID', 'External ID', 'Internal order number', 'Work order id', 'Recipient (Contract name)', 'Recipient (ID)', 'Management Unit', 'Name', 'Description', 'Program', 'Sub-program', 'Start Date', 'End Date', 'Contracted Start Date', 'Contracted End Date', 'Funding', 'Funding Type', 'Status'] + configurableIntersectionHeaders + ['Termination Reason', 'Last Modified'] + List projectPropertiesTerminationReason = ['grantId', 'externalId', 'internalOrderId', 'workOrderId', 'organisationName', 'organisationId', 'managementUnitName', 'name', 'description', 'associatedProgram', 'associatedSubProgram', 'plannedStartDate', 'plannedEndDate', 'contractStartDate', 'contractEndDate', 'funding', 'fundingType', 'status'] + configurableIntersectionProperties List projectPropertiesWithTerminationReason = ['projectId'] + projectPropertiesTerminationReason.collect{PROJECT_DATA_PREFIX+it} + ["terminationReason", PROJECT_DATA_PREFIX+"lastUpdated"] @@ -55,8 +56,13 @@ class ProjectXlsExporter extends ProjectExporter { List commonProjectHeaders = commonProjectHeadersWithoutSites + stateHeaders + electorateHeaders + projectApprovalHeaders List commonProjectProperties = commonProjectPropertiesWithoutSites + stateProperties + electorateProperties + projectApprovalProperties - List projectHeaders = projectHeadersWithTerminationReason + projectStateHeaders - List projectProperties = projectPropertiesWithTerminationReason + projectStateProperties + List associatedOrgProjectHeaders = (1..3).collect{['Contract name '+it, 'Organisation ID '+it, 'Organisation relationship from date '+it, 'Organisation relationship to date '+it, 'Organisation relationship '+it]}.flatten() + List associatedOrgProperties = ['name', 'organisationId', 'fromDate', 'toDate', 'description'] + + List associatedOrgProjectProperties = (1..3).collect{['associatedOrg_name'+it, 'associatedOrg_organisationId'+it, 'associatedOrg_fromDate'+it, 'associatedOrg_toDate'+it, 'associatedOrg_description'+it]}.flatten() + + List projectHeaders = projectHeadersWithTerminationReason + associatedOrgProjectHeaders + projectStateHeaders + List projectProperties = projectPropertiesWithTerminationReason + associatedOrgProjectProperties + projectStateProperties List siteStateHeaders = (1..5).collect{'State '+it} @@ -164,6 +170,7 @@ class ProjectXlsExporter extends ProjectExporter { List rdpMonitoringIndicatorsHeaders =commonProjectHeaders + ['Code', 'Monitoring methodology', 'Project service / Target measure/s', 'Monitoring method', 'Evidence to be retained'] List rdpMonitoringIndicatorsProperties =commonProjectProperties + ['relatedBaseline', 'data1', 'relatedTargetMeasures','protocols', 'evidence'] + OutputModelProcessor processor = new OutputModelProcessor() ProjectService projectService @@ -320,6 +327,14 @@ class ProjectXlsExporter extends ProjectExporter { if (project.managementUnitId) { project[PROJECT_DATA_PREFIX+'managementUnitName'] = managementUnitNames[project.managementUnitId] } + + Date now = new Date() + List orgs = project.associatedOrgs?.findAll{(!it.fromDate || it.fromDate <= now) && (!it.toDate || it.toDate >= now)} + if (orgs) { + project.organisationName = orgs[0].name + project.organisationId = orgs[0].organisationId + } + filterExternalIds(project, PROJECT_DATA_PREFIX) } @@ -499,6 +514,13 @@ class ProjectXlsExporter extends ProjectExporter { project[electorate] = projectElectorates.contains(electorate) ? 'Y' : 'N' } + project.associatedOrgs?.eachWithIndex { org, i -> + Map orgProps = associatedOrgProperties.collectEntries{ + [('associatedOrg_'+it+(i+1)):org[it]] + } + project.putAll(orgProps) + } + projectSheet.add([project], properties, row + 1) } From a6c3ee78a0531e15be518c7747ba7479f1d41a75 Mon Sep 17 00:00:00 2001 From: chrisala Date: Wed, 16 Oct 2024 13:51:05 +1100 Subject: [PATCH 16/16] Addressing code review comments #994 --- grails-app/conf/data/mapping.json | 7 ------- grails-app/domain/au/org/ala/ecodata/Organisation.groovy | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/grails-app/conf/data/mapping.json b/grails-app/conf/data/mapping.json index 26fcf577a..83398842f 100644 --- a/grails-app/conf/data/mapping.json +++ b/grails-app/conf/data/mapping.json @@ -77,9 +77,6 @@ "organisationFacet": { "type": "keyword" }, - "linkedOrganisationFacet": { - "type": "keyword" - }, "organisationSort": { "type": "keyword", "normalizer" : "case_insensitive_sort" @@ -104,10 +101,6 @@ "organisationId": { "type": "keyword", "copy_to": ["organisationId"] - }, - "organisationName": { - "type": "keyword", - "copy_to": ["organisationName"] } } }, diff --git a/grails-app/domain/au/org/ala/ecodata/Organisation.groovy b/grails-app/domain/au/org/ala/ecodata/Organisation.groovy index 635282c83..6b41a63ee 100644 --- a/grails-app/domain/au/org/ala/ecodata/Organisation.groovy +++ b/grails-app/domain/au/org/ala/ecodata/Organisation.groovy @@ -73,6 +73,7 @@ class Organisation { url nullable: true config nullable: true sourceSystem nullable: true + externalIds nullable: true hubId nullable: true, validator: { String hubId, Organisation organisation, Errors errors -> GormMongoUtil.validateWriteOnceProperty(organisation, 'organisationId', 'hubId', errors) }