diff --git a/waltz-data/src/main/java/org/finos/waltz/data/report_grid/ReportGridDao.java b/waltz-data/src/main/java/org/finos/waltz/data/report_grid/ReportGridDao.java index 979e567b00..0404c2a520 100644 --- a/waltz-data/src/main/java/org/finos/waltz/data/report_grid/ReportGridDao.java +++ b/waltz-data/src/main/java/org/finos/waltz/data/report_grid/ReportGridDao.java @@ -20,7 +20,6 @@ import org.finos.waltz.common.DateTimeUtilities; -import org.finos.waltz.common.SetUtilities; import org.finos.waltz.data.GenericSelector; import org.finos.waltz.data.InlineSelectFieldFactory; import org.finos.waltz.model.EntityKind; @@ -39,6 +38,7 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Repository; +import java.sql.Timestamp; import java.util.Comparator; import java.util.*; import java.util.function.Function; @@ -50,11 +50,11 @@ import static java.util.Collections.emptySet; import static java.util.function.Function.identity; import static java.util.stream.Collectors.*; +import static org.finos.waltz.common.DateTimeUtilities.toLocalDate; import static org.finos.waltz.common.DateTimeUtilities.toLocalDateTime; import static org.finos.waltz.common.ListUtilities.newArrayList; import static org.finos.waltz.common.MapUtilities.groupBy; -import static org.finos.waltz.common.SetUtilities.map; -import static org.finos.waltz.common.SetUtilities.union; +import static org.finos.waltz.common.SetUtilities.*; import static org.finos.waltz.common.StringUtilities.join; import static org.finos.waltz.model.EntityReference.mkRef; import static org.finos.waltz.model.survey.SurveyInstanceStatus.APPROVED; @@ -83,10 +83,14 @@ public class ReportGridDao { private final org.finos.waltz.schema.tables.InvolvementKind ik = INVOLVEMENT_KIND.as("ik"); private final org.finos.waltz.schema.tables.Person p = Tables.PERSON.as("p"); private final org.finos.waltz.schema.tables.SurveyQuestion sq = SURVEY_QUESTION.as("sq"); + private final org.finos.waltz.schema.tables.ApplicationGroup ag = APPLICATION_GROUP.as("ag"); + private final org.finos.waltz.schema.tables.ApplicationGroupEntry age = APPLICATION_GROUP_ENTRY.as("age"); + private final org.finos.waltz.schema.tables.ApplicationGroupOuEntry agoe = APPLICATION_GROUP_OU_ENTRY.as("agoe"); private final org.finos.waltz.schema.tables.EntityFieldReference efr = ENTITY_FIELD_REFERENCE.as("efr"); private final org.finos.waltz.schema.tables.SurveyTemplate st = SURVEY_TEMPLATE.as("st"); private final org.finos.waltz.schema.tables.Application a = APPLICATION.as("a"); private final org.finos.waltz.schema.tables.ChangeInitiative ci = CHANGE_INITIATIVE.as("ci"); + private final org.finos.waltz.schema.tables.EntityRelationship er = ENTITY_RELATIONSHIP.as("er"); private static final Field ENTITY_NAME_FIELD = InlineSelectFieldFactory.mkNameField( SURVEY_QUESTION_RESPONSE.ENTITY_RESPONSE_ID, @@ -258,6 +262,14 @@ private List getColumnDefinitions(Condition conditio sq.HELP_TEXT, condition); + SelectConditionStep> appGroupColumns = mkColumnDefinitionQuery( + EntityKind.APP_GROUP, + ag, + ag.ID, + ag.NAME, + ag.DESCRIPTION, + condition); + SelectConditionStep> surveyMetaColumns = mkColumnDefinitionQuery( EntityKind.SURVEY_TEMPLATE, st, @@ -287,6 +299,7 @@ private List getColumnDefinitions(Condition conditio .unionAll(costKindColumns) .unionAll(involvementKindColumns) .unionAll(surveyQuestionColumns) + .unionAll(appGroupColumns) .unionAll(surveyMetaColumns) .unionAll(applicationMetaColumns) .unionAll(changeInitiativeMetaColumns) @@ -405,6 +418,10 @@ private Set findCellDataByGridCondition(Condition gridCondition, colsByKind.getOrDefault(EntityKind.SURVEY_QUESTION, emptySet()), cd -> cd.columnEntityId()); + Set requiredAppGroupIds = map( + colsByKind.getOrDefault(EntityKind.APP_GROUP, emptySet()), + cd -> cd.columnEntityId()); + // COMPLEX GRID DEFS @@ -443,6 +460,7 @@ private Set findCellDataByGridCondition(Condition gridCondition, fetchCostData(genericSelector, requiredCostKinds), fetchInvolvementData(genericSelector, requiredInvolvementKinds), fetchSurveyQuestionResponseData(genericSelector, requiredSurveyQuestionIds), + fetchAppGroupData(genericSelector, requiredAppGroupIds), fetchSurveyFieldReferenceData(genericSelector, requiredSurveyTemplateIds), fetchApplicationFieldReferenceData(genericSelector, requiredApplicationColumns), fetchChangeInitiativeFieldReferenceData(genericSelector, requiredChangeInitiativeColumns)); @@ -450,6 +468,110 @@ private Set findCellDataByGridCondition(Condition gridCondition, } + private Set fetchAppGroupData(GenericSelector genericSelector, + Set requiredAppGroupIds) { + if (requiredAppGroupIds.size() == 0) { + return emptySet(); + } else { + + SelectOrderByStep> appGroupInfoSelect = determineAppGroupQuery(genericSelector, requiredAppGroupIds); + + return dsl + .fetch(appGroupInfoSelect) + .stream() + .map(r -> { + Long subjectId = r.get("subject_id", Long.class); + Timestamp created_at = r.get("created_at", Timestamp.class); + + return ImmutableReportGridCell + .builder() + .subjectId(subjectId) + .columnEntityId(r.get(ag.ID)) + .columnEntityKind(EntityKind.APP_GROUP) + .text("Y") + .comment(format("Created at: %s", toLocalDate(created_at).toString())) + .entityFieldReferenceId(null) + .build(); + }) + .collect(toSet()); + } + } + + + private SelectOrderByStep> determineAppGroupQuery(GenericSelector selector, Set requiredAppGroupIds) { + + switch (selector.kind()) { + case APPLICATION: + return mkApplicationAppGroupSelect(selector, requiredAppGroupIds); + case CHANGE_INITIATIVE: + return mkChangeInitiativeAppGroupSelect(selector, requiredAppGroupIds); + default: + throw new UnsupportedOperationException("Cannot return app group selector for kind: " + selector.kind().name()); + } + + } + + + private SelectOrderByStep> mkChangeInitiativeAppGroupSelect(GenericSelector selector, Set requiredAppGroupIds) { + + SelectConditionStep> groupASelect = dsl + .select(ci.ID.as("subject_id"), + ag.ID, + er.LAST_UPDATED_AT.as("created_at")) + .from(ag) + .innerJoin(er).on(ag.ID.eq(er.ID_A)) + .innerJoin(ci).on(er.ID_B.eq(ci.ID)) + .where(er.KIND_A.eq(EntityKind.APP_GROUP.name()) + .and(er.KIND_B.eq(EntityKind.CHANGE_INITIATIVE.name()))) + .and(ci.ID.in(selector.selector())) + .and(ag.ID.in(requiredAppGroupIds)); + + SelectConditionStep> groupBSelect = dsl + .select(ci.ID.as("subject_id"), + ag.ID, + er.LAST_UPDATED_AT.as("created_at")) + .from(ag) + .innerJoin(er).on(ag.ID.eq(er.ID_B)) + .innerJoin(ci).on(er.ID_A.eq(ci.ID)) + .where(er.KIND_B.eq(EntityKind.APP_GROUP.name()) + .and(er.KIND_A.eq(EntityKind.CHANGE_INITIATIVE.name()))) + .and(ci.ID.in(selector.selector())) + .and(ag.ID.in(requiredAppGroupIds)); + + + return groupASelect.union(groupBSelect); + } + + + private SelectOrderByStep> mkApplicationAppGroupSelect(GenericSelector selector, Set requiredAppGroupIds) { + + SelectConditionStep> directSelect = DSL + .select( + age.APPLICATION_ID.as("subject_id"), + ag.ID, + age.CREATED_AT.as("created_at")) + .from(ag) + .innerJoin(age).on(ag.ID.eq(age.GROUP_ID)) + .where(age.APPLICATION_ID.in(selector.selector())) + .and(ag.ID.in(requiredAppGroupIds)); + + SelectConditionStep> indirectSelect = DSL + .select( + a.ID.as("subject_id"), + ag.ID, + agoe.CREATED_AT.as("created_at")) + .from(ag) + .innerJoin(agoe).on(ag.ID.eq(agoe.GROUP_ID)) + .innerJoin(eh).on(agoe.ORG_UNIT_ID.eq(eh.ANCESTOR_ID) + .and(eh.KIND.eq(EntityKind.ORG_UNIT.name()))) + .innerJoin(a).on(eh.ID.eq(a.ORGANISATIONAL_UNIT_ID)) + .where(a.ID.in(selector.selector())) + .and(ag.ID.in(requiredAppGroupIds)); + + return directSelect.union(indirectSelect); + } + + public Set fetchApplicationFieldReferenceData(GenericSelector selector, Set> requiredApplicationColumns) { @@ -625,7 +747,7 @@ private Set fetchInvolvementData(GenericSelector selector, if (requiredInvolvementKinds.size() == 0) { return emptySet(); } else { - return SetUtilities.fromCollection(dsl + return fromCollection(dsl .select( inv.ENTITY_ID, inv.KIND_ID, diff --git a/waltz-ng/client/report-grid/components/grid-view-section/report-grid-view-section.html b/waltz-ng/client/report-grid/components/grid-view-section/report-grid-view-section.html index f7d25e2274..170006fd12 100644 --- a/waltz-ng/client/report-grid/components/grid-view-section/report-grid-view-section.html +++ b/waltz-ng/client/report-grid/components/grid-view-section/report-grid-view-section.html @@ -2,9 +2,15 @@ .wgrc-involvement-cell { background-color: #e0ffe1; } + .wgrc-survey-question-cell { background-color: #fff59d; } + + .wgrc-app-group-cell { + background-color: #d1dbff; + } + .wgrc-no-data-cell { background-color: #f7f9f9; } diff --git a/waltz-ng/client/report-grid/components/svelte/column-definition-edit-panel/ColumnDefinitionEditPanel.svelte b/waltz-ng/client/report-grid/components/svelte/column-definition-edit-panel/ColumnDefinitionEditPanel.svelte index 5b6ffd471e..740a019197 100644 --- a/waltz-ng/client/report-grid/components/svelte/column-definition-edit-panel/ColumnDefinitionEditPanel.svelte +++ b/waltz-ng/client/report-grid/components/svelte/column-definition-edit-panel/ColumnDefinitionEditPanel.svelte @@ -27,6 +27,7 @@ let canBeAdded; function onSelect(d) { + const column = { columnEntityId: d.columnEntityId, columnEntityKind: d.columnEntityKind, @@ -39,6 +40,10 @@ position: 0 }; + if (_.some($columnDefs, c => sameColumnRef(column, c))) { + return; + } + const newList = _.concat( $columnDefs, column); diff --git a/waltz-ng/client/report-grid/components/svelte/column-definition-edit-panel/EntitySelector.svelte b/waltz-ng/client/report-grid/components/svelte/column-definition-edit-panel/EntitySelector.svelte index 6e70828451..6935890597 100644 --- a/waltz-ng/client/report-grid/components/svelte/column-definition-edit-panel/EntitySelector.svelte +++ b/waltz-ng/client/report-grid/components/svelte/column-definition-edit-panel/EntitySelector.svelte @@ -19,6 +19,7 @@ entity.SURVEY_QUESTION, entity.ASSESSMENT_DEFINITION, entity.MEASURABLE, + entity.APP_GROUP, entity.SURVEY_INSTANCE, entity.APPLICATION, entity.CHANGE_INITIATIVE diff --git a/waltz-ng/client/report-grid/components/svelte/pickers/AppGroupPicker.svelte b/waltz-ng/client/report-grid/components/svelte/pickers/AppGroupPicker.svelte new file mode 100644 index 0000000000..da8f937426 --- /dev/null +++ b/waltz-ng/client/report-grid/components/svelte/pickers/AppGroupPicker.svelte @@ -0,0 +1,56 @@ + + +
+ + Select an app group using the search below. +
+
+ + \ No newline at end of file diff --git a/waltz-ng/client/report-grid/components/svelte/pickers/EntityPicker.svelte b/waltz-ng/client/report-grid/components/svelte/pickers/EntityPicker.svelte index 80eb1cf6ab..2a5bd87ff2 100644 --- a/waltz-ng/client/report-grid/components/svelte/pickers/EntityPicker.svelte +++ b/waltz-ng/client/report-grid/components/svelte/pickers/EntityPicker.svelte @@ -8,6 +8,7 @@ import SurveyInstanceFieldPicker from "./SurveyInstanceFieldPicker.svelte"; import ApplicationFieldPicker from "./ApplicationFieldPicker.svelte"; import ChangeInitiativeFieldPicker from "./ChangeInitiativeFieldPicker.svelte"; + import AppGroupPicker from "./AppGroupPicker.svelte"; export let onSelect = (d) => console.log("Selecting an entity", d); export let onDeselect = (d) => console.log("Deselecting an entity", d); @@ -32,6 +33,8 @@ return ApplicationFieldPicker; case "CHANGE_INITIATIVE": return ChangeInitiativeFieldPicker; + case "APP_GROUP": + return AppGroupPicker; default: throw "Cannot find picker for kind: " + entityKind; } diff --git a/waltz-ng/client/report-grid/components/svelte/report-grid-utils.js b/waltz-ng/client/report-grid/components/svelte/report-grid-utils.js index 31c9933c0e..bd90a30c8a 100644 --- a/waltz-ng/client/report-grid/components/svelte/report-grid-utils.js +++ b/waltz-ng/client/report-grid/components/svelte/report-grid-utils.js @@ -172,6 +172,7 @@ export function prepareColumnDefs(gridData) { case "SURVEY_TEMPLATE": case "APPLICATION": case "CHANGE_INITIATIVE": + case "APP_GROUP": case "SURVEY_QUESTION": return { allowSummary: false, @@ -187,6 +188,7 @@ export function prepareColumnDefs(gridData) { popover-placement="left" ng-class="{'wgrc-involvement-cell': COL_FIELD.text && ${c.columnEntityKind === "INVOLVEMENT_KIND"}, 'wgrc-survey-question-cell': COL_FIELD.text && ${c.columnEntityKind === "SURVEY_QUESTION"}, + 'wgrc-app-group-cell': COL_FIELD.text && ${c.columnEntityKind === "APP_GROUP"}, 'wgrc-no-data-cell': !COL_FIELD.text}" ng-style="{ 'border-bottom-right-radius': COL_FIELD.comment ? '15% 50%' : 0, @@ -294,6 +296,7 @@ export function prepareTableData(gridData) { case "SURVEY_TEMPLATE": case "APPLICATION": case "CHANGE_INITIATIVE": + case "APP_GROUP": case "SURVEY_QUESTION": return { text: x.text, diff --git a/waltz-web/src/main/java/org/finos/waltz/web/endpoints/extracts/ReportGridExtractor.java b/waltz-web/src/main/java/org/finos/waltz/web/endpoints/extracts/ReportGridExtractor.java index bf491a4a1d..cc0ed8f4c4 100644 --- a/waltz-web/src/main/java/org/finos/waltz/web/endpoints/extracts/ReportGridExtractor.java +++ b/waltz-web/src/main/java/org/finos/waltz/web/endpoints/extracts/ReportGridExtractor.java @@ -287,6 +287,7 @@ private Object getValueFromReportCell(Map ratingsById, case APPLICATION: case CHANGE_INITIATIVE: case SURVEY_QUESTION: + case APP_GROUP: return Optional.ofNullable(reportGridCell.text()).orElse("-"); case MEASURABLE: case ASSESSMENT_DEFINITION: