diff --git a/anet-dictionary.yml b/anet-dictionary.yml index 0328548f0a..abfcbf8748 100644 --- a/anet-dictionary.yml +++ b/anet-dictionary.yml @@ -130,6 +130,33 @@ fields: validations: - type: required params: [You must provide the top 3 issues] + - recurrence: once + relatedObjectType: report + questions: + question1: + type: special_field + widget: likertScale + label: Instant assessment Question 1 + helpText: Please provide assessment for something important + levels: + - color: red + endValue: 2 + label: test + - color: "#FFBF00" + endValue: 8 + label: mid + - color: green + endValue: 10 + label: high + aggregation: + widget: likertScale + question2: + type: number + label: Instant assessment Question 2 + question3: + type: number + label: Instant assessment Question 3 + customFieldRef1: label: Parent task placeholder: Start typing to search for a higher level task @@ -166,54 +193,6 @@ fields: responsiblePositions: label: Responsible positions placeholder: Search for a position... - customFields: - assessments: - type: array_of_objects - label: Assessments definition - helpText: Here you can add as many assessments as needed - addButtonLabel: Add an assessment - objectLabel: Assessment - objectFields: - recurrence: - type: enum - label: Recurrence - helpText: Select a recurrence for this periodic assessment - choices: - once: - label: once - daily: - label: daily - weekly: - label: weekly - biweekly: - label: biweekly - semimonthly: - label: semimonthly - monthly: - label: monthly - quarterly: - label: quarterly - semiannualy: - label: semiannually - annually: - label: annually - relatedObjectType: - type: enum - label: Related object type - helpText: object type context in which the assessment will be made - choices: - report: - label: Report - null: - label: None - questions: - type: json - label: Questions - helpText: JSON that defines the assessment (you need to know what you are doing) - placeholder: Fill in valid JSON - asA: textarea - style: - height: 200px report: canUnpublishReports: true diff --git a/client/src/components/Model.js b/client/src/components/Model.js index 6421169c57..0e03a12f5c 100644 --- a/client/src/components/Model.js +++ b/client/src/components/Model.js @@ -600,22 +600,13 @@ export default class Model { } generalAssessmentsConfig() { - // assessments configuration defined for more than one instance - return [] - } - - instanceAssessmentsConfig() { - // assessments configuration defined for one specific instance + // default assessments configuration return [] } getAssessmentsConfig() { - const general = this.generalAssessmentsConfig() - const instance = this.instanceAssessmentsConfig() return Object.assign( - Model.parseAssessmentsConfig(general), - // instance config can override - Model.parseAssessmentsConfig(instance) + Model.parseAssessmentsConfig(this.generalAssessmentsConfig()) ) } diff --git a/client/src/models/Task.js b/client/src/models/Task.js index 2feffab244..a591c9ed96 100644 --- a/client/src/models/Task.js +++ b/client/src/models/Task.js @@ -182,11 +182,6 @@ export default class Task extends Model { return this.fieldSettings().assessments || [] } - instanceAssessmentsConfig() { - // The given task instance might have a specific assessments config - return utils.parseJsonSafe(this.customFields).assessments || [] - } - static FILTERED_CLIENT_SIDE_FIELDS = ["assessment_customFieldEnum1"] static filterClientSideFields(obj, ...additionalFields) { diff --git a/client/tests/webdriver/baseSpecs/showReport.spec.js b/client/tests/webdriver/baseSpecs/showReport.spec.js index 125e241849..b6a47ba5d1 100644 --- a/client/tests/webdriver/baseSpecs/showReport.spec.js +++ b/client/tests/webdriver/baseSpecs/showReport.spec.js @@ -11,17 +11,17 @@ describe("Show report page", () => { it("We should see a table of tasks instant assessments related to the current report", () => { ShowReport.tasksEngagementAssessments.waitForDisplayed() // Both 1.2.A as 1.2.B tasks on the page have an svg type of assessment (LikertScale widgets) + // and two other questions const svgAssessments = ShowReport.tasksEngagementAssessments.$$("svg") expect(svgAssessments).to.have.length(2) - // Check on assessments of task 1.2.A const question2Assessments = ShowReport.tasksEngagementAssessments.$$( "[name*=question2]" ) - expect(question2Assessments).to.have.length(1) + expect(question2Assessments).to.have.length(2) const question3Assessments = ShowReport.tasksEngagementAssessments.$$( "[name*=question3]" ) - expect(question3Assessments).to.have.length(1) + expect(question3Assessments).to.have.length(2) }) }) }) diff --git a/client/tests/webdriver/customFieldsSpecs/customFields.spec.js b/client/tests/webdriver/customFieldsSpecs/customFields.spec.js index d9f79d5ff3..1835b61a19 100644 --- a/client/tests/webdriver/customFieldsSpecs/customFields.spec.js +++ b/client/tests/webdriver/customFieldsSpecs/customFields.spec.js @@ -1,7 +1,5 @@ import { expect } from "chai" -import { v4 as uuidv4 } from "uuid" import CreatePerson from "../pages/createNewPerson.page" -import CreateTask from "../pages/createNewTask.page" import CreateReport from "../pages/createReport.page" const INVALID_NUMBER_INPUT = "-10" @@ -14,10 +12,6 @@ const REQUIRED_PERSON_FIELDS = { gender: "MALE" } -const REQUIRED_TASK_FIELDS = { - shortName: "customTask" -} - describe("When working with custom fields for different anet objects", () => { // ------------------------------ REPORT CUSTOM FIELDS ----------------------------------------- describe("For report's custom fields", () => { @@ -262,40 +256,4 @@ describe("When working with custom fields for different anet objects", () => { CreatePerson.waitForAlertSuccessToLoad() }) }) - // ------------------------------ TASK CUSTOM FIELDS ----------------------------------------- - describe("For task's custom fields", () => { - it("Should be able load a new task form and fill normal required fields", () => { - CreateTask.openAsAdmin() - CreateTask.form.waitForExist() - CreateTask.form.waitForDisplayed() - // Fill other required fields so that we can test custom field validation - CreateTask.shortName.setValue( - `${REQUIRED_TASK_FIELDS.shortName} ${uuidv4()}` - ) - }) - - it("Should be able to see assessment fields", () => { - CreateTask.addAssessmentButton.click() - CreateTask.assessmentFields.forEach(field => { - field.waitForExist() - }) - }) - - it("Should warn invalid json for questions", () => { - CreateTask.questionsField.setValue("invalidJsonTest") - // normally there is already a help block, we need the other warning text - CreateTask.questionsFieldWarningText.waitForExist() - }) - - it("Should not warn valid json for questions", () => { - CreateTask.questionsField.setValue("{}") - // only the warning help text should be removed with valid json - CreateTask.questionsFieldWarningText.waitForExist({ reverse: true }) - }) - - it("Should be able to submit valid task", () => { - CreateTask.submitForm() - CreateTask.waitForAlertSuccessToLoad() - }) - }) }) diff --git a/client/tests/webdriver/customFieldsSpecs/myOrg.spec.js b/client/tests/webdriver/customFieldsSpecs/myOrg.spec.js index eaffbba94d..37dada97b6 100644 --- a/client/tests/webdriver/customFieldsSpecs/myOrg.spec.js +++ b/client/tests/webdriver/customFieldsSpecs/myOrg.spec.js @@ -34,9 +34,9 @@ describe("My Organization page", () => { expect(engagementStatusLegend).to.have.length(4) MyOrg.tasks.waitForDisplayed() - // There are 4 tasks on the x-axis + // There are 3 tasks on the x-axis const tasks = MyOrg.tasks.$("svg g").$$(".bars-group") - expect(tasks).to.have.length(4) + expect(tasks).to.have.length(3) let countTasksBars = 0 tasks.forEach(bar => { if (+bar.$("rect").getAttribute("height") > 0) { diff --git a/client/tests/webdriver/customFieldsSpecs/showTask.spec.js b/client/tests/webdriver/customFieldsSpecs/showTask.spec.js index f479cbcc9d..d065b9c2d4 100644 --- a/client/tests/webdriver/customFieldsSpecs/showTask.spec.js +++ b/client/tests/webdriver/customFieldsSpecs/showTask.spec.js @@ -13,18 +13,18 @@ describe("Show task page", () => { describe("When on the show page of a task with assessments", () => { it("We should see a table of assessments related to the current task", () => { ShowTask.assessmentResultsMonthly.waitForDisplayed() - const frenchFlagAssessmentMonthly = ShowTask.assessmentResultsMonthly.$( - "[id*=frenchFlag-assessment]" + const question1AssessmentMonthly = ShowTask.assessmentResultsMonthly.$( + "[id*=question1-assessment]" ) // eslint-disable-next-line no-unused-expressions - expect(frenchFlagAssessmentMonthly.isExisting()).to.be.true + expect(question1AssessmentMonthly.isExisting()).to.be.true ShowTask.assessmentResultsWeekly.waitForDisplayed() - const frenchFlagAssessmentWeekly = ShowTask.assessmentResultsWeekly.$( - "[id*=levels-assessment]" + const question1AssessmentWeekly = ShowTask.assessmentResultsWeekly.$( + "[id*=question1-assessment]" ) // eslint-disable-next-line no-unused-expressions - expect(frenchFlagAssessmentWeekly.isExisting()).to.be.true + expect(question1AssessmentWeekly.isExisting()).to.be.true }) }) }) diff --git a/insertBaseData-mssql.sql b/insertBaseData-mssql.sql index 7a26d7f42b..b71683790c 100644 --- a/insertBaseData-mssql.sql +++ b/insertBaseData-mssql.sql @@ -425,16 +425,10 @@ INSERT INTO tasks (uuid, shortName, longName, category, createdAt, updatedAt, cu (N'1b5eb36b-456c-46b7-ae9e-1c89e9075292', '1.1.B', 'Milestone the Second in EF 1.1', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'fdf107e7-a88a-4dc4-b744-748e9aaffabc'), (N'7fdef880-1bf3-4e56-8476-79166324023f', '1.1.C', 'Milestone the Third in EF 1.1', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'fdf107e7-a88a-4dc4-b744-748e9aaffabc'), (N'fe6b6b2f-d2a1-4ce1-9aa7-05361812a4d0', 'EF 1.2', 'Budgeting in the MoI', 'Sub-EF', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'1145e584-4485-4ce0-89c4-2fa2e1fe846a'), - (N'ac466253-1456-4fc8-9b14-a3643746e5a6', 'EF 1.3', 'Budgeting in the Police?', 'Sub-EF', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'1145e584-4485-4ce0-89c4-2fa2e1fe846a'); - -INSERT INTO tasks (uuid, shortName, longName, category, createdAt, updatedAt, customFieldRef1Uuid, customFields) - VALUES - (N'953e0b0b-25e6-44b6-bc77-ef98251d046a', '1.2.A', 'Milestone the First in EF 1.2', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'fe6b6b2f-d2a1-4ce1-9aa7-05361812a4d0', '{ "assessments":[{"questions":{ "question1": { "type": "special_field", "widget": "likertScale", "label": "Test Question 1", "helpText": "Please provide assessment for something important", "levels": [ { "color": "red", "endValue": 2, "label": "test" }, { "color": "#FFBF00", "endValue": 8, "label": "mid" }, { "color": "green", "endValue": 10, "label": "high" } ], "aggregation": { "widget": "likertScale" } }, "question2": { "type": "number", "label": "Test Question 2", "aggregation": { "widget": "numberAggregation" } }, "question3": { "type": "number", "label": "Test Question 3", "aggregation": { "widget": "numberAggregation" } } },"relatedObjectType":"report"}] }'), - (N'9d3da7f4-8266-47af-b518-995f587250c9', '1.2.B', 'Milestone the Second in EF 1.2', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'fe6b6b2f-d2a1-4ce1-9aa7-05361812a4d0', '{ "assessments":[{"questions":{ "frenchFlag": { "type": "special_field", "widget": "likertScale", "label": "French Flag assessment", "helpText": "Please tell us which is the best color in the French flag", "levels": [ { "color": "blue", "endValue": 3.3, "label": "blue" }, { "color": "white", "endValue": 6.6, "label": "white" }, { "color": "red", "endValue": 10, "label": "red" } ] }, "levels": { "type": "enumset", "label": "Achieved levels", "choices": { "lvl1": { "label": "Level 1" }, "lvl2": { "label": "Level 2" }, "lvl3": { "label": "Level 3" } } }, "description": { "type": "special_field", "label": "Detail levels", "widget": "richTextEditor" } },"relatedObjectType":"report"}] }'), - (N'6bbb1be9-4655-48d7-83f2-bc474781544a', '1.2.C', 'Milestone the Third in EF 1.2', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'fe6b6b2f-d2a1-4ce1-9aa7-05361812a4d0', '{ "assessments":[{"questions":{ "question1": { "type": "special_field", "widget": "likertScale", "label": "Monthly assessment Question 1", "helpText": "Please provide assessment for something important", "levels": [ { "color": "red", "endValue": 2, "label": "test" }, { "color": "#FFBF00", "endValue": 8, "label": "mid" }, { "color": "green", "endValue": 10, "label": "high" } ], "aggregation": { "widget": "likertScale" } }, "question2": { "type": "number", "label": "Monthly assessment Question 2", "aggregation": { "widget": "numberAggregation" } }, "question3": { "type": "number", "label": "Monthly assessment Question 3", "aggregation": { "widget": "numberAggregation" } } },"recurrence":"quarterly"},{"questions":{ "question1": { "type": "special_field", "widget": "likertScale", "label": "Weekly assessment Question 1", "helpText": "Please provide assessment for something important", "levels": [ { "color": "red", "endValue": 2, "label": "test" }, { "color": "#FFBF00", "endValue": 8, "label": "mid" }, { "color": "green", "endValue": 10, "label": "high" } ], "aggregation": { "widget": "likertScale" } }, "question2": { "type": "number", "label": "Weekly assessment Question 2", "aggregation": { "widget": "numberAggregation" } }, "question3": { "type": "number", "label": "Weekly assessment Question 3", "aggregation": { "widget": "numberAggregation" } } },"recurrence":"daily"},{"questions":{ "question1": { "type": "special_field", "widget": "likertScale", "label": "Instant assessment Question 1", "helpText": "Please provide assessment for something important", "levels": [ { "color": "red", "endValue": 2, "label": "test" }, { "color": "#FFBF00", "endValue": 8, "label": "mid" }, { "color": "green", "endValue": 10, "label": "high" } ], "aggregation": { "widget": "likertScale" } }, "question2": { "type": "number", "label": "Instant assessment Question 2", "aggregation": { "widget": "numberAggregation" } }, "question3": { "type": "number", "label": "Instant assessment Question 3", "aggregation": { "widget": "numberAggregation" } } },"relatedObjectType":"report"}] }'); - -INSERT INTO tasks (uuid, shortName, longName, category, createdAt, updatedAt, customFieldRef1Uuid) - VALUES + (N'ac466253-1456-4fc8-9b14-a3643746e5a6', 'EF 1.3', 'Budgeting in the Police?', 'Sub-EF', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'1145e584-4485-4ce0-89c4-2fa2e1fe846a'), + (N'953e0b0b-25e6-44b6-bc77-ef98251d046a', '1.2.A', 'Milestone the First in EF 1.2', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'fe6b6b2f-d2a1-4ce1-9aa7-05361812a4d0'), + (N'9d3da7f4-8266-47af-b518-995f587250c9', '1.2.B', 'Milestone the Second in EF 1.2', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'fe6b6b2f-d2a1-4ce1-9aa7-05361812a4d0'), + (N'6bbb1be9-4655-48d7-83f2-bc474781544a', '1.2.C', 'Milestone the Third in EF 1.2', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'fe6b6b2f-d2a1-4ce1-9aa7-05361812a4d0'), (N'076793eb-9950-4ea6-bbd5-2d8b8827828c', '1.3.A', 'Getting a budget in place', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'ac466253-1456-4fc8-9b14-a3643746e5a6'), (N'30bc5708-c12d-4a21-916c-5acd7f6f11da', '1.3.B', 'Tracking your expenses', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'ac466253-1456-4fc8-9b14-a3643746e5a6'), (N'df920c99-10ea-44e8-940f-cb1d1cbd22da', '1.3.C', 'Knowing when you run out of money', 'Milestone', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, N'ac466253-1456-4fc8-9b14-a3643746e5a6'), @@ -848,11 +842,9 @@ INSERT INTO reportPeople (personUuid, reportUuid, isPrimary, isAuthor) INSERT INTO reportPeople (personUuid, reportUuid, isPrimary) VALUES ((SELECT uuid FROM people where emailAddress='hunter+shardul@example.com'), @reportUuid, 1); INSERT INTO reportTasks (taskUuid, reportUuid) - VALUES ((SELECT uuid from tasks where shortName = '1.1.B'), @reportUuid); -INSERT INTO reportTasks (taskUuid, reportUuid) - VALUES ((SELECT uuid from tasks where shortName = '1.2.A'), @reportUuid); + VALUES ((SELECT uuid from tasks where shortName = '1.2.A'), @reportUuid); INSERT INTO reportTasks (taskUuid, reportUuid) - VALUES ((SELECT uuid from tasks where shortName = '1.2.B'), @reportUuid); + VALUES ((SELECT uuid from tasks where shortName = '1.2.B'), @reportUuid); SET @reportUuid = lower(newid()); INSERT INTO reports (uuid, createdAt, updatedAt, locationUuid, intent, text, nextSteps, keyOutcomes, state, engagementDate, atmosphere, advisorOrganizationUuid, principalOrganizationUuid) @@ -864,11 +856,9 @@ INSERT INTO reportPeople (personUuid, reportUuid, isPrimary, isAuthor) INSERT INTO reportPeople (personUuid, reportUuid, isPrimary) VALUES ((SELECT uuid FROM people where emailAddress='hunter+shardul@example.com'), @reportUuid, 1); INSERT INTO reportTasks (taskUuid, reportUuid) - VALUES ((SELECT uuid from tasks where shortName = '1.1.B'), @reportUuid); -INSERT INTO reportTasks (taskUuid, reportUuid) - VALUES ((SELECT uuid from tasks where shortName = '1.2.A'), @reportUuid); + VALUES ((SELECT uuid from tasks where shortName = '1.2.A'), @reportUuid); INSERT INTO reportTasks (taskUuid, reportUuid) - VALUES ((SELECT uuid from tasks where shortName = '1.2.B'), @reportUuid); + VALUES ((SELECT uuid from tasks where shortName = '1.2.B'), @reportUuid); -- Release all of the reports right now, so they show up in the rollup. UPDATE reports SET releasedAt = reports.createdAt WHERE state = 2 OR state = 4; @@ -1096,7 +1086,7 @@ INSERT INTO noteRelatedObjects (noteUuid, relatedObjectType, relatedObjectUuid) FROM reports r WHERE r.text LIKE 'Today%'; --- Add measurement assessments to tasks related to reports +-- Add instant assessments to tasks related to reports SET @noteUuid = lower(newid()); INSERT INTO notes (uuid, authorUuid, type, text, createdAt, updatedAt) VALUES (@noteUuid, @authorUuid, 3, '{"__recurrence":"once","__relatedObjectType":"report","question1":4.462819020045945,"question2":"1","question3":"22"}', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); @@ -1111,7 +1101,7 @@ INSERT INTO noteRelatedObjects (noteUuid, relatedObjectType, relatedObjectUuid) SET @noteUuid = lower(newid()); INSERT INTO notes (uuid, authorUuid, type, text, createdAt, updatedAt) - VALUES (@noteUuid, @authorUuid, 3, '{"__recurrence":"once","__relatedObjectType":"report","description":"

level 1

easily achieved

level 3

easily achieved

","frenchFlag":7.670554793177809,"levels":["lvl1","lvl3"]}', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); + VALUES (@noteUuid, @authorUuid, 3, '{"__recurrence":"once","__relatedObjectType":"report","question1":3.141592653589793,"question2":"3","question3":"14"}', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); INSERT INTO noteRelatedObjects (noteUuid, relatedObjectType, relatedObjectUuid) SELECT @noteUuid, 'reports', r.uuid FROM reports r diff --git a/src/main/java/mil/dds/anet/utils/PendingAssessmentsHelper.java b/src/main/java/mil/dds/anet/utils/PendingAssessmentsHelper.java index 9db883ea93..11ed8fdccb 100644 --- a/src/main/java/mil/dds/anet/utils/PendingAssessmentsHelper.java +++ b/src/main/java/mil/dds/anet/utils/PendingAssessmentsHelper.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.ImmutableList; import java.lang.invoke.MethodHandles; import java.time.DayOfWeek; @@ -225,9 +224,7 @@ public Set getTasksToAssess() { public static final String PRINCIPAL_PERSON_ASSESSMENTS = "fields.principal.person.assessments"; public static final String TASK_SUB_LEVEL_ASSESSMENTS = "fields.task.subLevel.assessments"; public static final String TASK_TOP_LEVEL_ASSESSMENTS = "fields.task.topLevel.assessments"; - // JSON fields in task.customFields we use - public static final String TASK_ASSESSMENTS = "assessments"; - public static final String TASK_RECURRENCE = "recurrence"; + public static final String ASSESSMENT_RECURRENCE = "recurrence"; // JSON fields in note.text we use public static final String NOTE_RECURRENCE = "__recurrence"; public static final String NOTE_PERIOD_START = "__periodStart"; @@ -252,9 +249,9 @@ public CompletableFuture> loadAll( } // Look up periodic assessment definitions for people in the dictionary - final Set globalPositionAssessmentRecurrence = - getGlobalAssessmentRecurrence(recurrenceSet, PRINCIPAL_PERSON_ASSESSMENTS); - logger.trace("globalPositionAssessmentRecurrence={}", globalPositionAssessmentRecurrence); + final Set positionAssessmentRecurrence = + getAssessmentRecurrence(recurrenceSet, PRINCIPAL_PERSON_ASSESSMENTS); + logger.trace("positionAssessmentRecurrence={}", positionAssessmentRecurrence); // Look up periodic assessment definitions for all tasks final Map> taskAssessmentRecurrence = new HashMap<>(); @@ -264,7 +261,7 @@ public CompletableFuture> loadAll( // Prepare maps of positions and tasks linked to active advisor positions final Map objectsToAssessByPosition = new HashMap<>(); - return preparePositionAssessmentMap(context, globalPositionAssessmentRecurrence, + return preparePositionAssessmentMap(context, positionAssessmentRecurrence, objectsToAssessByPosition).thenCompose(allPositionsToAssess -> { logger.trace("the following positions need to be checked for missing assessments: {}", allPositionsToAssess); @@ -325,22 +322,22 @@ private boolean shouldAssess(final Instant now, final Instant lastRun, final Ins return date != null && (lastRun == null || date.isAfter(lastRun) && !date.isAfter(now)); } - private Set getGlobalAssessmentRecurrence(final Set recurrenceSet, + private Set getAssessmentRecurrence(final Set recurrenceSet, final String keyPath) { - final Set globalPersonAssessmentRecurrence = new HashSet<>(); + final Set assessmentRecurrence = new HashSet<>(); @SuppressWarnings("unchecked") - final List> personAssessmentDefinitions = + final List> assessmentDefinitions = (List>) config.getDictionaryEntry(keyPath); - if (personAssessmentDefinitions != null) { - personAssessmentDefinitions.stream().forEach(pad -> { + if (assessmentDefinitions != null) { + assessmentDefinitions.stream().forEach(pad -> { final Recurrence recurrence = - Recurrence.valueOfRecurrence((String) pad.get(TASK_RECURRENCE)); + Recurrence.valueOfRecurrence((String) pad.get(ASSESSMENT_RECURRENCE)); if (shouldAddRecurrence(recurrenceSet, recurrence)) { - globalPersonAssessmentRecurrence.add(recurrence); + assessmentRecurrence.add(recurrence); } }); } - return globalPersonAssessmentRecurrence; + return assessmentRecurrence; } private boolean shouldAddRecurrence(final Set recurrenceSet, @@ -351,60 +348,29 @@ private boolean shouldAddRecurrence(final Set recurrenceSet, private void addTaskDefinitions(final Set recurrenceSet, final Map> taskAssessmentRecurrence, boolean topLevel) { // Look up periodic assessment definitions for all tasks in the dictionary - final Set globalTaskAssessmentRecurrence = getGlobalAssessmentRecurrence( - recurrenceSet, topLevel ? TASK_TOP_LEVEL_ASSESSMENTS : TASK_SUB_LEVEL_ASSESSMENTS); - - // Look up periodic assessment definitions for each tasks in customFields - final List tasks = getActiveTasks(topLevel); - tasks.stream().forEach(t -> { - if (!globalTaskAssessmentRecurrence.isEmpty()) { - // Add all global recurrence definitions for this task - taskAssessmentRecurrence.computeIfAbsent(t, - task -> new HashSet<>(globalTaskAssessmentRecurrence)); - } - try { - final JsonNode taskCustomFields = Utils.parseJsonSafe(t.getCustomFields()); - if (taskCustomFields != null) { - final JsonNode taskAssessmentsDefinition = taskCustomFields.get(TASK_ASSESSMENTS); - if (taskAssessmentsDefinition != null && taskAssessmentsDefinition.isArray()) { - final ArrayNode arrayNode = (ArrayNode) taskAssessmentsDefinition; - for (int i = 0; i < arrayNode.size(); i++) { - final JsonNode recurrenceDefinition = arrayNode.get(i).get(TASK_RECURRENCE); - if (recurrenceDefinition != null) { - final Recurrence recurrence = - Recurrence.valueOfRecurrence(recurrenceDefinition.asText()); - if (shouldAddRecurrence(recurrenceSet, recurrence)) { - // Add task-specific recurrence definition - taskAssessmentRecurrence.compute(t, (task, currentValue) -> { - if (currentValue == null) { - return new HashSet<>(Collections.singleton(recurrence)); - } else { - currentValue.add(recurrence); - return currentValue; - } - }); - } - } - } - } - } - } catch (JsonProcessingException ignored) { - // Invalid JSON, log and skip it - logger.error("Task {} has invalid JSON in customFields: {}", t, t.getCustomFields()); - } - }); + final Set assessmentRecurrence = getAssessmentRecurrence(recurrenceSet, + topLevel ? TASK_TOP_LEVEL_ASSESSMENTS : TASK_SUB_LEVEL_ASSESSMENTS); + + if (!assessmentRecurrence.isEmpty()) { + // Look up periodic assessment definitions for each active task + final List tasks = getActiveTasks(topLevel); + tasks.stream().forEach(t -> { + // Add all recurrence definitions for this task + taskAssessmentRecurrence.computeIfAbsent(t, task -> new HashSet<>(assessmentRecurrence)); + }); + } } private CompletableFuture>> preparePositionAssessmentMap( - final Map context, final Set globalPositionAssessmentRecurrence, + final Map context, final Set positionAssessmentRecurrence, final Map objectsToAssessByPosition) { final Map> allPositionsToAssess = new HashMap<>(); final CompletableFuture[] allFutures = getActiveAdvisorPositions(true).stream() - .map(p -> getPositionsToAssess(context, p, globalPositionAssessmentRecurrence) + .map(p -> getPositionsToAssess(context, p, positionAssessmentRecurrence) .thenApply(positionsToAssess -> { if (!positionsToAssess.isEmpty()) { positionsToAssess.stream().forEach(pta -> allPositionsToAssess.put(pta, - new HashSet<>(globalPositionAssessmentRecurrence))); + new HashSet<>(positionAssessmentRecurrence))); objectsToAssessByPosition.put(p, new ObjectsToAssess(positionsToAssess, null)); } return null; @@ -473,8 +439,8 @@ private List getActiveTasks(boolean topLevel) { } private CompletableFuture> getPositionsToAssess(final Map context, - final Position position, final Set globalPersonAssessmentRecurrence) { - if (position == null || globalPersonAssessmentRecurrence.isEmpty()) { + final Position position, final Set personAssessmentRecurrence) { + if (position == null || personAssessmentRecurrence.isEmpty()) { return CompletableFuture.completedFuture(Collections.emptySet()); } else { return position.loadAssociatedPositions(context).thenApply(ap -> ap.stream() diff --git a/src/main/resources/anet-schema.yml b/src/main/resources/anet-schema.yml index 807d0f9775..3c39a89592 100644 --- a/src/main/resources/anet-schema.yml +++ b/src/main/resources/anet-schema.yml @@ -121,23 +121,10 @@ $defs: properties: aggregationType: type: string - enum: [countPerValue, numbersList, valuesList] + enum: [countPerDate, countPerValue, countReportsByTask, likertScaleAndPieAgg, numbersList, objectsList, richText, valuesList] widget: type: string - enum: [pie, iqrBoxPlot, likertScale, default] - dependentSchemas: - widget: - oneOf: - - properties: - widget: - enum: [pie, likertScale, default] - - properties: - widget: - enum: [iqrBoxPlot] - aggregationType: - type: string - enum: [sum, avg, min, max] - required: [aggregationType] + enum: [default, calendar, iqrBoxPlot, likertScale, likertScaleAndPie, pie, reportsByTask, reportsMap, richText] # TODO: The following properties should not be here as these are only dependency fields. # When we have proper handling of `unevaluatedProperties` this section can be removed. choices: diff --git a/testDictionaries/no-custom-fields.yml b/testDictionaries/no-custom-fields.yml index 3a205396e8..345e440827 100644 --- a/testDictionaries/no-custom-fields.yml +++ b/testDictionaries/no-custom-fields.yml @@ -6,8 +6,8 @@ engagementsIncludeTimeAndDuration: true dateFormats: email: - date: d MMMM yyyy - withTime: d MMMM yyyy @ HH:mm + date: d MMMM yyyy Z + withTime: d MMMM yyyy @ HH:mm Z excel: d MMMM yyyy forms: input: @@ -22,6 +22,9 @@ dateFormats: date: dddd, D MMMM YYYY withTime: dddd, D MMMM YYYY @ HH:mm +menuOptions: + menuLinksDropdownTitle: My Work + printOptions: sensitiveInformationText: Sensitive Information sensitiveInformationTooltipText: Releasability Information @@ -64,7 +67,7 @@ fields: issues: type: special_field label: Top 3 issues - placeholder: Enter the top 3 issues + placeholder: Enter the top 3 issues widget: richTextEditor style: height: 300px @@ -90,7 +93,7 @@ fields: issues: type: special_field label: Top 3 issues - placeholder: Enter the top 3 issues + placeholder: Enter the top 3 issues widget: richTextEditor style: height: 300px @@ -119,13 +122,40 @@ fields: issues: type: special_field label: Top 3 issues - placeholder: Enter the top 3 issues + placeholder: Enter the top 3 issues widget: richTextEditor style: height: 300px validations: - type: required params: [You must provide the top 3 issues] + - recurrence: once + relatedObjectType: report + questions: + question1: + type: special_field + widget: likertScale + label: Instant assessment Question 1 + helpText: Please provide assessment for something important + levels: + - color: red + endValue: 2 + label: test + - color: "#FFBF00" + endValue: 8 + label: mid + - color: green + endValue: 10 + label: high + aggregation: + widget: likertScale + question2: + type: number + label: Instant assessment Question 2 + question3: + type: number + label: Instant assessment Question 3 + customFieldRef1: label: Parent task placeholder: Start typing to search for a higher level task @@ -168,9 +198,9 @@ fields: person: firstName: First name lastName: Last name + domainUsername: Domain username position: Current Position prevPositions: Previous Positions - domainUsername: Domain username emailAddress: label: Email placeholder: Only the following email domain names are allowed. ( example.com, cmil.mil, mission.ita, nato.int, *.isaf.nato.int ) @@ -239,8 +269,8 @@ fields: description: the rank of OF-9 app6Modifier: K gender: Gender - biography: Biography endOfTourDate: End of tour + biography: Biography customSensitiveInformation: birthday: type: date @@ -283,7 +313,7 @@ fields: Norway, Poland, Portugal, Romania, Slovakia, Slovenia, Spain, Sweden, Turkey, Ukraine, United Kingdom, United States of America] # number of fields after Avatar in the left column for advisors # adjust this number if two columns are not balanced on the Person Page - numberOfFieldsInLeftColumn: 7 + numberOfFieldsInLeftColumn: 6 # select and order person fields (including custom fields) showPageOrderedFields: - position @@ -408,7 +438,7 @@ fields: label: high color: '#ff8279' question2: - test: $.subject.position.organization.[?(@property == "identificationCode" && @.match(/^P/i))] + test: $.subject.position.organization[?(@property === "identificationCode" && @.match(/^P/i))] type: enumset label: Attendee from P org choices: @@ -416,7 +446,7 @@ fields: label: P color: '#ff0000' question3: - test: $.subject.position.organization.[?(@property == "identificationCode" && @.match(/^Z/i))] + test: $.subject.position.organization[?(@property === "identificationCode" && @.match(/^Z/i))] type: enumset label: Attendee from Z org choices: @@ -424,7 +454,7 @@ fields: label: Z color: '#00ff00' question4: - test: $.relatedObject.[?(@property == "atmosphere" && @ == "POSITIVE")] + test: $.relatedObject[?(@property === "atmosphere" && @ === "POSITIVE")] type: enumset label: Report had POSITIVE atmosphere choices: @@ -434,7 +464,7 @@ fields: # number of fields after Avatar in the left column for principals # adjust this number if two columns are not balanced on the Person Page - numberOfFieldsInLeftColumn: 6 + numberOfFieldsInLeftColumn: 7 # select and order person fields (including custom fields) showPageOrderedFields: - position @@ -521,7 +551,7 @@ imagery: automaticallyInactivateUsers: emailRemindersDaysPrior: [15, 30, 45] ignoredDomainNames: [] - checkIntervalInSecs: 86400 # 60 * 60 * 24 + checkIntervalInSecs: 86400 # 60 * 60 * 24 dashboards: - label: dashboard0