From 52220122945d97fb9d083f9126ee77365393053f Mon Sep 17 00:00:00 2001 From: Champari Oltion <51322092+oltionchampari@users.noreply.github.com> Date: Mon, 6 Mar 2023 12:39:03 +0100 Subject: [PATCH] Eslint and typescript errors (#56) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Upgrade python deps (#51) * Upgrade python deps * Switch back to #develop * Release 4.1.0 (#54) * prepare next dev version * Fix colors assignment in plots (#41) * Prepare github changes * Remove circleci * prepare next dev version * Use `Font Awesome 6 Free` in `font-family` (#39) * Use `Font Awesome 6 Free` in `font-family` Requires https://github.com/datavisyn/tdp_core/pull/732 * Update Lineup to 4.6.2 Co-authored-by: Klaus Eckelt * Update fontawesome * Merge visyn_scripts * prepare next dev version * Merged `d3_changes` into `develop` (#45) * use d3v7 and d3v3 imports remove depenencies to d3 add dependency to tdp_core * remove console log * Dev d3 merge fix (#47) * move RouterScrollToTop to coral_public * fix TS errors * move canvas-confetti to coral_public * update git dependencies Co-authored-by: Klaus Eckelt * Automatically select root cohort if onboarding was already done (#48) * Autoselect rootcohort if onboarding was done #579 * format code * Remove grid lines from visualizations (#49) remove gridlines #400 * Upgrade python deps (#51) * Upgrade python deps * Switch back to #develop * prepare release 4.1.0 --------- Co-authored-by: Klaus Eckelt Co-authored-by: anita-steiner <> Co-authored-by: Holger Stitz Co-authored-by: Patrick <42137747+PatrickAdelberger@users.noreply.github.com> Co-authored-by: Michael Pühringer <51900829+puehringer@users.noreply.github.com> * prepare next dev version * Migration to visyn_core (#55) * Migration to visyn_core * Linting * Upgrade deps --------- Co-authored-by: Michael Puehringer * Fix cohort * Fix circular dependencies * Further cleanup * Fix async in loop * Import * Add todos for async inside loop errors --------- Co-authored-by: Michael Pühringer <51900829+puehringer@users.noreply.github.com> Co-authored-by: Vanessa Stoiber <52395160+dvvanessastoiber@users.noreply.github.com> Co-authored-by: Klaus Eckelt Co-authored-by: Holger Stitz Co-authored-by: Patrick <42137747+PatrickAdelberger@users.noreply.github.com> Co-authored-by: dvvanessastoiber Co-authored-by: Michael Puehringer --- .eslintcache | 2 +- .gitignore | 3 + Makefile | 34 +- coral/__init__.py | 6 +- coral/db.py | 2 +- coral/migration/env.py | 4 +- coral/settings.py | 6 +- coral/sql.py | 2 +- coral/sql_query_mapper.py | 36 +- coral/tests/conftest.py | 34 + coral/tests/test_api.py | 5 + coral/tests/test_dummy.py | 2 - package.json | 2 +- requirements_dev.txt | 12 +- setup.py | 2 +- src/Cohort.ts | 26 +- src/CohortContext.ts | 15 + src/CohortRepresentations.ts | 41 +- src/OnboardingManager.ts | 5 +- src/Overview/CohortOverview.ts | 6 +- src/Provenance/CohortEV.ts | 5 +- src/Provenance/General.ts | 3 +- src/Tasks.ts | 143 +-- src/Taskview/SearchBar.ts | 77 +- src/Taskview/SearchColumn.ts | 8 +- src/Taskview/Taskview.ts | 1053 +++++++++-------- src/Taskview/columns/AColumn.ts | 12 +- src/Taskview/columns/AttributeColumn.ts | 337 +----- src/Taskview/columns/Histogram.ts | 334 ++++++ src/Taskview/columns/PrevalenceColumn.ts | 23 +- src/Taskview/columns/index.ts | 3 - src/Taskview/index.ts | 2 - src/Taskview/interfaces.ts | 0 src/Taskview/tasks/ATask.ts | 4 +- src/Taskview/tasks/Compare.ts | 21 +- src/Taskview/tasks/Details.ts | 13 +- src/Taskview/tasks/Filter.ts | 10 +- src/Taskview/tasks/Prevalence.ts | 15 +- .../visualizations/AVegaVisualization.ts | 99 +- src/Taskview/visualizations/AreaChart.ts | 4 +- src/Taskview/visualizations/DensityPlot.ts | 8 +- src/Taskview/visualizations/GroupedBoxplot.ts | 11 +- .../visualizations/GroupedHistogram.ts | 4 +- src/Taskview/visualizations/Histogram.ts | 2 +- src/Taskview/visualizations/IVisualization.ts | 4 +- .../visualizations/KaplanMeierPlot.ts | 8 +- .../MultiAttributeVisualization.ts | 15 +- src/Taskview/visualizations/Scatterplot.ts | 15 +- .../visualizations/dimreduce/OneHotEncoder.ts | 2 +- src/app/CoralApp.ts | 21 +- src/app/CoralSelectionListener.ts | 6 +- src/app/interfaces.ts | 11 +- src/base/events.ts | 8 +- src/base/index.ts | 2 + src/base/interfaces.ts | 236 ++++ src/base/rest.ts | 270 +---- src/cohortview.ts | 81 +- src/common/config.ts | 35 +- src/common/forms.ts | 67 +- src/data/Attribute.ts | 215 +--- src/data/IAttribute.ts | 128 ++ src/data/ISpecialAttribute.ts | 53 + src/data/SpecialAttribute.ts | 65 +- src/data/index.ts | 1 + src/index.ts | 3 +- src/phovea.ts | 10 +- src/phovea_registry.ts | 3 +- src/util.ts | 104 +- src/utils/labels.ts | 2 +- 69 files changed, 1969 insertions(+), 1832 deletions(-) create mode 100755 coral/tests/conftest.py create mode 100644 coral/tests/test_api.py delete mode 100644 coral/tests/test_dummy.py create mode 100644 src/CohortContext.ts create mode 100644 src/Taskview/columns/Histogram.ts create mode 100644 src/Taskview/interfaces.ts create mode 100644 src/base/index.ts create mode 100644 src/base/interfaces.ts create mode 100644 src/data/IAttribute.ts create mode 100644 src/data/ISpecialAttribute.ts diff --git a/.eslintcache b/.eslintcache index 178f192..862e86f 100644 --- a/.eslintcache +++ b/.eslintcache @@ -1 +1 @@ -[{"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Cohort.ts":"1","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/CohortInterfaces.ts":"2","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/CohortRepresentations.ts":"3","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/OnboardingManager.ts":"4","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Overview/CohortOverview.ts":"5","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Overview/OverviewLayout.ts":"6","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Overview/index.ts":"7","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Provenance/CohortEV.ts":"8","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Provenance/General.ts":"9","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/TaskRepresentations.ts":"10","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Tasks.ts":"11","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/SearchBar.ts":"12","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/SearchColumn.ts":"13","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/Taskview.ts":"14","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/columns/AColumn.ts":"15","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/columns/AttributeColumn.ts":"16","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/columns/CohortColumn.ts":"17","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/columns/NumberColumn.ts":"18","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/columns/PrevalenceColumn.ts":"19","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/columns/index.ts":"20","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/index.ts":"21","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/tasks/ATask.ts":"22","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/tasks/Compare.ts":"23","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/tasks/Details.ts":"24","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/tasks/Filter.ts":"25","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/tasks/Prevalence.ts":"26","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/tasks/TaskList.ts":"27","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/tasks/index.ts":"28","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/AVegaVisualization.ts":"29","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/AreaChart.ts":"30","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/DensityPlot.ts":"31","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/GroupedBoxplot.ts":"32","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/GroupedHistogram.ts":"33","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/Histogram.ts":"34","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/IVisualization.ts":"35","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/KaplanMeierPlot.ts":"36","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/MultiAttributeVisualization.ts":"37","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/Scatterplot.ts":"38","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/config/ConfidenceConfig.ts":"39","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/config/CountCounfig.ts":"40","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/config/GroupConfig.ts":"41","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/config/ScaleConfig.ts":"42","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/config/SortConfig.ts":"43","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/config/VisConfig.ts":"44","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/constants.ts":"45","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/dimreduce/OneHotEncoder.ts":"46","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/dimreduce/OneHotEncoder.worker.ts":"47","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/dimreduce/tsne.worker.ts":"48","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/index.ts":"49","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Tooltip.ts":"50","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/app.ts":"51","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/base/extensions.ts":"52","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/cohortview.ts":"53","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/colors.ts":"54","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/common/GeneUtils.ts":"55","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/common/config.ts":"56","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/common/forms.ts":"57","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/common/index.ts":"58","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/config/onboarding.ts":"59","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/data/Attribute.ts":"60","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/data/SpecialAttribute.ts":"61","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/data/index.ts":"62","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/index.ts":"63","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/phovea.ts":"64","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/phovea_registry.ts":"65","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/rest.ts":"66","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/util.ts":"67","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/utilCustomEvents.ts":"68","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/utilIdTypes.ts":"69","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/utilLabels.ts":"70","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/utils/RouterScrollToTop.tsx":"71","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/utils/index.ts":"72","/home/runner/work/github-maintenance/github-maintenance/tmp/repository/tests/app.test.ts":"73"},{"size":27212,"mtime":1660653050518},{"size":4277,"mtime":1660653050498},{"size":26818,"mtime":1660653050514},{"size":1502,"mtime":1660653050514},{"size":43468,"mtime":1660653050514},{"size":8116,"mtime":1660653050518},{"size":68,"mtime":1660653050514,"results":"74","hashOfConfig":"75"},{"size":3224,"mtime":1660653050522},{"size":1789,"mtime":1660653050522},{"size":5525,"mtime":1660653050514},{"size":5954,"mtime":1660653050522},{"size":42678,"mtime":1660653050522},{"size":6077,"mtime":1660653050522},{"size":24877,"mtime":1660653050522},{"size":6881,"mtime":1660653050518},{"size":17186,"mtime":1660653050518},{"size":3801,"mtime":1660653050518},{"size":1098,"mtime":1660653050518},{"size":4135,"mtime":1660653050518},{"size":344,"mtime":1660653050518},{"size":280,"mtime":1660653050518},{"size":2224,"mtime":1660653050522},{"size":28965,"mtime":1660653050522},{"size":6579,"mtime":1660653050522},{"size":9719,"mtime":1660653050522},{"size":42107,"mtime":1660653050522},{"size":319,"mtime":1660653050522},{"size":163,"mtime":1660653050522,"results":"76","hashOfConfig":"75"},{"size":31993,"mtime":1660653050518},{"size":3175,"mtime":1660653050518},{"size":20420,"mtime":1660653050518},{"size":45017,"mtime":1660653050518},{"size":8895,"mtime":1660653050518},{"size":5455,"mtime":1660653050518},{"size":411,"mtime":1660653050518},{"size":32290,"mtime":1660653050518},{"size":20281,"mtime":1660653050518},{"size":39002,"mtime":1660653050522},{"size":315,"mtime":1660653050518},{"size":272,"mtime":1660653050518},{"size":433,"mtime":1660653050518},{"size":631,"mtime":1660653050518},{"size":835,"mtime":1660653050518},{"size":1049,"mtime":1660653050518},{"size":173,"mtime":1660653050518},{"size":2820,"mtime":1660653050518},{"size":115,"mtime":1660653050518},{"size":95,"mtime":1660653050518},{"size":304,"mtime":1660653050518,"results":"77","hashOfConfig":"75"},{"size":2776,"mtime":1660653050514},{"size":26865,"mtime":1660653050518},{"size":838,"mtime":1660653050498},{"size":2595,"mtime":1660653050498},{"size":417,"mtime":1660653050514},{"size":8349,"mtime":1660653050498},{"size":17683,"mtime":1660653050498},{"size":24505,"mtime":1660653050498},{"size":80,"mtime":1660653050498,"results":"78","hashOfConfig":"75"},{"size":4306,"mtime":1660653050514},{"size":19692,"mtime":1660653050514},{"size":16041,"mtime":1660653050514},{"size":29,"mtime":1660653050514,"results":"79","hashOfConfig":"75"},{"size":311,"mtime":1660653050514,"results":"80","hashOfConfig":"75"},{"size":2685,"mtime":1660653050522},{"size":617,"mtime":1660653050534},{"size":23209,"mtime":1660653050518},{"size":9294,"mtime":1660653050498},{"size":3931,"mtime":1660653050514},{"size":1968,"mtime":1660653050498},{"size":9552,"mtime":1660653050498},{"size":358,"mtime":1660653050518,"results":"81","hashOfConfig":"75"},{"size":37,"mtime":1660653050518,"results":"82","hashOfConfig":"75"},{"size":280,"mtime":1660653046218,"results":"83","hashOfConfig":"75"},{"filePath":"84","messages":"85","suppressedMessages":"86","errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"2udmpj",{"filePath":"87","messages":"88","suppressedMessages":"89","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"90","messages":"91","suppressedMessages":"92","errorCount":8,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"93","messages":"94","suppressedMessages":"95","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"96","messages":"97","suppressedMessages":"98","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"99","messages":"100","suppressedMessages":"101","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"102","messages":"103","suppressedMessages":"104","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"105","messages":"106","suppressedMessages":"107","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"108","messages":"109","suppressedMessages":"110","errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Overview/index.ts",["111","112"],[],"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/tasks/index.ts",[],[],"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/Taskview/visualizations/index.ts",["113","114","115","116","117","118","119","120"],[],"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/common/index.ts",[],[],"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/data/index.ts",[],[],"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/index.ts",[],[],"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/utils/RouterScrollToTop.tsx",[],[],"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/src/utils/index.ts",[],[],"/home/runner/work/github-maintenance/github-maintenance/tmp/repository/tests/app.test.ts",["121","122"],[],{"ruleId":"123","severity":2,"message":"124","line":1,"column":1,"nodeType":"125","endLine":1,"endColumn":34},{"ruleId":"123","severity":2,"message":"126","line":2,"column":1,"nodeType":"125","endLine":2,"endColumn":34},{"ruleId":"123","severity":2,"message":"127","line":1,"column":1,"nodeType":"125","endLine":1,"endColumn":29},{"ruleId":"123","severity":2,"message":"128","line":2,"column":1,"nodeType":"125","endLine":2,"endColumn":38},{"ruleId":"123","severity":2,"message":"129","line":4,"column":1,"nodeType":"125","endLine":4,"endColumn":31},{"ruleId":"123","severity":2,"message":"130","line":5,"column":1,"nodeType":"125","endLine":5,"endColumn":36},{"ruleId":"123","severity":2,"message":"131","line":6,"column":1,"nodeType":"125","endLine":6,"endColumn":29},{"ruleId":"123","severity":2,"message":"132","line":7,"column":1,"nodeType":"125","endLine":7,"endColumn":34},{"ruleId":"123","severity":2,"message":"133","line":8,"column":1,"nodeType":"125","endLine":8,"endColumn":47},{"ruleId":"123","severity":2,"message":"134","line":9,"column":1,"nodeType":"125","endLine":9,"endColumn":31},{"ruleId":"135","severity":1,"message":"136","line":10,"column":1,"nodeType":"137","messageId":"138","endLine":10,"endColumn":30},{"ruleId":"135","severity":1,"message":"136","line":11,"column":1,"nodeType":"137","messageId":"138","endLine":11,"endColumn":29},"import/no-cycle","Dependency cycle via ../app:4","ExportAllDeclaration","Dependency cycle via ../CohortInterfaces:1=>./data/Attribute:4=>./SpecialAttribute:9=>../cohortview:4=>./app:3","Dependency cycle via ../../CohortInterfaces:4=>./Taskview/Taskview:6=>./SearchColumn:11=>./tasks/TaskList:10=>./Details:3","Dependency cycle via ../../CohortInterfaces:9=>./Taskview/Taskview:6=>./SearchColumn:11=>./tasks/TaskList:10=>./Details:3","Dependency cycle via ../../CohortInterfaces:6=>./Taskview/Taskview:6=>./SearchColumn:11=>./tasks/TaskList:10=>./Details:3","Dependency cycle via ../../CohortInterfaces:5=>./Taskview/Taskview:6=>./SearchColumn:11=>./tasks/TaskList:10=>./Details:3","Dependency cycle via ../../CohortInterfaces:2=>./Taskview/Taskview:6=>./SearchColumn:11=>./tasks/TaskList:10=>./Details:3","Dependency cycle via ../../CohortInterfaces:1=>./Taskview/Taskview:6=>./SearchColumn:11=>./tasks/TaskList:10=>./Details:3","Dependency cycle via ../../CohortInterfaces:8=>./Taskview/Taskview:6=>./SearchColumn:11=>./tasks/TaskList:10=>./Details:3","Dependency cycle via ../../Cohort:5=>./CohortInterfaces:2=>./Taskview/Taskview:6=>./SearchColumn:11=>./tasks/TaskList:10=>./Details:3","jest/no-commented-out-tests","Some tests seem to be commented","Line","commentedTests"] \ No newline at end of file +[{"/home/oltion/workspaces/coral/coral/coral/src/Cohort.ts":"1","/home/oltion/workspaces/coral/coral/coral/src/CohortContext.ts":"2","/home/oltion/workspaces/coral/coral/coral/src/CohortRepresentations.ts":"3","/home/oltion/workspaces/coral/coral/coral/src/OnboardingManager.ts":"4","/home/oltion/workspaces/coral/coral/coral/src/Overview/CohortOverview.ts":"5","/home/oltion/workspaces/coral/coral/coral/src/Overview/OverviewLayout.ts":"6","/home/oltion/workspaces/coral/coral/coral/src/Overview/index.ts":"7","/home/oltion/workspaces/coral/coral/coral/src/Provenance/CohortEV.ts":"8","/home/oltion/workspaces/coral/coral/coral/src/Provenance/General.ts":"9","/home/oltion/workspaces/coral/coral/coral/src/TaskRepresentations.ts":"10","/home/oltion/workspaces/coral/coral/coral/src/Tasks.ts":"11","/home/oltion/workspaces/coral/coral/coral/src/Taskview/SearchBar.ts":"12","/home/oltion/workspaces/coral/coral/coral/src/Taskview/SearchColumn.ts":"13","/home/oltion/workspaces/coral/coral/coral/src/Taskview/Taskview.ts":"14","/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/AColumn.ts":"15","/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/AttributeColumn.ts":"16","/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/CohortColumn.ts":"17","/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/NumberColumn.ts":"18","/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/PrevalenceColumn.ts":"19","/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/index.ts":"20","/home/oltion/workspaces/coral/coral/coral/src/Taskview/index.ts":"21","/home/oltion/workspaces/coral/coral/coral/src/Taskview/interfaces.ts":"22","/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/ATask.ts":"23","/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/Compare.ts":"24","/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/Details.ts":"25","/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/Filter.ts":"26","/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/Prevalence.ts":"27","/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/TaskList.ts":"28","/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/index.ts":"29","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/AVegaVisualization.ts":"30","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/AreaChart.ts":"31","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/DensityPlot.ts":"32","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/GroupedBoxplot.ts":"33","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/GroupedHistogram.ts":"34","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/Histogram.ts":"35","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/IVisualization.ts":"36","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/KaplanMeierPlot.ts":"37","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/MultiAttributeVisualization.ts":"38","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/Scatterplot.ts":"39","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/ConfidenceConfig.ts":"40","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/CountCounfig.ts":"41","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/GroupConfig.ts":"42","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/ScaleConfig.ts":"43","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/SortConfig.ts":"44","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/VisConfig.ts":"45","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/constants.ts":"46","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/dimreduce/OneHotEncoder.ts":"47","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/dimreduce/OneHotEncoder.worker.ts":"48","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/dimreduce/tsne.worker.ts":"49","/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/index.ts":"50","/home/oltion/workspaces/coral/coral/coral/src/app/Coral.ts":"51","/home/oltion/workspaces/coral/coral/coral/src/app/CoralApp.ts":"52","/home/oltion/workspaces/coral/coral/coral/src/app/CoralSelectionListener.ts":"53","/home/oltion/workspaces/coral/coral/coral/src/app/index.ts":"54","/home/oltion/workspaces/coral/coral/coral/src/app/interfaces.ts":"55","/home/oltion/workspaces/coral/coral/coral/src/base/events.ts":"56","/home/oltion/workspaces/coral/coral/coral/src/base/extensions.ts":"57","/home/oltion/workspaces/coral/coral/coral/src/base/index.ts":"58","/home/oltion/workspaces/coral/coral/coral/src/base/interfaces.ts":"59","/home/oltion/workspaces/coral/coral/coral/src/base/rest.ts":"60","/home/oltion/workspaces/coral/coral/coral/src/cohortview.ts":"61","/home/oltion/workspaces/coral/coral/coral/src/common/GeneUtils.ts":"62","/home/oltion/workspaces/coral/coral/coral/src/common/config.ts":"63","/home/oltion/workspaces/coral/coral/coral/src/common/forms.ts":"64","/home/oltion/workspaces/coral/coral/coral/src/common/index.ts":"65","/home/oltion/workspaces/coral/coral/coral/src/config/colors.ts":"66","/home/oltion/workspaces/coral/coral/coral/src/config/entities.ts":"67","/home/oltion/workspaces/coral/coral/coral/src/config/onboarding.ts":"68","/home/oltion/workspaces/coral/coral/coral/src/data/Attribute.ts":"69","/home/oltion/workspaces/coral/coral/coral/src/data/IAttribute.ts":"70","/home/oltion/workspaces/coral/coral/coral/src/data/ISpecialAttribute.ts":"71","/home/oltion/workspaces/coral/coral/coral/src/data/SpecialAttribute.ts":"72","/home/oltion/workspaces/coral/coral/coral/src/data/index.ts":"73","/home/oltion/workspaces/coral/coral/coral/src/index.ts":"74","/home/oltion/workspaces/coral/coral/coral/src/phovea.ts":"75","/home/oltion/workspaces/coral/coral/coral/src/phovea_registry.ts":"76","/home/oltion/workspaces/coral/coral/coral/src/util.ts":"77","/home/oltion/workspaces/coral/coral/coral/src/utils/RouterScrollToTop.tsx":"78","/home/oltion/workspaces/coral/coral/coral/src/utils/ScrollLinker.ts":"79","/home/oltion/workspaces/coral/coral/coral/src/utils/index.ts":"80","/home/oltion/workspaces/coral/coral/coral/src/utils/labels.ts":"81","/home/oltion/workspaces/coral/coral/coral/tests/app.test.ts":"82"},{"size":28962,"mtime":1677750080609,"results":"83","hashOfConfig":"84"},{"size":336,"mtime":1677762336104,"results":"85","hashOfConfig":"84"},{"size":26927,"mtime":1677762386064,"results":"86","hashOfConfig":"84"},{"size":1471,"mtime":1677752771124,"results":"87","hashOfConfig":"84"},{"size":43451,"mtime":1677758519960,"results":"88","hashOfConfig":"84"},{"size":8036,"mtime":1677674925077,"results":"89","hashOfConfig":"84"},{"size":68,"mtime":1677147981823,"results":"90","hashOfConfig":"84"},{"size":3364,"mtime":1677758572696,"results":"91","hashOfConfig":"84"},{"size":1843,"mtime":1677758627448,"results":"92","hashOfConfig":"84"},{"size":5511,"mtime":1677674925077,"results":"93","hashOfConfig":"84"},{"size":4045,"mtime":1677752741909,"results":"94","hashOfConfig":"84"},{"size":41779,"mtime":1677757663852,"results":"95","hashOfConfig":"84"},{"size":6210,"mtime":1677758743755,"results":"96","hashOfConfig":"84"},{"size":24769,"mtime":1677759334028,"results":"97","hashOfConfig":"84"},{"size":9665,"mtime":1677757712484,"results":"98","hashOfConfig":"84"},{"size":17420,"mtime":1677757934638,"results":"99","hashOfConfig":"84"},{"size":3816,"mtime":1677674925077,"results":"100","hashOfConfig":"84"},{"size":1110,"mtime":1677674925077,"results":"101","hashOfConfig":"84"},{"size":4187,"mtime":1677674925077,"results":"102","hashOfConfig":"84"},{"size":247,"mtime":1677758031466,"results":"103","hashOfConfig":"84"},{"size":284,"mtime":1677147981823,"results":"104","hashOfConfig":"84"},{"size":0,"mtime":1677752754957,"results":"105","hashOfConfig":"84"},{"size":2236,"mtime":1677750630495,"results":"106","hashOfConfig":"84"},{"size":29374,"mtime":1677750892903,"results":"107","hashOfConfig":"84"},{"size":6615,"mtime":1677751116446,"results":"108","hashOfConfig":"84"},{"size":9903,"mtime":1677759432283,"results":"109","hashOfConfig":"84"},{"size":42231,"mtime":1677758809951,"results":"110","hashOfConfig":"84"},{"size":319,"mtime":1677147981823,"results":"111","hashOfConfig":"84"},{"size":163,"mtime":1677147981823,"results":"112","hashOfConfig":"84"},{"size":32721,"mtime":1677758979138,"results":"113","hashOfConfig":"84"},{"size":3205,"mtime":1677758924402,"results":"114","hashOfConfig":"84"},{"size":20498,"mtime":1677759042594,"results":"115","hashOfConfig":"84"},{"size":45715,"mtime":1677759084225,"results":"116","hashOfConfig":"84"},{"size":9381,"mtime":1677759101049,"results":"117","hashOfConfig":"84"},{"size":5511,"mtime":1677674925077,"results":"118","hashOfConfig":"84"},{"size":426,"mtime":1677762573739,"results":"119","hashOfConfig":"84"},{"size":32067,"mtime":1677762580106,"results":"120","hashOfConfig":"84"},{"size":20476,"mtime":1677759285868,"results":"121","hashOfConfig":"84"},{"size":40013,"mtime":1677759259572,"results":"122","hashOfConfig":"84"},{"size":316,"mtime":1677147981827,"results":"123","hashOfConfig":"84"},{"size":273,"mtime":1677147981827,"results":"124","hashOfConfig":"84"},{"size":435,"mtime":1677147981827,"results":"125","hashOfConfig":"84"},{"size":633,"mtime":1677147981827,"results":"126","hashOfConfig":"84"},{"size":848,"mtime":1677147981827,"results":"127","hashOfConfig":"84"},{"size":1047,"mtime":1677147981827,"results":"128","hashOfConfig":"84"},{"size":171,"mtime":1677147981827,"results":"129","hashOfConfig":"84"},{"size":2871,"mtime":1677147981827,"results":"130","hashOfConfig":"84"},{"size":117,"mtime":1677147981827,"results":"131","hashOfConfig":"84"},{"size":97,"mtime":1677147981827,"results":"132","hashOfConfig":"84"},{"size":304,"mtime":1677147981827,"results":"133","hashOfConfig":"84"},{"size":1873,"mtime":1677674925077,"results":"134","hashOfConfig":"84"},{"size":22713,"mtime":1677762243073,"results":"135","hashOfConfig":"84"},{"size":3141,"mtime":1677759409836,"results":"136","hashOfConfig":"84"},{"size":125,"mtime":1677674925077,"results":"137","hashOfConfig":"84"},{"size":6846,"mtime":1677675318586,"results":"138","hashOfConfig":"84"},{"size":4006,"mtime":1677757813027,"results":"139","hashOfConfig":"84"},{"size":842,"mtime":1677147981835,"results":"140","hashOfConfig":"84"},{"size":54,"mtime":1677678106347,"results":"141","hashOfConfig":"84"},{"size":6542,"mtime":1677757948254,"results":"142","hashOfConfig":"84"},{"size":17405,"mtime":1677679749229,"results":"143","hashOfConfig":"84"},{"size":2522,"mtime":1677762480963,"results":"144","hashOfConfig":"84"},{"size":8357,"mtime":1677147981835,"results":"145","hashOfConfig":"84"},{"size":16562,"mtime":1677674999908,"results":"146","hashOfConfig":"84"},{"size":22691,"mtime":1677674999908,"results":"147","hashOfConfig":"84"},{"size":80,"mtime":1677147981835,"results":"148","hashOfConfig":"84"},{"size":1226,"mtime":1677674925081,"results":"149","hashOfConfig":"84"},{"size":1974,"mtime":1677674925081,"results":"150","hashOfConfig":"84"},{"size":4314,"mtime":1677147981835,"results":"151","hashOfConfig":"84"},{"size":16586,"mtime":1677750630495,"results":"152","hashOfConfig":"84"},{"size":3220,"mtime":1677679815813,"results":"153","hashOfConfig":"84"},{"size":1522,"mtime":1677750630495,"results":"154","hashOfConfig":"84"},{"size":15086,"mtime":1677750630495,"results":"155","hashOfConfig":"84"},{"size":59,"mtime":1677750661147,"results":"156","hashOfConfig":"84"},{"size":298,"mtime":1677678138579,"results":"157","hashOfConfig":"84"},{"size":2725,"mtime":1677674999908,"results":"158","hashOfConfig":"84"},{"size":660,"mtime":1677674999908,"results":"159","hashOfConfig":"84"},{"size":8905,"mtime":1677759231461,"results":"160","hashOfConfig":"84"},{"size":358,"mtime":1677674925081,"results":"161","hashOfConfig":"84"},{"size":1214,"mtime":1677674925081,"results":"162","hashOfConfig":"84"},{"size":37,"mtime":1677674925081,"results":"163","hashOfConfig":"84"},{"size":9496,"mtime":1677759126833,"results":"164","hashOfConfig":"84"},{"size":280,"mtime":1677147981835,"results":"165","hashOfConfig":"84"},{"filePath":"166","messages":"167","suppressedMessages":"168","errorCount":25,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"vl0kru",{"filePath":"169","messages":"170","suppressedMessages":"171","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"172","messages":"173","suppressedMessages":"174","errorCount":4,"fatalErrorCount":0,"warningCount":15,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"175","messages":"176","suppressedMessages":"177","errorCount":2,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"178","messages":"179","suppressedMessages":"180","errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"181","messages":"182","suppressedMessages":"183","errorCount":0,"fatalErrorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"184","messages":"185","suppressedMessages":"186","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"187","messages":"188","suppressedMessages":"189","errorCount":3,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"190","messages":"191","suppressedMessages":"192","errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"193","messages":"194","suppressedMessages":"195","errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"196","messages":"197","suppressedMessages":"198","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"199","messages":"200","suppressedMessages":"201","errorCount":7,"fatalErrorCount":0,"warningCount":17,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"202","messages":"203","suppressedMessages":"204","errorCount":1,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"205","messages":"206","suppressedMessages":"207","errorCount":8,"fatalErrorCount":0,"warningCount":11,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"208","messages":"209","suppressedMessages":"210","errorCount":4,"fatalErrorCount":0,"warningCount":12,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"211","messages":"212","suppressedMessages":"213","errorCount":3,"fatalErrorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"214","messages":"215","suppressedMessages":"216","errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"217","messages":"218","suppressedMessages":"219","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"220","messages":"221","suppressedMessages":"222","errorCount":2,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"223","messages":"224","suppressedMessages":"225","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"226","messages":"227","suppressedMessages":"228","errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"229","messages":"230","suppressedMessages":"231","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"232","messages":"233","suppressedMessages":"234","errorCount":2,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"235","messages":"236","suppressedMessages":"237","errorCount":10,"fatalErrorCount":0,"warningCount":16,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"238","messages":"239","suppressedMessages":"240","errorCount":1,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"241","messages":"242","suppressedMessages":"243","errorCount":1,"fatalErrorCount":0,"warningCount":12,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"244","messages":"245","suppressedMessages":"246","errorCount":3,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"247","messages":"248","suppressedMessages":"249","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"250","messages":"251","suppressedMessages":"252","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"253","messages":"254","suppressedMessages":"255","errorCount":11,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"256","messages":"257","suppressedMessages":"258","errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"259","messages":"260","suppressedMessages":"261","errorCount":58,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":1,"fixableWarningCount":0,"source":null},{"filePath":"262","messages":"263","suppressedMessages":"264","errorCount":5,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"265","messages":"266","suppressedMessages":"267","errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"268","messages":"269","suppressedMessages":"270","errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"271","messages":"272","suppressedMessages":"273","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"274","messages":"275","suppressedMessages":"276","errorCount":2,"fatalErrorCount":0,"warningCount":12,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"277","messages":"278","suppressedMessages":"279","errorCount":12,"fatalErrorCount":0,"warningCount":15,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"280","messages":"281","suppressedMessages":"282","errorCount":8,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"283","messages":"284","suppressedMessages":"285","errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"286","messages":"287","suppressedMessages":"288","errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"289","messages":"290","suppressedMessages":"291","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"292","messages":"293","suppressedMessages":"294","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"295","messages":"296","suppressedMessages":"297","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"298","messages":"299","suppressedMessages":"300","errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"301","messages":"302","suppressedMessages":"303","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"304","messages":"305","suppressedMessages":"306","errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"307","messages":"308","suppressedMessages":"309","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"310","messages":"311","suppressedMessages":"312","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"313","messages":"314","suppressedMessages":"315","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"316","messages":"317","suppressedMessages":"318","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"319","messages":"320","suppressedMessages":"321","errorCount":2,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"322","messages":"323","suppressedMessages":"324","errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"325","messages":"326","suppressedMessages":"327","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"328","messages":"329","suppressedMessages":"330","errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"331","messages":"332","suppressedMessages":"333","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"334","messages":"335","suppressedMessages":"336","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"337","messages":"338","suppressedMessages":"339","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"340","messages":"341","suppressedMessages":"342","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"343","messages":"344","suppressedMessages":"345","errorCount":1,"fatalErrorCount":0,"warningCount":10,"fixableErrorCount":1,"fixableWarningCount":0,"source":null},{"filePath":"346","messages":"347","suppressedMessages":"348","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"349","messages":"350","suppressedMessages":"351","errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"352","messages":"353","suppressedMessages":"354","errorCount":7,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"355","messages":"356","suppressedMessages":"357","errorCount":17,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"358","messages":"359","suppressedMessages":"360","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"361","messages":"362","suppressedMessages":"363","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"364","messages":"365","suppressedMessages":"366","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"367","messages":"368","suppressedMessages":"369","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"370","messages":"371","suppressedMessages":"372","errorCount":3,"fatalErrorCount":0,"warningCount":16,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"373","messages":"374","suppressedMessages":"375","errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"376","messages":"377","suppressedMessages":"378","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"379","messages":"380","suppressedMessages":"381","errorCount":3,"fatalErrorCount":0,"warningCount":10,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"382","messages":"383","suppressedMessages":"384","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"385","messages":"386","suppressedMessages":"387","errorCount":60,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"388","messages":"389","suppressedMessages":"390","errorCount":1,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"391","messages":"392","suppressedMessages":"393","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"394","messages":"395","suppressedMessages":"396","errorCount":1,"fatalErrorCount":0,"warningCount":11,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"397","messages":"398","suppressedMessages":"399","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"400","messages":"401","suppressedMessages":"402","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"403","messages":"404","suppressedMessages":"405","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"406","messages":"407","suppressedMessages":"408","errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},{"filePath":"409","messages":"410","suppressedMessages":"411","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/home/oltion/workspaces/coral/coral/coral/src/Cohort.ts",["412","413","414","415","416","417","418","419","420","421","422","423","424","425","426","427","428","429","430","431","432","433","434","435","436","437","438"],[],"/home/oltion/workspaces/coral/coral/coral/src/CohortContext.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/CohortRepresentations.ts",["439","440","441","442","443","444","445","446","447","448","449","450","451","452","453","454","455","456","457"],[],"/home/oltion/workspaces/coral/coral/coral/src/OnboardingManager.ts",["458","459","460"],[],"/home/oltion/workspaces/coral/coral/coral/src/Overview/CohortOverview.ts",["461","462","463","464","465","466"],[],"/home/oltion/workspaces/coral/coral/coral/src/Overview/OverviewLayout.ts",["467","468","469","470","471"],[],"/home/oltion/workspaces/coral/coral/coral/src/Overview/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Provenance/CohortEV.ts",["472","473","474","475","476","477","478","479","480"],[],"/home/oltion/workspaces/coral/coral/coral/src/Provenance/General.ts",["481","482"],[],"/home/oltion/workspaces/coral/coral/coral/src/TaskRepresentations.ts",["483","484"],[],"/home/oltion/workspaces/coral/coral/coral/src/Tasks.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/SearchBar.ts",["485","486","487","488","489","490","491","492","493","494","495","496","497","498","499","500","501","502","503","504","505","506","507","508"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/SearchColumn.ts",["509","510","511","512","513"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/Taskview.ts",["514","515","516","517","518","519","520","521","522","523","524","525","526","527","528","529","530","531","532"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/AColumn.ts",["533","534","535","536","537","538","539","540","541","542","543","544","545","546","547","548"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/AttributeColumn.ts",["549","550","551","552","553","554","555","556"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/CohortColumn.ts",["557"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/NumberColumn.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/PrevalenceColumn.ts",["558","559","560"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/columns/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/index.ts",["561","562"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/interfaces.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/ATask.ts",["563","564","565","566","567","568","569","570"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/Compare.ts",["571","572","573","574","575","576","577","578","579","580","581","582","583","584","585","586","587","588","589","590","591","592","593","594","595","596"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/Details.ts",["597","598","599","600","601","602","603"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/Filter.ts",["604","605","606","607","608","609","610","611","612","613","614","615","616"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/Prevalence.ts",["617","618","619","620","621","622","623","624","625"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/TaskList.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/tasks/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/AVegaVisualization.ts",["626","627","628","629","630","631","632","633","634","635","636","637","638","639","640","641","642","643","644","645","646","647","648","649"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/AreaChart.ts",["650"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/DensityPlot.ts",["651","652","653","654","655","656","657","658","659","660","661","662","663","664","665","666","667","668","669","670","671","672","673","674","675","676","677","678","679","680","681","682","683","684","685","686","687","688","689","690","691","692","693","694","695","696","697","698","699","700","701","702","703","704","705","706","707","708"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/GroupedBoxplot.ts",["709","710","711","712","713","714","715","716","717"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/GroupedHistogram.ts",["718","719"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/Histogram.ts",["720","721"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/IVisualization.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/KaplanMeierPlot.ts",["722","723","724","725","726","727","728","729","730","731","732","733","734","735"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/MultiAttributeVisualization.ts",["736","737","738","739","740","741","742","743","744","745","746","747","748","749","750","751","752","753","754","755","756","757","758","759","760","761","762"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/Scatterplot.ts",["763","764","765","766","767","768","769","770","771","772","773","774","775","776","777","778","779","780","781","782","783"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/ConfidenceConfig.ts",["784"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/CountCounfig.ts",["785"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/GroupConfig.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/ScaleConfig.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/SortConfig.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/config/VisConfig.ts",["786"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/constants.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/dimreduce/OneHotEncoder.ts",["787","788"],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/dimreduce/OneHotEncoder.worker.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/dimreduce/tsne.worker.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/Taskview/visualizations/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/app/Coral.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/app/CoralApp.ts",["789","790","791","792","793","794","795","796","797","798","799","800","801","802","803"],[],"/home/oltion/workspaces/coral/coral/coral/src/app/CoralSelectionListener.ts",["804","805","806","807","808","809"],[],"/home/oltion/workspaces/coral/coral/coral/src/app/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/app/interfaces.ts",["810","811","812","813"],[],"/home/oltion/workspaces/coral/coral/coral/src/base/events.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/base/extensions.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/base/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/base/interfaces.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/base/rest.ts",["814","815","816","817","818","819","820","821","822","823","824"],[],"/home/oltion/workspaces/coral/coral/coral/src/cohortview.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/common/GeneUtils.ts",["825","826","827","828","829","830"],[],"/home/oltion/workspaces/coral/coral/coral/src/common/config.ts",["831","832","833","834","835","836","837","838","839","840"],[],"/home/oltion/workspaces/coral/coral/coral/src/common/forms.ts",["841","842","843","844","845","846","847","848","849","850","851","852","853","854","855","856","857","858","859","860","861"],[],"/home/oltion/workspaces/coral/coral/coral/src/common/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/config/colors.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/config/entities.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/config/onboarding.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/data/Attribute.ts",["862","863","864","865","866","867","868","869","870","871","872","873","874","875","876","877","878","879","880"],[],"/home/oltion/workspaces/coral/coral/coral/src/data/IAttribute.ts",["881","882"],[],"/home/oltion/workspaces/coral/coral/coral/src/data/ISpecialAttribute.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/data/SpecialAttribute.ts",["883","884","885","886","887","888","889","890","891","892","893","894","895"],[],"/home/oltion/workspaces/coral/coral/coral/src/data/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/index.ts",["896","897","898","899","900","901","902","903","904","905","906","907","908","909","910","911","912","913","914","915","916","917","918","919","920","921","922","923","924","925","926","927","928","929","930","931","932","933","934","935","936","937","938","939","940","941","942","943","944","945","946","947","948","949","950","951","952","953","954","955"],[],"/home/oltion/workspaces/coral/coral/coral/src/phovea.ts",["956","957","958","959","960"],[],"/home/oltion/workspaces/coral/coral/coral/src/phovea_registry.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/util.ts",["961","962","963","964","965","966","967","968","969","970","971","972"],[],"/home/oltion/workspaces/coral/coral/coral/src/utils/RouterScrollToTop.tsx",[],[],"/home/oltion/workspaces/coral/coral/coral/src/utils/ScrollLinker.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/utils/index.ts",[],[],"/home/oltion/workspaces/coral/coral/coral/src/utils/labels.ts",["973","974","975","976","977","978","979"],[],"/home/oltion/workspaces/coral/coral/coral/tests/app.test.ts",[],[],{"ruleId":"980","severity":2,"message":"981","line":95,"column":11,"nodeType":"982","messageId":"983","endLine":95,"endColumn":29},{"ruleId":"980","severity":2,"message":"984","line":103,"column":22,"nodeType":"982","messageId":"983","endLine":103,"endColumn":45},{"ruleId":"980","severity":2,"message":"985","line":106,"column":19,"nodeType":"982","messageId":"983","endLine":106,"endColumn":25},{"ruleId":"980","severity":2,"message":"981","line":143,"column":11,"nodeType":"982","messageId":"983","endLine":143,"endColumn":29},{"ruleId":"980","severity":2,"message":"984","line":149,"column":22,"nodeType":"982","messageId":"983","endLine":149,"endColumn":45},{"ruleId":"980","severity":2,"message":"985","line":154,"column":19,"nodeType":"982","messageId":"983","endLine":154,"endColumn":25},{"ruleId":"980","severity":2,"message":"981","line":181,"column":11,"nodeType":"982","messageId":"983","endLine":181,"endColumn":29},{"ruleId":"980","severity":2,"message":"984","line":187,"column":22,"nodeType":"982","messageId":"983","endLine":187,"endColumn":45},{"ruleId":"980","severity":2,"message":"985","line":192,"column":19,"nodeType":"982","messageId":"983","endLine":192,"endColumn":25},{"ruleId":"980","severity":2,"message":"981","line":218,"column":11,"nodeType":"982","messageId":"983","endLine":218,"endColumn":29},{"ruleId":"980","severity":2,"message":"984","line":223,"column":22,"nodeType":"982","messageId":"983","endLine":223,"endColumn":45},{"ruleId":"980","severity":2,"message":"985","line":228,"column":19,"nodeType":"982","messageId":"983","endLine":228,"endColumn":25},{"ruleId":"980","severity":2,"message":"981","line":256,"column":11,"nodeType":"982","messageId":"983","endLine":256,"endColumn":29},{"ruleId":"980","severity":2,"message":"984","line":263,"column":22,"nodeType":"982","messageId":"983","endLine":263,"endColumn":45},{"ruleId":"980","severity":2,"message":"985","line":269,"column":19,"nodeType":"982","messageId":"983","endLine":269,"endColumn":25},{"ruleId":"980","severity":2,"message":"981","line":298,"column":11,"nodeType":"982","messageId":"983","endLine":298,"endColumn":29},{"ruleId":"980","severity":2,"message":"984","line":306,"column":22,"nodeType":"982","messageId":"983","endLine":306,"endColumn":45},{"ruleId":"980","severity":2,"message":"985","line":312,"column":19,"nodeType":"982","messageId":"983","endLine":312,"endColumn":25},{"ruleId":"980","severity":2,"message":"981","line":341,"column":11,"nodeType":"982","messageId":"983","endLine":341,"endColumn":29},{"ruleId":"980","severity":2,"message":"984","line":349,"column":22,"nodeType":"982","messageId":"983","endLine":349,"endColumn":45},{"ruleId":"980","severity":2,"message":"985","line":355,"column":19,"nodeType":"982","messageId":"983","endLine":355,"endColumn":25},{"ruleId":"980","severity":2,"message":"981","line":381,"column":11,"nodeType":"982","messageId":"983","endLine":381,"endColumn":29},{"ruleId":"980","severity":2,"message":"984","line":386,"column":22,"nodeType":"982","messageId":"983","endLine":386,"endColumn":45},{"ruleId":"980","severity":2,"message":"985","line":392,"column":19,"nodeType":"982","messageId":"983","endLine":392,"endColumn":25},{"ruleId":"980","severity":2,"message":"985","line":468,"column":19,"nodeType":"982","messageId":"983","endLine":468,"endColumn":25},{"ruleId":"986","severity":1,"message":"987","line":469,"column":9,"nodeType":"982","messageId":"988","endLine":469,"endColumn":13},{"ruleId":"989","severity":1,"message":"990","line":871,"column":40,"nodeType":"991","messageId":"992","endLine":871,"endColumn":43,"suggestions":"993"},{"ruleId":"986","severity":1,"message":"994","line":3,"column":10,"nodeType":"982","messageId":"988","endLine":3,"endColumn":27},{"ruleId":"989","severity":1,"message":"990","line":28,"column":23,"nodeType":"991","messageId":"992","endLine":28,"endColumn":26,"suggestions":"995"},{"ruleId":"986","severity":1,"message":"996","line":55,"column":19,"nodeType":"982","messageId":"988","endLine":55,"endColumn":33},{"ruleId":"986","severity":1,"message":"997","line":55,"column":35,"nodeType":"982","messageId":"988","endLine":55,"endColumn":48},{"ruleId":"989","severity":1,"message":"990","line":174,"column":35,"nodeType":"991","messageId":"992","endLine":174,"endColumn":38,"suggestions":"998"},{"ruleId":"989","severity":1,"message":"990","line":195,"column":41,"nodeType":"991","messageId":"992","endLine":195,"endColumn":44,"suggestions":"999"},{"ruleId":"1000","severity":1,"message":"1001","line":214,"column":7,"nodeType":"1002","messageId":"1003","endLine":214,"endColumn":16},{"ruleId":"989","severity":1,"message":"990","line":227,"column":53,"nodeType":"991","messageId":"992","endLine":227,"endColumn":56,"suggestions":"1004"},{"ruleId":"989","severity":1,"message":"990","line":234,"column":58,"nodeType":"991","messageId":"992","endLine":234,"endColumn":61,"suggestions":"1005"},{"ruleId":"986","severity":1,"message":"1006","line":273,"column":38,"nodeType":"982","messageId":"988","endLine":273,"endColumn":43},{"ruleId":"989","severity":1,"message":"990","line":385,"column":111,"nodeType":"991","messageId":"992","endLine":385,"endColumn":114,"suggestions":"1007"},{"ruleId":"989","severity":1,"message":"990","line":405,"column":121,"nodeType":"991","messageId":"992","endLine":405,"endColumn":124,"suggestions":"1008"},{"ruleId":"989","severity":1,"message":"990","line":427,"column":52,"nodeType":"991","messageId":"992","endLine":427,"endColumn":55,"suggestions":"1009"},{"ruleId":"989","severity":1,"message":"990","line":445,"column":46,"nodeType":"991","messageId":"992","endLine":445,"endColumn":49,"suggestions":"1010"},{"ruleId":"1011","severity":2,"message":"1012","line":510,"column":18,"nodeType":"982","messageId":"1013","endLine":510,"endColumn":19},{"ruleId":"986","severity":1,"message":"1014","line":530,"column":9,"nodeType":"982","messageId":"988","endLine":530,"endColumn":18},{"ruleId":"1011","severity":2,"message":"1015","line":602,"column":20,"nodeType":"982","messageId":"1013","endLine":602,"endColumn":31},{"ruleId":"1011","severity":2,"message":"1016","line":617,"column":17,"nodeType":"982","messageId":"1013","endLine":617,"endColumn":24},{"ruleId":"1011","severity":2,"message":"1017","line":634,"column":17,"nodeType":"982","messageId":"1013","endLine":634,"endColumn":26},{"ruleId":"989","severity":1,"message":"990","line":9,"column":32,"nodeType":"991","messageId":"992","endLine":9,"endColumn":35,"suggestions":"1018"},{"ruleId":"1011","severity":2,"message":"1019","line":20,"column":30,"nodeType":"982","messageId":"1013","endLine":20,"endColumn":33},{"ruleId":"980","severity":2,"message":"1020","line":29,"column":7,"nodeType":"982","messageId":"983","endLine":29,"endColumn":26},{"ruleId":"989","severity":1,"message":"990","line":55,"column":26,"nodeType":"991","messageId":"992","endLine":55,"endColumn":29,"suggestions":"1021"},{"ruleId":"986","severity":1,"message":"1022","line":117,"column":34,"nodeType":"982","messageId":"988","endLine":117,"endColumn":36},{"ruleId":"986","severity":1,"message":"1023","line":126,"column":32,"nodeType":"982","messageId":"988","endLine":126,"endColumn":60},{"ruleId":"986","severity":1,"message":"1024","line":848,"column":17,"nodeType":"982","messageId":"988","endLine":848,"endColumn":26},{"ruleId":"986","severity":1,"message":"1006","line":929,"column":49,"nodeType":"982","messageId":"988","endLine":929,"endColumn":54},{"ruleId":"986","severity":1,"message":"1025","line":964,"column":11,"nodeType":"982","messageId":"988","endLine":964,"endColumn":21},{"ruleId":"989","severity":1,"message":"990","line":6,"column":26,"nodeType":"991","messageId":"992","endLine":6,"endColumn":29,"suggestions":"1026"},{"ruleId":"989","severity":1,"message":"990","line":23,"column":38,"nodeType":"991","messageId":"992","endLine":23,"endColumn":41,"suggestions":"1027"},{"ruleId":"1028","severity":1,"message":"1029","line":47,"column":5,"nodeType":"982","messageId":"1030","endLine":47,"endColumn":14},{"ruleId":"1028","severity":1,"message":"1029","line":48,"column":5,"nodeType":"982","messageId":"1030","endLine":48,"endColumn":14},{"ruleId":"986","severity":1,"message":"1031","line":90,"column":13,"nodeType":"982","messageId":"988","endLine":90,"endColumn":23},{"ruleId":"980","severity":2,"message":"1032","line":24,"column":5,"nodeType":"982","messageId":"983","endLine":24,"endColumn":26},{"ruleId":"989","severity":1,"message":"990","line":33,"column":64,"nodeType":"991","messageId":"992","endLine":33,"endColumn":67,"suggestions":"1033"},{"ruleId":"989","severity":1,"message":"990","line":33,"column":83,"nodeType":"991","messageId":"992","endLine":33,"endColumn":86,"suggestions":"1034"},{"ruleId":"980","severity":2,"message":"1035","line":43,"column":5,"nodeType":"982","messageId":"983","endLine":43,"endColumn":18},{"ruleId":"989","severity":1,"message":"990","line":51,"column":35,"nodeType":"991","messageId":"992","endLine":51,"endColumn":38,"suggestions":"1036"},{"ruleId":"1028","severity":1,"message":"1037","line":65,"column":3,"nodeType":"982","messageId":"1030","endLine":65,"endColumn":6},{"ruleId":"980","severity":2,"message":"1038","line":81,"column":5,"nodeType":"982","messageId":"983","endLine":81,"endColumn":29},{"ruleId":"989","severity":1,"message":"990","line":90,"column":67,"nodeType":"991","messageId":"992","endLine":90,"endColumn":70,"suggestions":"1039"},{"ruleId":"989","severity":1,"message":"990","line":90,"column":86,"nodeType":"991","messageId":"992","endLine":90,"endColumn":89,"suggestions":"1040"},{"ruleId":"980","severity":2,"message":"1041","line":19,"column":5,"nodeType":"982","messageId":"983","endLine":19,"endColumn":19},{"ruleId":"989","severity":1,"message":"990","line":28,"column":81,"nodeType":"991","messageId":"992","endLine":28,"endColumn":84,"suggestions":"1042"},{"ruleId":"989","severity":1,"message":"990","line":85,"column":31,"nodeType":"991","messageId":"992","endLine":85,"endColumn":34,"suggestions":"1043"},{"ruleId":"989","severity":1,"message":"990","line":106,"column":37,"nodeType":"991","messageId":"992","endLine":106,"endColumn":40,"suggestions":"1044"},{"ruleId":"989","severity":1,"message":"990","line":38,"column":30,"nodeType":"991","messageId":"992","endLine":38,"endColumn":33,"suggestions":"1045"},{"ruleId":"989","severity":1,"message":"990","line":51,"column":48,"nodeType":"991","messageId":"992","endLine":51,"endColumn":51,"suggestions":"1046"},{"ruleId":"989","severity":1,"message":"990","line":108,"column":19,"nodeType":"991","messageId":"992","endLine":108,"endColumn":22,"suggestions":"1047"},{"ruleId":"986","severity":1,"message":"1048","line":211,"column":11,"nodeType":"982","messageId":"988","endLine":211,"endColumn":34},{"ruleId":"989","severity":1,"message":"990","line":254,"column":31,"nodeType":"991","messageId":"992","endLine":254,"endColumn":34,"suggestions":"1049"},{"ruleId":"986","severity":1,"message":"1050","line":325,"column":16,"nodeType":"982","messageId":"988","endLine":325,"endColumn":17},{"ruleId":"989","severity":1,"message":"990","line":376,"column":39,"nodeType":"991","messageId":"992","endLine":376,"endColumn":42,"suggestions":"1051"},{"ruleId":"989","severity":1,"message":"990","line":388,"column":67,"nodeType":"991","messageId":"992","endLine":388,"endColumn":70,"suggestions":"1052"},{"ruleId":"1053","severity":2,"message":"1054","line":395,"column":5,"nodeType":"1055","messageId":"1056","endLine":419,"endColumn":6},{"ruleId":"1057","severity":2,"message":"1058","line":397,"column":9,"nodeType":"1059","messageId":"1060","endLine":397,"endColumn":82},{"ruleId":"989","severity":1,"message":"990","line":447,"column":48,"nodeType":"991","messageId":"992","endLine":447,"endColumn":51,"suggestions":"1061"},{"ruleId":"986","severity":1,"message":"1062","line":466,"column":11,"nodeType":"982","messageId":"988","endLine":466,"endColumn":19},{"ruleId":"986","severity":1,"message":"1063","line":776,"column":35,"nodeType":"982","messageId":"988","endLine":776,"endColumn":43},{"ruleId":"1064","severity":1,"message":"1065","line":882,"column":43,"nodeType":"1066","messageId":"1067","endLine":882,"endColumn":108},{"ruleId":"1028","severity":1,"message":"1068","line":882,"column":50,"nodeType":"982","messageId":"1030","endLine":882,"endColumn":56},{"ruleId":"1064","severity":1,"message":"1065","line":883,"column":43,"nodeType":"1066","messageId":"1067","endLine":883,"endColumn":84},{"ruleId":"1028","severity":1,"message":"1068","line":883,"column":50,"nodeType":"982","messageId":"1030","endLine":883,"endColumn":56},{"ruleId":"1011","severity":2,"message":"1069","line":923,"column":32,"nodeType":"982","messageId":"1013","endLine":923,"endColumn":53},{"ruleId":"1011","severity":2,"message":"1069","line":926,"column":40,"nodeType":"982","messageId":"1013","endLine":926,"endColumn":61},{"ruleId":"1011","severity":2,"message":"1069","line":929,"column":18,"nodeType":"982","messageId":"1013","endLine":929,"endColumn":39},{"ruleId":"1011","severity":2,"message":"1069","line":930,"column":32,"nodeType":"982","messageId":"1013","endLine":930,"endColumn":53},{"ruleId":"989","severity":1,"message":"990","line":933,"column":62,"nodeType":"991","messageId":"992","endLine":933,"endColumn":65,"suggestions":"1070"},{"ruleId":"1071","severity":2,"message":"1072","line":957,"column":73,"nodeType":"1073","messageId":"1074","endLine":957,"endColumn":74},{"ruleId":"986","severity":1,"message":"1062","line":972,"column":13,"nodeType":"982","messageId":"988","endLine":972,"endColumn":21},{"ruleId":"986","severity":1,"message":"1063","line":75,"column":89,"nodeType":"982","messageId":"988","endLine":75,"endColumn":90},{"ruleId":"1075","severity":2,"message":"1076","line":141,"column":13,"nodeType":"982","messageId":"1077","endLine":141,"endColumn":17},{"ruleId":"1000","severity":1,"message":"1001","line":144,"column":22,"nodeType":"1002","messageId":"1003","endLine":144,"endColumn":31},{"ruleId":"1078","severity":1,"message":"1079","line":158,"column":13,"nodeType":"1080","messageId":"1081","endLine":158,"endColumn":26},{"ruleId":"1078","severity":1,"message":"1079","line":160,"column":13,"nodeType":"1080","messageId":"1081","endLine":160,"endColumn":26},{"ruleId":"986","severity":1,"message":"1022","line":79,"column":66,"nodeType":"982","messageId":"988","endLine":79,"endColumn":68},{"ruleId":"980","severity":2,"message":"1082","line":87,"column":22,"nodeType":"982","messageId":"983","endLine":87,"endColumn":35},{"ruleId":"980","severity":2,"message":"1083","line":95,"column":23,"nodeType":"982","messageId":"983","endLine":95,"endColumn":37},{"ruleId":"1084","severity":2,"message":"1085","line":124,"column":9,"nodeType":"1086","messageId":"1087","endLine":124,"endColumn":75},{"ruleId":"1084","severity":2,"message":"1085","line":192,"column":24,"nodeType":"1086","messageId":"1087","endLine":192,"endColumn":38},{"ruleId":"1000","severity":1,"message":"1001","line":202,"column":24,"nodeType":"1002","messageId":"1003","endLine":202,"endColumn":33},{"ruleId":"989","severity":1,"message":"990","line":218,"column":63,"nodeType":"991","messageId":"992","endLine":218,"endColumn":66,"suggestions":"1088"},{"ruleId":"989","severity":1,"message":"990","line":221,"column":65,"nodeType":"991","messageId":"992","endLine":221,"endColumn":68,"suggestions":"1089"},{"ruleId":"1090","severity":2,"message":"1091","line":302,"column":94,"nodeType":"1066","messageId":"1092","endLine":302,"endColumn":150},{"ruleId":"1084","severity":2,"message":"1085","line":330,"column":25,"nodeType":"1086","messageId":"1087","endLine":330,"endColumn":55},{"ruleId":"1084","severity":2,"message":"1085","line":354,"column":9,"nodeType":"1086","messageId":"1087","endLine":354,"endColumn":36},{"ruleId":"1093","severity":2,"message":"1094","line":500,"column":31,"nodeType":"1066","messageId":"1095","endLine":500,"endColumn":33},{"ruleId":"1064","severity":1,"message":"1065","line":557,"column":57,"nodeType":"1066","messageId":"1067","endLine":557,"endColumn":90},{"ruleId":"1028","severity":1,"message":"1096","line":557,"column":67,"nodeType":"982","messageId":"1030","endLine":557,"endColumn":70},{"ruleId":"1028","severity":1,"message":"1097","line":583,"column":11,"nodeType":"982","messageId":"1030","endLine":583,"endColumn":15},{"ruleId":"1028","severity":1,"message":"1097","line":589,"column":15,"nodeType":"982","messageId":"1030","endLine":589,"endColumn":19},{"ruleId":"1028","severity":1,"message":"1098","line":639,"column":7,"nodeType":"982","messageId":"1030","endLine":639,"endColumn":9},{"ruleId":"1064","severity":1,"message":"1065","line":663,"column":45,"nodeType":"1066","messageId":"1067","endLine":663,"endColumn":96},{"ruleId":"1028","severity":1,"message":"1096","line":663,"column":56,"nodeType":"982","messageId":"1030","endLine":663,"endColumn":59},{"ruleId":"1099","severity":2,"message":"1100","line":8,"column":10,"nodeType":"982","endLine":8,"endColumn":18},{"ruleId":"1101","severity":2,"message":"1102","line":127,"column":11,"nodeType":"982","messageId":"1103","endLine":127,"endColumn":22},{"ruleId":"1075","severity":2,"message":"1076","line":127,"column":11,"nodeType":"982","messageId":"1077","endLine":127,"endColumn":22},{"ruleId":"1000","severity":1,"message":"1001","line":130,"column":25,"nodeType":"1002","messageId":"1003","endLine":130,"endColumn":34},{"ruleId":"1000","severity":1,"message":"1001","line":139,"column":13,"nodeType":"1002","messageId":"1003","endLine":139,"endColumn":22},{"ruleId":"986","severity":1,"message":"1104","line":167,"column":58,"nodeType":"982","messageId":"988","endLine":167,"endColumn":71},{"ruleId":"1028","severity":1,"message":"1105","line":176,"column":7,"nodeType":"982","messageId":"1030","endLine":176,"endColumn":11},{"ruleId":"1028","severity":1,"message":"1105","line":180,"column":7,"nodeType":"982","messageId":"1030","endLine":180,"endColumn":11},{"ruleId":"1028","severity":1,"message":"1105","line":194,"column":5,"nodeType":"982","messageId":"1030","endLine":194,"endColumn":9},{"ruleId":"986","severity":1,"message":"1106","line":213,"column":24,"nodeType":"982","messageId":"988","endLine":213,"endColumn":44},{"ruleId":"986","severity":1,"message":"1107","line":213,"column":46,"nodeType":"982","messageId":"988","endLine":213,"endColumn":58},{"ruleId":"986","severity":1,"message":"1104","line":213,"column":60,"nodeType":"982","messageId":"988","endLine":213,"endColumn":73},{"ruleId":"1011","severity":2,"message":"1108","line":219,"column":43,"nodeType":"982","messageId":"1013","endLine":219,"endColumn":61},{"ruleId":"986","severity":1,"message":"1106","line":249,"column":24,"nodeType":"982","messageId":"988","endLine":249,"endColumn":44},{"ruleId":"986","severity":1,"message":"1107","line":249,"column":46,"nodeType":"982","messageId":"988","endLine":249,"endColumn":58},{"ruleId":"986","severity":1,"message":"1104","line":249,"column":60,"nodeType":"982","messageId":"988","endLine":249,"endColumn":73},{"ruleId":"980","severity":2,"message":"1109","line":157,"column":22,"nodeType":"982","messageId":"983","endLine":157,"endColumn":31},{"ruleId":"989","severity":1,"message":"990","line":170,"column":13,"nodeType":"991","messageId":"992","endLine":170,"endColumn":16,"suggestions":"1110"},{"ruleId":"1075","severity":2,"message":"1076","line":187,"column":11,"nodeType":"982","messageId":"1077","endLine":187,"endColumn":15},{"ruleId":"1111","severity":2,"message":"1112","line":260,"column":11,"nodeType":"1113","messageId":"1114","endLine":274,"endColumn":12},{"ruleId":"986","severity":1,"message":"1115","line":299,"column":37,"nodeType":"982","messageId":"988","endLine":299,"endColumn":78},{"ruleId":"1028","severity":1,"message":"1116","line":301,"column":7,"nodeType":"982","messageId":"1117","endLine":301,"endColumn":12},{"ruleId":"989","severity":1,"message":"990","line":336,"column":15,"nodeType":"991","messageId":"992","endLine":336,"endColumn":18,"suggestions":"1118"},{"ruleId":"989","severity":1,"message":"990","line":399,"column":15,"nodeType":"991","messageId":"992","endLine":399,"endColumn":18,"suggestions":"1119"},{"ruleId":"1028","severity":1,"message":"1105","line":35,"column":5,"nodeType":"982","messageId":"1030","endLine":35,"endColumn":9},{"ruleId":"980","severity":2,"message":"1120","line":15,"column":26,"nodeType":"982","messageId":"983","endLine":15,"endColumn":39},{"ruleId":"1075","severity":2,"message":"1076","line":38,"column":11,"nodeType":"982","messageId":"1077","endLine":38,"endColumn":15},{"ruleId":"986","severity":1,"message":"1121","line":50,"column":11,"nodeType":"982","messageId":"988","endLine":50,"endColumn":15},{"ruleId":"1122","severity":2,"message":"1123","line":6,"column":15,"nodeType":"1124","endLine":6,"endColumn":31},{"ruleId":"1122","severity":2,"message":"1125","line":8,"column":15,"nodeType":"1124","endLine":8,"endColumn":27},{"ruleId":"989","severity":1,"message":"990","line":25,"column":45,"nodeType":"991","messageId":"992","endLine":25,"endColumn":48,"suggestions":"1126"},{"ruleId":"989","severity":1,"message":"990","line":27,"column":47,"nodeType":"991","messageId":"992","endLine":27,"endColumn":50,"suggestions":"1127"},{"ruleId":"989","severity":1,"message":"990","line":29,"column":45,"nodeType":"991","messageId":"992","endLine":29,"endColumn":48,"suggestions":"1128"},{"ruleId":"986","severity":1,"message":"1129","line":35,"column":65,"nodeType":"982","messageId":"988","endLine":35,"endColumn":89},{"ruleId":"986","severity":1,"message":"1130","line":35,"column":91,"nodeType":"982","messageId":"988","endLine":35,"endColumn":109},{"ruleId":"1075","severity":2,"message":"1076","line":36,"column":11,"nodeType":"982","messageId":"1077","endLine":36,"endColumn":15},{"ruleId":"1000","severity":1,"message":"1001","line":58,"column":20,"nodeType":"1002","messageId":"1003","endLine":58,"endColumn":29},{"ruleId":"1075","severity":2,"message":"1076","line":59,"column":15,"nodeType":"982","messageId":"1077","endLine":59,"endColumn":25},{"ruleId":"986","severity":1,"message":"1131","line":48,"column":11,"nodeType":"982","messageId":"988","endLine":48,"endColumn":25},{"ruleId":"989","severity":1,"message":"990","line":63,"column":50,"nodeType":"991","messageId":"992","endLine":63,"endColumn":53,"suggestions":"1132"},{"ruleId":"1075","severity":2,"message":"1076","line":85,"column":11,"nodeType":"982","messageId":"1077","endLine":85,"endColumn":15},{"ruleId":"1011","severity":2,"message":"1133","line":86,"column":73,"nodeType":"982","messageId":"1013","endLine":86,"endColumn":90},{"ruleId":"1000","severity":1,"message":"1001","line":117,"column":28,"nodeType":"1002","messageId":"1003","endLine":117,"endColumn":37},{"ruleId":"1000","severity":1,"message":"1001","line":141,"column":22,"nodeType":"1002","messageId":"1003","endLine":141,"endColumn":31},{"ruleId":"1000","severity":1,"message":"1001","line":144,"column":26,"nodeType":"1002","messageId":"1003","endLine":144,"endColumn":35},{"ruleId":"1000","severity":1,"message":"1001","line":147,"column":25,"nodeType":"1002","messageId":"1003","endLine":147,"endColumn":34},{"ruleId":"1000","severity":1,"message":"1001","line":150,"column":24,"nodeType":"1002","messageId":"1003","endLine":150,"endColumn":33},{"ruleId":"980","severity":2,"message":"1134","line":181,"column":5,"nodeType":"982","messageId":"983","endLine":181,"endColumn":17},{"ruleId":"1084","severity":2,"message":"1085","line":224,"column":28,"nodeType":"1086","messageId":"1087","endLine":224,"endColumn":59},{"ruleId":"1084","severity":2,"message":"1085","line":234,"column":32,"nodeType":"1086","messageId":"1087","endLine":234,"endColumn":63},{"ruleId":"986","severity":1,"message":"1135","line":245,"column":55,"nodeType":"982","messageId":"988","endLine":245,"endColumn":61},{"ruleId":"980","severity":2,"message":"1136","line":301,"column":23,"nodeType":"982","messageId":"983","endLine":301,"endColumn":43},{"ruleId":"980","severity":2,"message":"1137","line":320,"column":17,"nodeType":"982","messageId":"983","endLine":320,"endColumn":28},{"ruleId":"1000","severity":1,"message":"1001","line":367,"column":15,"nodeType":"1002","messageId":"1003","endLine":367,"endColumn":24},{"ruleId":"1138","severity":2,"message":"1139","line":387,"column":76,"nodeType":"982","messageId":"1140","endLine":387,"endColumn":81},{"ruleId":"1000","severity":1,"message":"1001","line":418,"column":54,"nodeType":"1002","messageId":"1003","endLine":418,"endColumn":63},{"ruleId":"986","severity":1,"message":"1050","line":418,"column":64,"nodeType":"982","messageId":"988","endLine":418,"endColumn":65},{"ruleId":"989","severity":1,"message":"990","line":517,"column":50,"nodeType":"991","messageId":"992","endLine":517,"endColumn":53,"suggestions":"1141"},{"ruleId":"1075","severity":2,"message":"1076","line":525,"column":11,"nodeType":"982","messageId":"1077","endLine":525,"endColumn":15},{"ruleId":"1000","severity":1,"message":"1001","line":528,"column":36,"nodeType":"1002","messageId":"1003","endLine":528,"endColumn":45},{"ruleId":"1138","severity":2,"message":"1139","line":549,"column":73,"nodeType":"982","messageId":"1140","endLine":549,"endColumn":78},{"ruleId":"989","severity":1,"message":"990","line":566,"column":64,"nodeType":"991","messageId":"992","endLine":566,"endColumn":67,"suggestions":"1142"},{"ruleId":"989","severity":1,"message":"990","line":583,"column":64,"nodeType":"991","messageId":"992","endLine":583,"endColumn":67,"suggestions":"1143"},{"ruleId":"989","severity":1,"message":"990","line":764,"column":15,"nodeType":"991","messageId":"992","endLine":764,"endColumn":18,"suggestions":"1144"},{"ruleId":"1145","severity":2,"message":"1146","line":69,"column":35,"nodeType":"982","messageId":"1147","endLine":69,"endColumn":40},{"ruleId":"986","severity":1,"message":"1148","line":82,"column":58,"nodeType":"982","messageId":"988","endLine":82,"endColumn":62},{"ruleId":"986","severity":1,"message":"1149","line":82,"column":64,"nodeType":"982","messageId":"988","endLine":82,"endColumn":69},{"ruleId":"989","severity":1,"message":"990","line":90,"column":21,"nodeType":"991","messageId":"992","endLine":90,"endColumn":24,"suggestions":"1150"},{"ruleId":"989","severity":1,"message":"990","line":91,"column":33,"nodeType":"991","messageId":"992","endLine":91,"endColumn":36,"suggestions":"1151"},{"ruleId":"989","severity":1,"message":"990","line":142,"column":47,"nodeType":"991","messageId":"992","endLine":142,"endColumn":50,"suggestions":"1152"},{"ruleId":"1153","severity":1,"message":"1154","line":156,"column":53,"nodeType":"1155","messageId":"1156","endLine":156,"endColumn":65,"suggestions":"1157"},{"ruleId":"989","severity":1,"message":"990","line":26,"column":39,"nodeType":"991","messageId":"992","endLine":26,"endColumn":42,"suggestions":"1158"},{"ruleId":"986","severity":1,"message":"1129","line":130,"column":26,"nodeType":"982","messageId":"988","endLine":130,"endColumn":50},{"ruleId":"986","severity":1,"message":"1130","line":130,"column":52,"nodeType":"982","messageId":"988","endLine":130,"endColumn":70},{"ruleId":"1000","severity":1,"message":"1001","line":139,"column":62,"nodeType":"1002","messageId":"1003","endLine":139,"endColumn":71},{"ruleId":"989","severity":1,"message":"990","line":160,"column":47,"nodeType":"991","messageId":"992","endLine":160,"endColumn":50,"suggestions":"1159"},{"ruleId":"989","severity":1,"message":"990","line":160,"column":86,"nodeType":"991","messageId":"992","endLine":160,"endColumn":89,"suggestions":"1160"},{"ruleId":"989","severity":1,"message":"990","line":162,"column":32,"nodeType":"991","messageId":"992","endLine":162,"endColumn":35,"suggestions":"1161"},{"ruleId":"989","severity":1,"message":"990","line":164,"column":28,"nodeType":"991","messageId":"992","endLine":164,"endColumn":31,"suggestions":"1162"},{"ruleId":"989","severity":1,"message":"990","line":164,"column":67,"nodeType":"991","messageId":"992","endLine":164,"endColumn":70,"suggestions":"1163"},{"ruleId":"989","severity":1,"message":"990","line":166,"column":102,"nodeType":"991","messageId":"992","endLine":166,"endColumn":105,"suggestions":"1164"},{"ruleId":"989","severity":1,"message":"990","line":166,"column":129,"nodeType":"991","messageId":"992","endLine":166,"endColumn":132,"suggestions":"1165"},{"ruleId":"1166","severity":2,"message":"1167","line":167,"column":34,"nodeType":"1168","messageId":"1169","endLine":167,"endColumn":42},{"ruleId":"989","severity":1,"message":"990","line":188,"column":38,"nodeType":"991","messageId":"992","endLine":188,"endColumn":41,"suggestions":"1170"},{"ruleId":"1099","severity":2,"message":"1171","line":5,"column":10,"nodeType":"982","endLine":5,"endColumn":23},{"ruleId":"986","severity":1,"message":"1006","line":148,"column":47,"nodeType":"982","messageId":"988","endLine":148,"endColumn":52},{"ruleId":"986","severity":1,"message":"1172","line":181,"column":45,"nodeType":"982","messageId":"988","endLine":181,"endColumn":63},{"ruleId":"986","severity":1,"message":"1006","line":288,"column":46,"nodeType":"982","messageId":"988","endLine":288,"endColumn":51},{"ruleId":"986","severity":1,"message":"1173","line":591,"column":11,"nodeType":"982","messageId":"988","endLine":591,"endColumn":18},{"ruleId":"1084","severity":2,"message":"1085","line":809,"column":25,"nodeType":"1086","messageId":"1087","endLine":809,"endColumn":82},{"ruleId":"1084","severity":2,"message":"1085","line":819,"column":27,"nodeType":"1086","messageId":"1087","endLine":819,"endColumn":132},{"ruleId":"989","severity":1,"message":"990","line":970,"column":40,"nodeType":"991","messageId":"992","endLine":970,"endColumn":43,"suggestions":"1174"},{"ruleId":"989","severity":1,"message":"990","line":1007,"column":39,"nodeType":"991","messageId":"992","endLine":1007,"endColumn":42,"suggestions":"1175"},{"ruleId":"1176","severity":2,"message":"1177","line":67,"column":42,"nodeType":"982","messageId":"1178","endLine":67,"endColumn":48},{"ruleId":"989","severity":1,"message":"990","line":78,"column":26,"nodeType":"991","messageId":"992","endLine":78,"endColumn":29,"suggestions":"1179"},{"ruleId":"986","severity":1,"message":"1180","line":229,"column":17,"nodeType":"982","messageId":"988","endLine":229,"endColumn":18},{"ruleId":"1028","severity":1,"message":"1181","line":417,"column":7,"nodeType":"982","messageId":"1030","endLine":417,"endColumn":12},{"ruleId":"1111","severity":2,"message":"1112","line":477,"column":9,"nodeType":"1113","messageId":"1114","endLine":528,"endColumn":10},{"ruleId":"986","severity":1,"message":"1182","line":567,"column":13,"nodeType":"982","messageId":"988","endLine":567,"endColumn":27},{"ruleId":"1075","severity":2,"message":"1076","line":675,"column":11,"nodeType":"982","messageId":"1077","endLine":675,"endColumn":15},{"ruleId":"1000","severity":1,"message":"1001","line":678,"column":20,"nodeType":"1002","messageId":"1003","endLine":678,"endColumn":29},{"ruleId":"1075","severity":2,"message":"1076","line":679,"column":15,"nodeType":"982","messageId":"1077","endLine":679,"endColumn":22},{"ruleId":"1000","severity":1,"message":"1001","line":685,"column":30,"nodeType":"1002","messageId":"1003","endLine":685,"endColumn":39},{"ruleId":"1075","severity":2,"message":"1076","line":686,"column":13,"nodeType":"982","messageId":"1077","endLine":686,"endColumn":20},{"ruleId":"1000","severity":1,"message":"1001","line":691,"column":35,"nodeType":"1002","messageId":"1003","endLine":691,"endColumn":44},{"ruleId":"1075","severity":2,"message":"1076","line":692,"column":13,"nodeType":"982","messageId":"1077","endLine":692,"endColumn":20},{"ruleId":"1000","severity":1,"message":"1001","line":696,"column":33,"nodeType":"1002","messageId":"1003","endLine":696,"endColumn":42},{"ruleId":"1075","severity":2,"message":"1076","line":747,"column":11,"nodeType":"982","messageId":"1077","endLine":747,"endColumn":22},{"ruleId":"1000","severity":1,"message":"1001","line":750,"column":20,"nodeType":"1002","messageId":"1003","endLine":750,"endColumn":29},{"ruleId":"986","severity":1,"message":"1006","line":807,"column":28,"nodeType":"982","messageId":"988","endLine":807,"endColumn":33},{"ruleId":"1138","severity":2,"message":"1139","line":828,"column":36,"nodeType":"982","messageId":"1140","endLine":828,"endColumn":41},{"ruleId":"1138","severity":2,"message":"1139","line":832,"column":36,"nodeType":"982","messageId":"1140","endLine":832,"endColumn":41},{"ruleId":"1075","severity":2,"message":"1076","line":865,"column":13,"nodeType":"982","messageId":"1077","endLine":865,"endColumn":17},{"ruleId":"1000","severity":1,"message":"1001","line":866,"column":27,"nodeType":"1002","messageId":"1003","endLine":866,"endColumn":36},{"ruleId":"1075","severity":2,"message":"1076","line":867,"column":15,"nodeType":"982","messageId":"1077","endLine":867,"endColumn":22},{"ruleId":"986","severity":1,"message":"1183","line":876,"column":24,"nodeType":"982","messageId":"988","endLine":876,"endColumn":28},{"ruleId":"986","severity":1,"message":"1184","line":876,"column":30,"nodeType":"982","messageId":"988","endLine":876,"endColumn":35},{"ruleId":"1176","severity":2,"message":"1177","line":12,"column":32,"nodeType":"982","messageId":"1178","endLine":12,"endColumn":38},{"ruleId":"1185","severity":2,"message":"1186","line":9,"column":9,"nodeType":null,"messageId":"1187","endLine":9,"endColumn":20,"fix":"1188"},{"ruleId":"1176","severity":2,"message":"1177","line":18,"column":32,"nodeType":"982","messageId":"1178","endLine":18,"endColumn":38},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":31,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":32,"suggestions":"1193"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":56,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":57,"suggestions":"1194"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":73,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":74,"suggestions":"1195"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":80,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":81,"suggestions":"1196"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":85,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":86,"suggestions":"1197"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":87,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":88,"suggestions":"1198"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":92,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":93,"suggestions":"1199"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":101,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":102,"suggestions":"1200"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":118,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":119,"suggestions":"1201"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":127,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":128,"suggestions":"1202"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":132,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":133,"suggestions":"1203"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":134,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":135,"suggestions":"1204"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":139,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":140,"suggestions":"1205"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":146,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":147,"suggestions":"1206"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":163,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":164,"suggestions":"1207"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":170,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":171,"suggestions":"1208"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":175,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":176,"suggestions":"1209"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":177,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":178,"suggestions":"1210"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":182,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":183,"suggestions":"1211"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":197,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":198,"suggestions":"1212"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":215,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":216,"suggestions":"1213"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":230,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":231,"suggestions":"1214"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":243,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":244,"suggestions":"1215"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":258,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":259,"suggestions":"1216"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":264,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":265,"suggestions":"1217"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":266,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":267,"suggestions":"1218"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":275,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":276,"suggestions":"1219"},{"ruleId":"1189","severity":2,"message":"1190","line":386,"column":290,"nodeType":"1191","messageId":"1192","endLine":386,"endColumn":291,"suggestions":"1220"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":30,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":31,"suggestions":"1221"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":57,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":58,"suggestions":"1222"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":76,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":77,"suggestions":"1223"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":83,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":84,"suggestions":"1224"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":88,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":89,"suggestions":"1225"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":90,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":91,"suggestions":"1226"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":97,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":98,"suggestions":"1227"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":110,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":111,"suggestions":"1228"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":129,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":130,"suggestions":"1229"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":138,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":139,"suggestions":"1230"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":143,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":144,"suggestions":"1231"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":145,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":146,"suggestions":"1232"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":152,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":153,"suggestions":"1233"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":163,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":164,"suggestions":"1234"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":182,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":183,"suggestions":"1235"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":189,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":190,"suggestions":"1236"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":194,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":195,"suggestions":"1237"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":196,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":197,"suggestions":"1238"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":203,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":204,"suggestions":"1239"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":222,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":223,"suggestions":"1240"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":242,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":243,"suggestions":"1241"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":257,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":258,"suggestions":"1242"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":270,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":271,"suggestions":"1243"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":285,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":286,"suggestions":"1244"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":291,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":292,"suggestions":"1245"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":293,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":294,"suggestions":"1246"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":302,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":303,"suggestions":"1247"},{"ruleId":"1189","severity":2,"message":"1190","line":390,"column":317,"nodeType":"1191","messageId":"1192","endLine":390,"endColumn":318,"suggestions":"1248"},{"ruleId":"1176","severity":2,"message":"1177","line":24,"column":32,"nodeType":"982","messageId":"1178","endLine":24,"endColumn":38},{"ruleId":"1028","severity":1,"message":"1249","line":40,"column":7,"nodeType":"982","messageId":"1030","endLine":40,"endColumn":11},{"ruleId":"986","severity":1,"message":"1250","line":57,"column":57,"nodeType":"982","messageId":"988","endLine":57,"endColumn":61},{"ruleId":"986","severity":1,"message":"1006","line":61,"column":28,"nodeType":"982","messageId":"988","endLine":61,"endColumn":33},{"ruleId":"1138","severity":2,"message":"1139","line":70,"column":36,"nodeType":"982","messageId":"1140","endLine":70,"endColumn":41},{"ruleId":"1138","severity":2,"message":"1139","line":73,"column":36,"nodeType":"982","messageId":"1140","endLine":73,"endColumn":41},{"ruleId":"1075","severity":2,"message":"1076","line":109,"column":15,"nodeType":"982","messageId":"1077","endLine":109,"endColumn":19},{"ruleId":"1000","severity":1,"message":"1001","line":110,"column":29,"nodeType":"1002","messageId":"1003","endLine":110,"endColumn":38},{"ruleId":"1075","severity":2,"message":"1076","line":111,"column":17,"nodeType":"982","messageId":"1077","endLine":111,"endColumn":24},{"ruleId":"1176","severity":2,"message":"1177","line":21,"column":32,"nodeType":"982","messageId":"1178","endLine":21,"endColumn":38},{"ruleId":"989","severity":1,"message":"990","line":194,"column":26,"nodeType":"991","messageId":"992","endLine":194,"endColumn":29,"suggestions":"1251"},{"ruleId":"1099","severity":2,"message":"1252","line":3,"column":10,"nodeType":"982","endLine":3,"endColumn":21},{"ruleId":"989","severity":1,"message":"990","line":129,"column":28,"nodeType":"991","messageId":"992","endLine":129,"endColumn":31,"suggestions":"1253"},{"ruleId":"986","severity":1,"message":"1254","line":3,"column":28,"nodeType":"982","messageId":"988","endLine":3,"endColumn":32},{"ruleId":"1176","severity":2,"message":"1177","line":27,"column":32,"nodeType":"982","messageId":"1178","endLine":27,"endColumn":38},{"ruleId":"1145","severity":2,"message":"1146","line":59,"column":35,"nodeType":"982","messageId":"1147","endLine":59,"endColumn":40},{"ruleId":"986","severity":1,"message":"1180","line":64,"column":52,"nodeType":"982","messageId":"988","endLine":64,"endColumn":53},{"ruleId":"1028","severity":1,"message":"1255","line":81,"column":5,"nodeType":"982","messageId":"1117","endLine":81,"endColumn":9},{"ruleId":"989","severity":1,"message":"990","line":485,"column":18,"nodeType":"991","messageId":"992","endLine":485,"endColumn":21,"suggestions":"1256"},{"ruleId":"989","severity":1,"message":"990","line":809,"column":35,"nodeType":"991","messageId":"992","endLine":809,"endColumn":38,"suggestions":"1257"},{"ruleId":"989","severity":1,"message":"990","line":809,"column":88,"nodeType":"991","messageId":"992","endLine":809,"endColumn":91,"suggestions":"1258"},{"ruleId":"989","severity":1,"message":"990","line":811,"column":21,"nodeType":"991","messageId":"992","endLine":811,"endColumn":24,"suggestions":"1259"},{"ruleId":"989","severity":1,"message":"990","line":816,"column":46,"nodeType":"991","messageId":"992","endLine":816,"endColumn":49,"suggestions":"1260"},{"ruleId":"989","severity":1,"message":"990","line":828,"column":38,"nodeType":"991","messageId":"992","endLine":828,"endColumn":41,"suggestions":"1261"},{"ruleId":"989","severity":1,"message":"990","line":828,"column":92,"nodeType":"991","messageId":"992","endLine":828,"endColumn":95,"suggestions":"1262"},{"ruleId":"989","severity":1,"message":"990","line":846,"column":54,"nodeType":"991","messageId":"992","endLine":846,"endColumn":57,"suggestions":"1263"},{"ruleId":"989","severity":1,"message":"990","line":846,"column":84,"nodeType":"991","messageId":"992","endLine":846,"endColumn":87,"suggestions":"1264"},{"ruleId":"986","severity":1,"message":"1265","line":38,"column":44,"nodeType":"982","messageId":"988","endLine":38,"endColumn":52},{"ruleId":"1145","severity":2,"message":"1146","line":39,"column":35,"nodeType":"982","messageId":"1147","endLine":39,"endColumn":40},{"ruleId":"986","severity":1,"message":"1148","line":48,"column":58,"nodeType":"982","messageId":"988","endLine":48,"endColumn":62},{"ruleId":"986","severity":1,"message":"1149","line":48,"column":64,"nodeType":"982","messageId":"988","endLine":48,"endColumn":69},{"ruleId":"1075","severity":2,"message":"1076","line":162,"column":11,"nodeType":"982","messageId":"1077","endLine":162,"endColumn":15},{"ruleId":"1000","severity":1,"message":"1001","line":165,"column":20,"nodeType":"1002","messageId":"1003","endLine":165,"endColumn":29},{"ruleId":"1075","severity":2,"message":"1076","line":166,"column":15,"nodeType":"982","messageId":"1077","endLine":166,"endColumn":22},{"ruleId":"989","severity":1,"message":"990","line":209,"column":35,"nodeType":"991","messageId":"992","endLine":209,"endColumn":38,"suggestions":"1266"},{"ruleId":"989","severity":1,"message":"990","line":210,"column":37,"nodeType":"991","messageId":"992","endLine":210,"endColumn":40,"suggestions":"1267"},{"ruleId":"1075","severity":2,"message":"1076","line":249,"column":11,"nodeType":"982","messageId":"1077","endLine":249,"endColumn":15},{"ruleId":"1000","severity":1,"message":"1001","line":252,"column":30,"nodeType":"1002","messageId":"1003","endLine":252,"endColumn":39},{"ruleId":"1075","severity":2,"message":"1076","line":253,"column":13,"nodeType":"982","messageId":"1077","endLine":253,"endColumn":20},{"ruleId":"1000","severity":1,"message":"1001","line":258,"column":35,"nodeType":"1002","messageId":"1003","endLine":258,"endColumn":44},{"ruleId":"1075","severity":2,"message":"1076","line":259,"column":13,"nodeType":"982","messageId":"1077","endLine":259,"endColumn":20},{"ruleId":"1000","severity":1,"message":"1001","line":265,"column":33,"nodeType":"1002","messageId":"1003","endLine":265,"endColumn":42},{"ruleId":"986","severity":1,"message":"1006","line":330,"column":28,"nodeType":"982","messageId":"988","endLine":330,"endColumn":33},{"ruleId":"1138","severity":2,"message":"1139","line":342,"column":38,"nodeType":"982","messageId":"1140","endLine":342,"endColumn":43},{"ruleId":"1138","severity":2,"message":"1139","line":345,"column":38,"nodeType":"982","messageId":"1140","endLine":345,"endColumn":43},{"ruleId":"1138","severity":2,"message":"1139","line":366,"column":60,"nodeType":"982","messageId":"1140","endLine":366,"endColumn":65},{"ruleId":"1138","severity":2,"message":"1139","line":370,"column":39,"nodeType":"982","messageId":"1140","endLine":370,"endColumn":44},{"ruleId":"1075","severity":2,"message":"1076","line":399,"column":15,"nodeType":"982","messageId":"1077","endLine":399,"endColumn":19},{"ruleId":"1000","severity":1,"message":"1001","line":400,"column":29,"nodeType":"1002","messageId":"1003","endLine":400,"endColumn":38},{"ruleId":"1075","severity":2,"message":"1076","line":401,"column":17,"nodeType":"982","messageId":"1077","endLine":401,"endColumn":24},{"ruleId":"1064","severity":1,"message":"1065","line":409,"column":18,"nodeType":"1066","messageId":"1067","endLine":409,"endColumn":67},{"ruleId":"1028","severity":1,"message":"1268","line":409,"column":30,"nodeType":"982","messageId":"1030","endLine":409,"endColumn":34},{"ruleId":"986","severity":1,"message":"1184","line":413,"column":30,"nodeType":"982","messageId":"988","endLine":413,"endColumn":35},{"ruleId":"1028","severity":1,"message":"1181","line":459,"column":7,"nodeType":"982","messageId":"1030","endLine":459,"endColumn":12},{"ruleId":"1176","severity":2,"message":"1177","line":20,"column":32,"nodeType":"982","messageId":"1178","endLine":20,"endColumn":38},{"ruleId":"986","severity":1,"message":"1269","line":873,"column":13,"nodeType":"982","messageId":"988","endLine":873,"endColumn":19},{"ruleId":"989","severity":1,"message":"990","line":1031,"column":12,"nodeType":"991","messageId":"992","endLine":1031,"endColumn":15,"suggestions":"1270"},{"ruleId":"989","severity":1,"message":"990","line":1035,"column":9,"nodeType":"991","messageId":"992","endLine":1035,"endColumn":12,"suggestions":"1271"},{"ruleId":"989","severity":1,"message":"990","line":1037,"column":20,"nodeType":"991","messageId":"992","endLine":1037,"endColumn":23,"suggestions":"1272"},{"ruleId":"989","severity":1,"message":"990","line":1039,"column":14,"nodeType":"991","messageId":"992","endLine":1039,"endColumn":17,"suggestions":"1273"},{"ruleId":"989","severity":1,"message":"990","line":1041,"column":13,"nodeType":"991","messageId":"992","endLine":1041,"endColumn":16,"suggestions":"1274"},{"ruleId":"1176","severity":2,"message":"1177","line":1045,"column":32,"nodeType":"982","messageId":"1178","endLine":1045,"endColumn":38},{"ruleId":"989","severity":1,"message":"990","line":1052,"column":41,"nodeType":"991","messageId":"992","endLine":1052,"endColumn":44,"suggestions":"1275"},{"ruleId":"989","severity":1,"message":"990","line":1053,"column":41,"nodeType":"991","messageId":"992","endLine":1053,"endColumn":44,"suggestions":"1276"},{"ruleId":"989","severity":1,"message":"990","line":1068,"column":32,"nodeType":"991","messageId":"992","endLine":1068,"endColumn":35,"suggestions":"1277"},{"ruleId":"1278","severity":2,"message":"1279","line":1068,"column":36,"nodeType":"1280","messageId":"1060","endLine":1068,"endColumn":115},{"ruleId":"1281","severity":2,"message":"1282","line":1068,"column":36,"nodeType":"1280","messageId":"1283","endLine":1068,"endColumn":115},{"ruleId":"989","severity":1,"message":"990","line":1069,"column":55,"nodeType":"991","messageId":"992","endLine":1069,"endColumn":58,"suggestions":"1284"},{"ruleId":"1166","severity":2,"message":"1167","line":1070,"column":30,"nodeType":"1168","messageId":"1169","endLine":1070,"endColumn":41},{"ruleId":"989","severity":1,"message":"990","line":1081,"column":30,"nodeType":"991","messageId":"992","endLine":1081,"endColumn":33,"suggestions":"1285"},{"ruleId":"1278","severity":2,"message":"1279","line":1081,"column":34,"nodeType":"1280","messageId":"1060","endLine":1081,"endColumn":102},{"ruleId":"1281","severity":2,"message":"1282","line":1081,"column":34,"nodeType":"1280","messageId":"1283","endLine":1081,"endColumn":102},{"ruleId":"989","severity":1,"message":"990","line":1082,"column":50,"nodeType":"991","messageId":"992","endLine":1082,"endColumn":53,"suggestions":"1286"},{"ruleId":"1166","severity":2,"message":"1167","line":1083,"column":32,"nodeType":"1168","messageId":"1169","endLine":1083,"endColumn":41},{"ruleId":"986","severity":1,"message":"1287","line":1185,"column":19,"nodeType":"982","messageId":"988","endLine":1185,"endColumn":36},{"ruleId":"986","severity":1,"message":"1288","line":1,"column":31,"nodeType":"982","messageId":"988","endLine":1,"endColumn":40},{"ruleId":"986","severity":1,"message":"1288","line":1,"column":31,"nodeType":"982","messageId":"988","endLine":1,"endColumn":40},{"ruleId":"1028","severity":1,"message":"1289","line":30,"column":5,"nodeType":"982","messageId":"1030","endLine":30,"endColumn":11},{"ruleId":"989","severity":1,"message":"990","line":12,"column":22,"nodeType":"991","messageId":"992","endLine":12,"endColumn":25,"suggestions":"1290"},{"ruleId":"1291","severity":2,"message":"1292","line":15,"column":25,"nodeType":"1073","messageId":"1293","endLine":15,"endColumn":30},{"ruleId":"989","severity":1,"message":"990","line":41,"column":53,"nodeType":"991","messageId":"992","endLine":41,"endColumn":56,"suggestions":"1294"},{"ruleId":"989","severity":1,"message":"990","line":47,"column":56,"nodeType":"991","messageId":"992","endLine":47,"endColumn":59,"suggestions":"1295"},{"ruleId":"986","severity":1,"message":"1296","line":167,"column":13,"nodeType":"982","messageId":"988","endLine":167,"endColumn":21},{"ruleId":"1075","severity":2,"message":"1076","line":249,"column":11,"nodeType":"982","messageId":"1077","endLine":249,"endColumn":15},{"ruleId":"1000","severity":1,"message":"1297","line":260,"column":20,"nodeType":"1002","messageId":"1003","endLine":260,"endColumn":35},{"ruleId":"1011","severity":2,"message":"1298","line":263,"column":15,"nodeType":"982","messageId":"1013","endLine":263,"endColumn":35},{"ruleId":"986","severity":1,"message":"1299","line":286,"column":13,"nodeType":"982","messageId":"988","endLine":286,"endColumn":20},{"ruleId":"1028","severity":1,"message":"1300","line":291,"column":5,"nodeType":"982","messageId":"1030","endLine":291,"endColumn":15},{"ruleId":"989","severity":1,"message":"990","line":299,"column":46,"nodeType":"991","messageId":"992","endLine":299,"endColumn":49,"suggestions":"1301"},{"ruleId":"989","severity":1,"message":"990","line":300,"column":26,"nodeType":"991","messageId":"992","endLine":300,"endColumn":29,"suggestions":"1302"},{"ruleId":"989","severity":1,"message":"990","line":311,"column":41,"nodeType":"991","messageId":"992","endLine":311,"endColumn":44,"suggestions":"1303"},{"ruleId":"989","severity":1,"message":"990","line":495,"column":84,"nodeType":"991","messageId":"992","endLine":495,"endColumn":87,"suggestions":"1304"},{"ruleId":"986","severity":1,"message":"1006","line":520,"column":38,"nodeType":"982","messageId":"988","endLine":520,"endColumn":43},{"ruleId":"986","severity":1,"message":"1006","line":528,"column":38,"nodeType":"982","messageId":"988","endLine":528,"endColumn":43},{"ruleId":"986","severity":1,"message":"1006","line":535,"column":38,"nodeType":"982","messageId":"988","endLine":535,"endColumn":43},{"ruleId":"1064","severity":1,"message":"1065","line":23,"column":58,"nodeType":"1066","messageId":"1067","endLine":23,"endColumn":89},{"ruleId":"1028","severity":1,"message":"1096","line":23,"column":68,"nodeType":"982","messageId":"1030","endLine":23,"endColumn":71},{"ruleId":"1064","severity":1,"message":"1065","line":48,"column":28,"nodeType":"1066","messageId":"1067","endLine":48,"endColumn":59},{"ruleId":"1028","severity":1,"message":"1096","line":48,"column":38,"nodeType":"982","messageId":"1030","endLine":48,"endColumn":41},{"ruleId":"1064","severity":1,"message":"1065","line":77,"column":30,"nodeType":"1066","messageId":"1067","endLine":77,"endColumn":60},{"ruleId":"1028","severity":1,"message":"1096","line":77,"column":40,"nodeType":"982","messageId":"1030","endLine":77,"endColumn":43},{"ruleId":"989","severity":1,"message":"990","line":102,"column":18,"nodeType":"991","messageId":"992","endLine":102,"endColumn":21,"suggestions":"1305"},{"ruleId":"989","severity":1,"message":"990","line":230,"column":38,"nodeType":"991","messageId":"992","endLine":230,"endColumn":41,"suggestions":"1306"},{"ruleId":"989","severity":1,"message":"990","line":252,"column":29,"nodeType":"991","messageId":"992","endLine":252,"endColumn":32,"suggestions":"1307"},{"ruleId":"989","severity":1,"message":"990","line":273,"column":29,"nodeType":"991","messageId":"992","endLine":273,"endColumn":32,"suggestions":"1308"},{"ruleId":"989","severity":1,"message":"990","line":63,"column":72,"nodeType":"991","messageId":"992","endLine":63,"endColumn":75,"suggestions":"1309"},{"ruleId":"1028","severity":1,"message":"1310","line":70,"column":9,"nodeType":"982","messageId":"1117","endLine":70,"endColumn":15},{"ruleId":"1028","severity":1,"message":"1310","line":73,"column":9,"nodeType":"982","messageId":"1117","endLine":73,"endColumn":15},{"ruleId":"1028","severity":1,"message":"1310","line":80,"column":9,"nodeType":"982","messageId":"1117","endLine":80,"endColumn":15},{"ruleId":"1028","severity":1,"message":"1310","line":83,"column":9,"nodeType":"982","messageId":"1117","endLine":83,"endColumn":15},{"ruleId":"1028","severity":1,"message":"1311","line":100,"column":5,"nodeType":"982","messageId":"1030","endLine":100,"endColumn":11},{"ruleId":"1028","severity":1,"message":"1311","line":223,"column":10,"nodeType":"982","messageId":"1030","endLine":223,"endColumn":16},{"ruleId":"1028","severity":1,"message":"1311","line":240,"column":10,"nodeType":"982","messageId":"1030","endLine":240,"endColumn":16},{"ruleId":"1028","severity":1,"message":"1311","line":249,"column":10,"nodeType":"982","messageId":"1030","endLine":249,"endColumn":16},{"ruleId":"1185","severity":2,"message":"1312","line":376,"column":1,"nodeType":null,"messageId":"1313","endLine":377,"endColumn":1,"fix":"1314"},{"ruleId":"1028","severity":1,"message":"1311","line":410,"column":3,"nodeType":"982","messageId":"1030","endLine":410,"endColumn":9},{"ruleId":"1153","severity":1,"message":"1154","line":57,"column":35,"nodeType":"1155","messageId":"1156","endLine":57,"endColumn":54},{"ruleId":"1153","severity":1,"message":"1154","line":90,"column":35,"nodeType":"1155","messageId":"1156","endLine":90,"endColumn":54},{"ruleId":"1153","severity":1,"message":"1154","line":125,"column":33,"nodeType":"1155","messageId":"1156","endLine":125,"endColumn":52},{"ruleId":"1153","severity":1,"message":"1154","line":126,"column":75,"nodeType":"1155","messageId":"1156","endLine":126,"endColumn":94},{"ruleId":"1153","severity":1,"message":"1154","line":128,"column":53,"nodeType":"1155","messageId":"1156","endLine":128,"endColumn":72},{"ruleId":"1153","severity":1,"message":"1154","line":190,"column":33,"nodeType":"1155","messageId":"1156","endLine":190,"endColumn":52},{"ruleId":"989","severity":1,"message":"990","line":51,"column":18,"nodeType":"991","messageId":"992","endLine":51,"endColumn":21,"suggestions":"1315"},{"ruleId":"1316","severity":2,"message":"1317","line":243,"column":17,"nodeType":"1318","messageId":"1319","endLine":243,"endColumn":33},{"ruleId":"989","severity":1,"message":"990","line":243,"column":40,"nodeType":"991","messageId":"992","endLine":243,"endColumn":43,"suggestions":"1320"},{"ruleId":"1053","severity":2,"message":"1054","line":254,"column":3,"nodeType":"1055","messageId":"1056","endLine":261,"endColumn":4},{"ruleId":"980","severity":2,"message":"1321","line":352,"column":19,"nodeType":"982","messageId":"983","endLine":352,"endColumn":37},{"ruleId":"980","severity":2,"message":"1321","line":371,"column":19,"nodeType":"982","messageId":"983","endLine":371,"endColumn":37},{"ruleId":"980","severity":2,"message":"1321","line":389,"column":19,"nodeType":"982","messageId":"983","endLine":389,"endColumn":37},{"ruleId":"989","severity":1,"message":"990","line":499,"column":57,"nodeType":"991","messageId":"992","endLine":499,"endColumn":60,"suggestions":"1322"},{"ruleId":"980","severity":2,"message":"1323","line":509,"column":10,"nodeType":"982","messageId":"983","endLine":509,"endColumn":26},{"ruleId":"1053","severity":2,"message":"1054","line":515,"column":3,"nodeType":"1055","messageId":"1056","endLine":531,"endColumn":4},{"ruleId":"1011","severity":2,"message":"1324","line":250,"column":31,"nodeType":"982","messageId":"1013","endLine":250,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1324","line":266,"column":31,"nodeType":"982","messageId":"1013","endLine":266,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1324","line":282,"column":31,"nodeType":"982","messageId":"1013","endLine":282,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1324","line":298,"column":31,"nodeType":"982","messageId":"1013","endLine":298,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1324","line":314,"column":31,"nodeType":"982","messageId":"1013","endLine":314,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1325","line":335,"column":31,"nodeType":"982","messageId":"1013","endLine":335,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1325","line":351,"column":31,"nodeType":"982","messageId":"1013","endLine":351,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1325","line":367,"column":31,"nodeType":"982","messageId":"1013","endLine":367,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1325","line":383,"column":31,"nodeType":"982","messageId":"1013","endLine":383,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1325","line":399,"column":31,"nodeType":"982","messageId":"1013","endLine":399,"endColumn":32},{"ruleId":"1011","severity":2,"message":"1326","line":436,"column":35,"nodeType":"982","messageId":"1013","endLine":436,"endColumn":36},{"ruleId":"1011","severity":2,"message":"1326","line":452,"column":35,"nodeType":"982","messageId":"1013","endLine":452,"endColumn":36},{"ruleId":"1011","severity":2,"message":"1326","line":468,"column":35,"nodeType":"982","messageId":"1013","endLine":468,"endColumn":36},{"ruleId":"989","severity":1,"message":"990","line":536,"column":13,"nodeType":"991","messageId":"992","endLine":536,"endColumn":16,"suggestions":"1327"},{"ruleId":"989","severity":1,"message":"990","line":540,"column":29,"nodeType":"991","messageId":"992","endLine":540,"endColumn":32,"suggestions":"1328"},{"ruleId":"980","severity":2,"message":"1329","line":575,"column":16,"nodeType":"982","messageId":"983","endLine":575,"endColumn":30},{"ruleId":"980","severity":2,"message":"1330","line":575,"column":31,"nodeType":"982","messageId":"983","endLine":575,"endColumn":54},{"ruleId":"980","severity":2,"message":"1329","line":578,"column":16,"nodeType":"982","messageId":"983","endLine":578,"endColumn":30},{"ruleId":"980","severity":2,"message":"1330","line":578,"column":31,"nodeType":"982","messageId":"983","endLine":578,"endColumn":54},{"ruleId":"989","severity":1,"message":"990","line":597,"column":53,"nodeType":"991","messageId":"992","endLine":597,"endColumn":56,"suggestions":"1331"},{"ruleId":"989","severity":1,"message":"990","line":597,"column":76,"nodeType":"991","messageId":"992","endLine":597,"endColumn":79,"suggestions":"1332"},{"ruleId":"986","severity":1,"message":"1115","line":57,"column":37,"nodeType":"982","messageId":"988","endLine":57,"endColumn":58},{"ruleId":"986","severity":1,"message":"1115","line":62,"column":25,"nodeType":"982","messageId":"988","endLine":62,"endColumn":46},{"ruleId":"986","severity":1,"message":"1333","line":62,"column":48,"nodeType":"982","messageId":"988","endLine":62,"endColumn":61},{"ruleId":"986","severity":1,"message":"1115","line":89,"column":32,"nodeType":"982","messageId":"988","endLine":89,"endColumn":53},{"ruleId":"986","severity":1,"message":"1115","line":152,"column":37,"nodeType":"982","messageId":"988","endLine":152,"endColumn":58},{"ruleId":"980","severity":2,"message":"1334","line":234,"column":31,"nodeType":"982","messageId":"983","endLine":234,"endColumn":43},{"ruleId":"986","severity":1,"message":"1335","line":254,"column":11,"nodeType":"982","messageId":"988","endLine":254,"endColumn":20},{"ruleId":"986","severity":1,"message":"1336","line":255,"column":11,"nodeType":"982","messageId":"988","endLine":255,"endColumn":23},{"ruleId":"1028","severity":1,"message":"1337","line":343,"column":9,"nodeType":"982","messageId":"1117","endLine":343,"endColumn":16},{"ruleId":"1028","severity":1,"message":"1338","line":344,"column":9,"nodeType":"982","messageId":"1030","endLine":344,"endColumn":16},{"ruleId":"1028","severity":1,"message":"1337","line":346,"column":9,"nodeType":"982","messageId":"1117","endLine":346,"endColumn":16},{"ruleId":"986","severity":1,"message":"1115","line":358,"column":31,"nodeType":"982","messageId":"988","endLine":358,"endColumn":52},{"ruleId":"986","severity":1,"message":"1333","line":358,"column":54,"nodeType":"982","messageId":"988","endLine":358,"endColumn":67},{"ruleId":"986","severity":1,"message":"1115","line":400,"column":37,"nodeType":"982","messageId":"988","endLine":400,"endColumn":58},{"ruleId":"986","severity":1,"message":"1115","line":416,"column":31,"nodeType":"982","messageId":"988","endLine":416,"endColumn":52},{"ruleId":"986","severity":1,"message":"1333","line":416,"column":54,"nodeType":"982","messageId":"988","endLine":416,"endColumn":67},{"ruleId":"989","severity":1,"message":"990","line":433,"column":46,"nodeType":"991","messageId":"992","endLine":433,"endColumn":49,"suggestions":"1339"},{"ruleId":"980","severity":2,"message":"1340","line":471,"column":10,"nodeType":"982","messageId":"983","endLine":471,"endColumn":30},{"ruleId":"1084","severity":2,"message":"1085","line":485,"column":17,"nodeType":"1086","messageId":"1087","endLine":485,"endColumn":74},{"ruleId":"989","severity":1,"message":"990","line":20,"column":20,"nodeType":"991","messageId":"992","endLine":20,"endColumn":23,"suggestions":"1341"},{"ruleId":"989","severity":1,"message":"990","line":49,"column":18,"nodeType":"991","messageId":"992","endLine":49,"endColumn":21,"suggestions":"1342"},{"ruleId":"1099","severity":2,"message":"1343","line":8,"column":10,"nodeType":"982","endLine":8,"endColumn":23},{"ruleId":"986","severity":1,"message":"1344","line":102,"column":13,"nodeType":"982","messageId":"988","endLine":102,"endColumn":35},{"ruleId":"986","severity":1,"message":"1115","line":179,"column":37,"nodeType":"982","messageId":"988","endLine":179,"endColumn":58},{"ruleId":"1028","severity":1,"message":"1345","line":218,"column":11,"nodeType":"982","messageId":"1030","endLine":218,"endColumn":14},{"ruleId":"986","severity":1,"message":"1115","line":256,"column":31,"nodeType":"982","messageId":"988","endLine":256,"endColumn":52},{"ruleId":"986","severity":1,"message":"1333","line":256,"column":54,"nodeType":"982","messageId":"988","endLine":256,"endColumn":67},{"ruleId":"1028","severity":1,"message":"1097","line":303,"column":9,"nodeType":"982","messageId":"1030","endLine":303,"endColumn":13},{"ruleId":"986","severity":1,"message":"1346","line":313,"column":5,"nodeType":"982","messageId":"988","endLine":313,"endColumn":28},{"ruleId":"986","severity":1,"message":"1347","line":314,"column":5,"nodeType":"982","messageId":"988","endLine":314,"endColumn":122},{"ruleId":"986","severity":1,"message":"1348","line":319,"column":12,"nodeType":"982","messageId":"988","endLine":319,"endColumn":30},{"ruleId":"986","severity":1,"message":"1115","line":319,"column":32,"nodeType":"982","messageId":"988","endLine":319,"endColumn":53},{"ruleId":"1316","severity":2,"message":"1349","line":323,"column":3,"nodeType":"1002","messageId":"1319","endLine":323,"endColumn":9},{"ruleId":"1176","severity":2,"message":"1350","line":344,"column":47,"nodeType":"982","messageId":"1178","endLine":344,"endColumn":55},{"ruleId":"1122","severity":2,"message":"1351","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1353","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1354","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1355","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1356","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1357","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1358","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1359","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1360","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1361","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1362","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1363","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1364","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1365","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1366","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1367","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1368","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1369","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1370","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1371","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1372","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1373","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1374","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1375","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1376","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1377","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1378","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1379","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1380","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1381","line":1,"column":1,"nodeType":"1352","endLine":1,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1351","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1353","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1354","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1355","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1356","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1357","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1358","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1359","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1360","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1361","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1362","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1363","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1364","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1365","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1366","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1367","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1368","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1369","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1370","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1371","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1372","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1373","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1374","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1375","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1376","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1377","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1378","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1379","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1380","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1122","severity":2,"message":"1381","line":6,"column":1,"nodeType":"1352","endLine":6,"endColumn":23},{"ruleId":"1000","severity":1,"message":"1001","line":8,"column":16,"nodeType":"1318","messageId":"1003","endLine":8,"endColumn":25},{"ruleId":"989","severity":1,"message":"990","line":11,"column":70,"nodeType":"991","messageId":"992","endLine":11,"endColumn":73,"suggestions":"1382"},{"ruleId":"1176","severity":2,"message":"1383","line":11,"column":85,"nodeType":"1384","messageId":"1178","endLine":11,"endColumn":87},{"ruleId":"986","severity":1,"message":"1385","line":18,"column":12,"nodeType":"982","messageId":"988","endLine":18,"endColumn":28},{"ruleId":"989","severity":1,"message":"990","line":18,"column":89,"nodeType":"991","messageId":"992","endLine":18,"endColumn":92,"suggestions":"1386"},{"ruleId":"989","severity":1,"message":"990","line":34,"column":42,"nodeType":"991","messageId":"992","endLine":34,"endColumn":45,"suggestions":"1387"},{"ruleId":"989","severity":1,"message":"990","line":37,"column":22,"nodeType":"991","messageId":"992","endLine":37,"endColumn":25,"suggestions":"1388"},{"ruleId":"989","severity":1,"message":"990","line":38,"column":16,"nodeType":"991","messageId":"992","endLine":38,"endColumn":19,"suggestions":"1389"},{"ruleId":"989","severity":1,"message":"990","line":41,"column":23,"nodeType":"991","messageId":"992","endLine":41,"endColumn":26,"suggestions":"1390"},{"ruleId":"989","severity":1,"message":"990","line":41,"column":40,"nodeType":"991","messageId":"992","endLine":41,"endColumn":43,"suggestions":"1391"},{"ruleId":"989","severity":1,"message":"990","line":41,"column":52,"nodeType":"991","messageId":"992","endLine":41,"endColumn":55,"suggestions":"1392"},{"ruleId":"989","severity":1,"message":"990","line":45,"column":49,"nodeType":"991","messageId":"992","endLine":45,"endColumn":52,"suggestions":"1393"},{"ruleId":"989","severity":1,"message":"990","line":45,"column":78,"nodeType":"991","messageId":"992","endLine":45,"endColumn":81,"suggestions":"1394"},{"ruleId":"989","severity":1,"message":"990","line":47,"column":24,"nodeType":"991","messageId":"992","endLine":47,"endColumn":27,"suggestions":"1395"},{"ruleId":"989","severity":1,"message":"990","line":140,"column":59,"nodeType":"991","messageId":"992","endLine":140,"endColumn":62,"suggestions":"1396"},{"ruleId":"989","severity":1,"message":"990","line":151,"column":62,"nodeType":"991","messageId":"992","endLine":151,"endColumn":65,"suggestions":"1397"},{"ruleId":"1398","severity":2,"message":"1399","line":182,"column":37,"nodeType":"1280","messageId":"1400","endLine":182,"endColumn":64},{"ruleId":"986","severity":1,"message":"1401","line":15,"column":9,"nodeType":"982","messageId":"988","endLine":15,"endColumn":18},{"ruleId":"989","severity":1,"message":"990","line":45,"column":54,"nodeType":"991","messageId":"992","endLine":45,"endColumn":57,"suggestions":"1402"},{"ruleId":"986","severity":1,"message":"1403","line":97,"column":11,"nodeType":"982","messageId":"988","endLine":97,"endColumn":16},{"ruleId":"1404","severity":1,"message":"1405","line":195,"column":19,"nodeType":"1280","messageId":"1406","endLine":195,"endColumn":33},{"ruleId":"1404","severity":1,"message":"1405","line":205,"column":21,"nodeType":"1280","messageId":"1406","endLine":205,"endColumn":35},{"ruleId":"1404","severity":1,"message":"1405","line":303,"column":19,"nodeType":"1280","messageId":"1406","endLine":303,"endColumn":33},{"ruleId":"1404","severity":1,"message":"1405","line":313,"column":23,"nodeType":"1280","messageId":"1406","endLine":313,"endColumn":37},"@typescript-eslint/no-use-before-define","'combineLabelsForDB' was used before it was defined.","Identifier","noUseBeforeDefine","'cohortCreationDBHandler' was used before it was defined.","'Cohort' was used before it was defined.","@typescript-eslint/no-unused-vars","'test' is assigned a value but never used.","unusedVar","@typescript-eslint/no-explicit-any","Unexpected any. Specify a different type.","TSAnyKeyword","unexpectedAny",["1407","1408"],"'IBloodlineElement' is defined but never used.",["1409","1410"],"'height' is defined but never used.","'width' is defined but never used.",["1411","1412"],["1413","1414"],"func-names","Unexpected unnamed function.","FunctionExpression","unnamed",["1415","1416"],["1417","1418"],"'event' is defined but never used.",["1419","1420"],["1421","1422"],["1423","1424"],["1425","1426"],"@typescript-eslint/no-shadow","'i' is already declared in the upper scope on line 493 column 16.","noShadow","'summaries' is assigned a value but never used.","'currChtInfo' is already declared in the upper scope on line 571 column 13.","'chtName' is already declared in the upper scope on line 572 column 13.","'chtValues' is already declared in the upper scope on line 576 column 13.",["1427","1428"],"'tip' is already declared in the upper scope on line 18 column 9.","'setOnboardingCookie' was used before it was defined.",["1429","1430"],"'ev' is defined but never used.","'viewDescr' is defined but never used.","'barHeight' is assigned a value but never used.","'scrollLink' is assigned a value but never used.",["1431","1432"],["1433","1434"],"no-param-reassign","Assignment to property of function parameter 'container'.","assignmentToFunctionParamProp","'currMaxRow' is assigned a value but never used.","'addOverviewCohortImpl' was used before it was defined.",["1435","1436"],["1437","1438"],"'setChtCounter' was used before it was defined.",["1439","1440"],"Assignment to property of function parameter 'app'.","'removeOverviewCohortImpl' was used before it was defined.",["1441","1442"],["1443","1444"],"'setDatasetImpl' was used before it was defined.",["1445","1446"],["1447","1448"],["1449","1450"],["1451","1452"],["1453","1454"],["1455","1456"],"'badgeIds' is assigned a value but never used.",["1457","1458"],"'d' is defined but never used.",["1459","1460"],["1461","1462"],"default-case","Expected a default case.","SwitchStatement","missingDefaultCase","no-case-declarations","Unexpected lexical declaration in case block.","VariableDeclaration","unexpected",["1463","1464"],"'optionId' is assigned a value but never used.","'e' is defined but never used.","no-return-assign","Arrow function should not return assignment.","ArrowFunctionExpression","arrowAssignment","Assignment to property of function parameter 'parent'.","'d' is already declared in the upper scope on line 912 column 14.",["1465","1466"],"no-useless-concat","Unexpected string concatenation of literals.","BinaryExpression","unexpectedConcat","@typescript-eslint/no-this-alias","Unexpected aliasing of 'this' to local variable.","thisAssignment","@typescript-eslint/ban-ts-comment","Do not use \"@ts-ignore\" because it alters compilation errors.","Line","tsDirectiveComment","'TaskviewInput' was used before it was defined.","'TaskviewOutput' was used before it was defined.","no-await-in-loop","Unexpected `await` inside a loop.","AwaitExpression","unexpectedAwait",["1467","1468"],["1469","1470"],"@typescript-eslint/no-loop-func","Function declared in a loop contains unsafe references to variable(s) 'getLoaderCohort'.","unsafeRefs","array-callback-return","Array.prototype.map() expects a return value from arrow function.","expectedInside","Assignment to property of function parameter 'cht'.","Assignment to property of function parameter 'elem'.","Assignment to property of function parameter 'ev'.","import/named","taskview not found in '../../cohortview'","@typescript-eslint/naming-convention","Variable name `_thisColumn` must match one of the following formats: camelCase, PascalCase, UPPER_CASE","doesNotMatchFormat","'index' is defined but never used.","Assignment to property of function parameter 'cell'.","'cell' is defined but never used.","'cht' is defined but never used.","'taskview' is already declared in the upper scope on line 8 column 10.","'Histogram' was used before it was defined.",["1471","1472"],"no-lonely-if","Unexpected if as the only statement in an else block.","IfStatement","unexpectedLonelyIf","'filters' is defined but never used.","Assignment to function parameter 'value'.","assignmentToFunctionParam",["1473","1474"],["1475","1476"],"'PrevalenceBar' was used before it was defined.","'spec' is assigned a value but never used.","import/export","No named exports found in module './SearchColumn'.","Literal","No named exports found in module './Taskview'.",["1477","1478"],["1479","1480"],["1481","1482"],"'attributes' is defined but never used.","'cohorts' is defined but never used.","'tableContainer' is assigned a value but never used.",["1483","1484"],"'timestamp' is already declared in the upper scope on line 76 column 11.","'insertLegend' was used before it was defined.","'reject' is defined but never used.","'textColor4Background' was used before it was defined.","'score2color' was used before it was defined.","no-restricted-globals","Unexpected use of 'isNaN'. Use Number.isNaN instead https://github.com/airbnb/javascript#standard-library--isnan","customMessage",["1485","1486"],["1487","1488"],["1489","1490"],["1491","1492"],"no-async-promise-executor","Promise executor functions should not be async.","async","'data' is defined but never used.","'label' is defined but never used.",["1493","1494"],["1495","1496"],["1497","1498"],"@typescript-eslint/no-non-null-assertion","Forbidden non-null assertion.","TSNonNullExpression","noNonNull",["1499"],["1500","1501"],["1502","1503"],["1504","1505"],["1506","1507"],["1508","1509"],["1510","1511"],["1512","1513"],["1514","1515"],"new-cap","A constructor name should not start with a lowercase letter.","NewExpression","lower",["1516","1517"],"getRootCohort not found in '../../cohortview'","'exclState' is defined but never used.","'taskRep' is assigned a value but never used.",["1518","1519"],["1520","1521"],"@typescript-eslint/ban-types","Don't use `Object` as a type. The `Object` type actually means \"any non-nullish value\", so it is marginally better than `unknown`.\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.","bannedTypeMessage",["1522","1523"],"'i' is defined but never used.","Assignment to property of function parameter 'chart'.","'closingBracket' is assigned a value but never used.","'name' is defined but never used.","'value' is defined but never used.","prettier/prettier","Replace `IdValuePair` with `·IdValuePair·`","replace",{"range":"1524","text":"1525"},"no-useless-escape","Unnecessary escape character: \\'.","TemplateElement","unnecessaryEscape",["1526","1527"],["1528","1529"],["1530","1531"],["1532","1533"],["1534","1535"],["1536","1537"],["1538","1539"],["1540","1541"],["1542","1543"],["1544","1545"],["1546","1547"],["1548","1549"],["1550","1551"],["1552","1553"],["1554","1555"],["1556","1557"],["1558","1559"],["1560","1561"],["1562","1563"],["1564","1565"],["1566","1567"],["1568","1569"],["1570","1571"],["1572","1573"],["1574","1575"],["1576","1577"],["1578","1579"],["1580","1581"],["1582","1583"],["1584","1585"],["1586","1587"],["1588","1589"],["1590","1591"],["1592","1593"],["1594","1595"],["1596","1597"],["1598","1599"],["1600","1601"],["1602","1603"],["1604","1605"],["1606","1607"],["1608","1609"],["1610","1611"],["1612","1613"],["1614","1615"],["1616","1617"],["1618","1619"],["1620","1621"],["1622","1623"],["1624","1625"],["1626","1627"],["1628","1629"],["1630","1631"],["1632","1633"],["1634","1635"],["1636","1637"],"Assignment to property of function parameter 'spec'.","'axis' is defined but never used.",["1638","1639"],"IdValuePair not found in '../../data/Attribute'",["1640","1641"],"'None' is defined but never used.","Assignment to function parameter 'data'.",["1642","1643"],["1644","1645"],["1646","1647"],["1648","1649"],["1650","1651"],["1652","1653"],["1654","1655"],["1656","1657"],["1658","1659"],"'chtIndex' is defined but never used.",["1660","1661"],["1662","1663"],"Assignment to property of function parameter 'node'.","'filter' is assigned a value but never used.",["1664","1665"],["1666","1667"],["1668","1669"],["1670","1671"],["1672","1673"],["1674","1675"],["1676","1677"],["1678","1679"],"global-require","Unexpected require().","CallExpression","@typescript-eslint/no-var-requires","Require statement not part of import statement.","noVarReqs",["1680","1681"],["1682","1683"],["1684","1685"],"'attribute' is defined but never used.","'VisConfig' is defined but never used.","Assignment to property of function parameter 'option'.",["1686","1687"],"no-self-compare","Comparing to itself is potentially pointless.","comparingToSelf",["1688","1689"],["1690","1691"],"'dataBtns' is assigned a value but never used.","Unexpected unnamed async function.","'dataSourcesAndPanels' is already declared in the upper scope on line 187 column 11.","'loading' is assigned a value but never used.","Assignment to property of function parameter 'newDataset'.",["1692","1693"],["1694","1695"],["1696","1697"],["1698","1699"],["1700","1701"],["1702","1703"],["1704","1705"],["1706","1707"],["1708","1709"],"Assignment to function parameter 'values'.","Assignment to property of function parameter 'params'.","Delete `⏎`","delete",{"range":"1710","text":"1711"},["1712","1713"],"consistent-return","Expected to return a value at the end of function 'chooseDataSource'.","FunctionDeclaration","missingReturn",["1714","1715"],"'toLineUpCategories' was used before it was defined.",["1716","1717"],"'resolveDataTypes' was used before it was defined.","'d' is already declared in the upper scope on line 238 column 39.","'d' is already declared in the upper scope on line 323 column 41.","'d' is already declared in the upper scope on line 408 column 25.",["1718","1719"],["1720","1721"],"'addEmptyOption' was used before it was defined.","'selectCategoricalColumn' was used before it was defined.",["1722","1723"],["1724","1725"],"'bins' is defined but never used.","'subType2Type' was used before it was defined.","'scoreView' is assigned a value but never used.","'scoreFilters' is assigned a value but never used.","Assignment to function parameter 'filters'.","Assignment to property of function parameter 'filters'.",["1726","1727"],"'multiAttributeFilter' was used before it was defined.",["1728","1729"],["1730","1731"],"getRootCohort not found in '../cohortview'","'tempAgents' is assigned a value but never used.","Assignment to property of function parameter 'row'.","'histType' is defined but never used.","'params' is defined but never used.","'cohortDbId' is defined but never used.","Expected to return a value at the end of method 'filter'.","Don't use `Function` as a type. The `Function` type accepts any function-like value.\nIt provides no type safety when calling the function, which can be a common source of bugs.\nIt also accepts things like class declarations, which will throw at runtime as they will not be called with `new`.\nIf you are expecting the function to accept certain arguments, you should explicitly define the function shape.","Multiple exports of name 'CoralApp'.","ExportAllDeclaration","Multiple exports of name 'Coral'.","Multiple exports of name 'CohortSelectionListener'.","Multiple exports of name 'ECloneFilterTypes'.","Multiple exports of name 'ICohortClassBasicValues'.","Multiple exports of name 'ICohortClassDatabaseValues'.","Multiple exports of name 'IPanelDesc'.","Multiple exports of name 'IDatasetDesc'.","Multiple exports of name 'TaskType'.","Multiple exports of name 'EElementProvType'.","Multiple exports of name 'IProvAttrAndValuesCohort'.","Multiple exports of name 'IProvAttrAndValuesTask'.","Multiple exports of name 'IElementProvJSON'.","Multiple exports of name 'IElementProvJSONCohort'.","Multiple exports of name 'IElementProvJSONTask'.","Multiple exports of name 'IElement'.","Multiple exports of name 'IBloodlineElement'.","Multiple exports of name 'ICohort'.","Multiple exports of name 'IInputCohort'.","Multiple exports of name 'IOutputCohort'.","Multiple exports of name 'ITask'.","Multiple exports of name 'IOverviewLayout'.","Multiple exports of name 'IRectLayout'.","Multiple exports of name 'ICohortRep'.","Multiple exports of name 'IRectCohortRep'.","Multiple exports of name 'ITaskRep'.","Multiple exports of name 'IRectTaskRep'.","Multiple exports of name 'ISetTwoLabelFunc'.","Multiple exports of name 'ISetLabelFunc'.","Multiple exports of name 'ITaskParams'.",["1732","1733"],"Don't use `{}` as a type. `{}` actually means \"any non-nullish value\".\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.\n- If you want a type meaning \"empty object\", you probably want `Record` instead.","TSTypeLiteral","'actionCompressor' is defined but never used.",["1734","1735"],["1736","1737"],["1738","1739"],["1740","1741"],["1742","1743"],["1744","1745"],["1746","1747"],["1748","1749"],["1750","1751"],["1752","1753"],["1754","1755"],["1756","1757"],"no-promise-executor-return","Return values from promise executor functions cannot be read.","returnsValue","'attribute' is assigned a value but never used.",["1758","1759"],"'opOne' is assigned a value but never used.","no-prototype-builtins","Do not access Object.prototype method 'hasOwnProperty' from target object.","prototypeBuildIn",{"messageId":"1760","fix":"1761","desc":"1762"},{"messageId":"1763","fix":"1764","desc":"1765"},{"messageId":"1760","fix":"1766","desc":"1762"},{"messageId":"1763","fix":"1767","desc":"1765"},{"messageId":"1760","fix":"1768","desc":"1762"},{"messageId":"1763","fix":"1769","desc":"1765"},{"messageId":"1760","fix":"1770","desc":"1762"},{"messageId":"1763","fix":"1771","desc":"1765"},{"messageId":"1760","fix":"1772","desc":"1762"},{"messageId":"1763","fix":"1773","desc":"1765"},{"messageId":"1760","fix":"1774","desc":"1762"},{"messageId":"1763","fix":"1775","desc":"1765"},{"messageId":"1760","fix":"1776","desc":"1762"},{"messageId":"1763","fix":"1777","desc":"1765"},{"messageId":"1760","fix":"1778","desc":"1762"},{"messageId":"1763","fix":"1779","desc":"1765"},{"messageId":"1760","fix":"1780","desc":"1762"},{"messageId":"1763","fix":"1781","desc":"1765"},{"messageId":"1760","fix":"1782","desc":"1762"},{"messageId":"1763","fix":"1783","desc":"1765"},{"messageId":"1760","fix":"1784","desc":"1762"},{"messageId":"1763","fix":"1785","desc":"1765"},{"messageId":"1760","fix":"1786","desc":"1762"},{"messageId":"1763","fix":"1787","desc":"1765"},{"messageId":"1760","fix":"1788","desc":"1762"},{"messageId":"1763","fix":"1789","desc":"1765"},{"messageId":"1760","fix":"1790","desc":"1762"},{"messageId":"1763","fix":"1791","desc":"1765"},{"messageId":"1760","fix":"1792","desc":"1762"},{"messageId":"1763","fix":"1793","desc":"1765"},{"messageId":"1760","fix":"1794","desc":"1762"},{"messageId":"1763","fix":"1795","desc":"1765"},{"messageId":"1760","fix":"1796","desc":"1762"},{"messageId":"1763","fix":"1797","desc":"1765"},{"messageId":"1760","fix":"1798","desc":"1762"},{"messageId":"1763","fix":"1799","desc":"1765"},{"messageId":"1760","fix":"1800","desc":"1762"},{"messageId":"1763","fix":"1801","desc":"1765"},{"messageId":"1760","fix":"1802","desc":"1762"},{"messageId":"1763","fix":"1803","desc":"1765"},{"messageId":"1760","fix":"1804","desc":"1762"},{"messageId":"1763","fix":"1805","desc":"1765"},{"messageId":"1760","fix":"1806","desc":"1762"},{"messageId":"1763","fix":"1807","desc":"1765"},{"messageId":"1760","fix":"1808","desc":"1762"},{"messageId":"1763","fix":"1809","desc":"1765"},{"messageId":"1760","fix":"1810","desc":"1762"},{"messageId":"1763","fix":"1811","desc":"1765"},{"messageId":"1760","fix":"1812","desc":"1762"},{"messageId":"1763","fix":"1813","desc":"1765"},{"messageId":"1760","fix":"1814","desc":"1762"},{"messageId":"1763","fix":"1815","desc":"1765"},{"messageId":"1760","fix":"1816","desc":"1762"},{"messageId":"1763","fix":"1817","desc":"1765"},{"messageId":"1760","fix":"1818","desc":"1762"},{"messageId":"1763","fix":"1819","desc":"1765"},{"messageId":"1760","fix":"1820","desc":"1762"},{"messageId":"1763","fix":"1821","desc":"1765"},{"messageId":"1760","fix":"1822","desc":"1762"},{"messageId":"1763","fix":"1823","desc":"1765"},{"messageId":"1760","fix":"1824","desc":"1762"},{"messageId":"1763","fix":"1825","desc":"1765"},{"messageId":"1760","fix":"1826","desc":"1762"},{"messageId":"1763","fix":"1827","desc":"1765"},{"messageId":"1760","fix":"1828","desc":"1762"},{"messageId":"1763","fix":"1829","desc":"1765"},{"messageId":"1760","fix":"1830","desc":"1762"},{"messageId":"1763","fix":"1831","desc":"1765"},{"messageId":"1760","fix":"1832","desc":"1762"},{"messageId":"1763","fix":"1833","desc":"1765"},{"messageId":"1760","fix":"1834","desc":"1762"},{"messageId":"1763","fix":"1835","desc":"1765"},{"messageId":"1760","fix":"1836","desc":"1762"},{"messageId":"1763","fix":"1837","desc":"1765"},{"messageId":"1760","fix":"1838","desc":"1762"},{"messageId":"1763","fix":"1839","desc":"1765"},{"messageId":"1760","fix":"1840","desc":"1762"},{"messageId":"1763","fix":"1841","desc":"1765"},{"messageId":"1760","fix":"1842","desc":"1762"},{"messageId":"1763","fix":"1843","desc":"1765"},{"messageId":"1760","fix":"1844","desc":"1762"},{"messageId":"1763","fix":"1845","desc":"1765"},{"messageId":"1760","fix":"1846","desc":"1762"},{"messageId":"1763","fix":"1847","desc":"1765"},{"messageId":"1760","fix":"1848","desc":"1762"},{"messageId":"1763","fix":"1849","desc":"1765"},{"messageId":"1760","fix":"1850","desc":"1762"},{"messageId":"1763","fix":"1851","desc":"1765"},{"messageId":"1760","fix":"1852","desc":"1762"},{"messageId":"1763","fix":"1853","desc":"1765"},{"messageId":"1760","fix":"1854","desc":"1762"},{"messageId":"1763","fix":"1855","desc":"1765"},{"messageId":"1856","fix":"1857","desc":"1858"},{"messageId":"1760","fix":"1859","desc":"1762"},{"messageId":"1763","fix":"1860","desc":"1765"},{"messageId":"1760","fix":"1861","desc":"1762"},{"messageId":"1763","fix":"1862","desc":"1765"},{"messageId":"1760","fix":"1863","desc":"1762"},{"messageId":"1763","fix":"1864","desc":"1765"},{"messageId":"1760","fix":"1865","desc":"1762"},{"messageId":"1763","fix":"1866","desc":"1765"},{"messageId":"1760","fix":"1867","desc":"1762"},{"messageId":"1763","fix":"1868","desc":"1765"},{"messageId":"1760","fix":"1869","desc":"1762"},{"messageId":"1763","fix":"1870","desc":"1765"},{"messageId":"1760","fix":"1871","desc":"1762"},{"messageId":"1763","fix":"1872","desc":"1765"},{"messageId":"1760","fix":"1873","desc":"1762"},{"messageId":"1763","fix":"1874","desc":"1765"},{"messageId":"1760","fix":"1875","desc":"1762"},{"messageId":"1763","fix":"1876","desc":"1765"},{"messageId":"1760","fix":"1877","desc":"1762"},{"messageId":"1763","fix":"1878","desc":"1765"},{"messageId":"1760","fix":"1879","desc":"1762"},{"messageId":"1763","fix":"1880","desc":"1765"},{"messageId":"1760","fix":"1881","desc":"1762"},{"messageId":"1763","fix":"1882","desc":"1765"},[414,425]," IdValuePair ",{"messageId":"1883","fix":"1884","desc":"1885"},{"messageId":"1886","fix":"1887","desc":"1888"},{"messageId":"1883","fix":"1889","desc":"1885"},{"messageId":"1886","fix":"1890","desc":"1888"},{"messageId":"1883","fix":"1891","desc":"1885"},{"messageId":"1886","fix":"1892","desc":"1888"},{"messageId":"1883","fix":"1893","desc":"1885"},{"messageId":"1886","fix":"1894","desc":"1888"},{"messageId":"1883","fix":"1895","desc":"1885"},{"messageId":"1886","fix":"1896","desc":"1888"},{"messageId":"1883","fix":"1897","desc":"1885"},{"messageId":"1886","fix":"1898","desc":"1888"},{"messageId":"1883","fix":"1899","desc":"1885"},{"messageId":"1886","fix":"1900","desc":"1888"},{"messageId":"1883","fix":"1901","desc":"1885"},{"messageId":"1886","fix":"1902","desc":"1888"},{"messageId":"1883","fix":"1903","desc":"1885"},{"messageId":"1886","fix":"1904","desc":"1888"},{"messageId":"1883","fix":"1905","desc":"1885"},{"messageId":"1886","fix":"1906","desc":"1888"},{"messageId":"1883","fix":"1907","desc":"1885"},{"messageId":"1886","fix":"1908","desc":"1888"},{"messageId":"1883","fix":"1909","desc":"1885"},{"messageId":"1886","fix":"1910","desc":"1888"},{"messageId":"1883","fix":"1911","desc":"1885"},{"messageId":"1886","fix":"1912","desc":"1888"},{"messageId":"1883","fix":"1913","desc":"1885"},{"messageId":"1886","fix":"1914","desc":"1888"},{"messageId":"1883","fix":"1915","desc":"1885"},{"messageId":"1886","fix":"1916","desc":"1888"},{"messageId":"1883","fix":"1917","desc":"1885"},{"messageId":"1886","fix":"1918","desc":"1888"},{"messageId":"1883","fix":"1919","desc":"1885"},{"messageId":"1886","fix":"1920","desc":"1888"},{"messageId":"1883","fix":"1921","desc":"1885"},{"messageId":"1886","fix":"1922","desc":"1888"},{"messageId":"1883","fix":"1923","desc":"1885"},{"messageId":"1886","fix":"1924","desc":"1888"},{"messageId":"1883","fix":"1925","desc":"1885"},{"messageId":"1886","fix":"1926","desc":"1888"},{"messageId":"1883","fix":"1927","desc":"1885"},{"messageId":"1886","fix":"1928","desc":"1888"},{"messageId":"1883","fix":"1929","desc":"1885"},{"messageId":"1886","fix":"1930","desc":"1888"},{"messageId":"1883","fix":"1931","desc":"1885"},{"messageId":"1886","fix":"1932","desc":"1888"},{"messageId":"1883","fix":"1933","desc":"1885"},{"messageId":"1886","fix":"1934","desc":"1888"},{"messageId":"1883","fix":"1935","desc":"1885"},{"messageId":"1886","fix":"1936","desc":"1888"},{"messageId":"1883","fix":"1937","desc":"1885"},{"messageId":"1886","fix":"1938","desc":"1888"},{"messageId":"1883","fix":"1939","desc":"1885"},{"messageId":"1886","fix":"1940","desc":"1888"},{"messageId":"1883","fix":"1941","desc":"1885"},{"messageId":"1886","fix":"1942","desc":"1888"},{"messageId":"1883","fix":"1943","desc":"1885"},{"messageId":"1886","fix":"1944","desc":"1888"},{"messageId":"1883","fix":"1945","desc":"1885"},{"messageId":"1886","fix":"1946","desc":"1888"},{"messageId":"1883","fix":"1947","desc":"1885"},{"messageId":"1886","fix":"1948","desc":"1888"},{"messageId":"1883","fix":"1949","desc":"1885"},{"messageId":"1886","fix":"1950","desc":"1888"},{"messageId":"1883","fix":"1951","desc":"1885"},{"messageId":"1886","fix":"1952","desc":"1888"},{"messageId":"1883","fix":"1953","desc":"1885"},{"messageId":"1886","fix":"1954","desc":"1888"},{"messageId":"1883","fix":"1955","desc":"1885"},{"messageId":"1886","fix":"1956","desc":"1888"},{"messageId":"1883","fix":"1957","desc":"1885"},{"messageId":"1886","fix":"1958","desc":"1888"},{"messageId":"1883","fix":"1959","desc":"1885"},{"messageId":"1886","fix":"1960","desc":"1888"},{"messageId":"1883","fix":"1961","desc":"1885"},{"messageId":"1886","fix":"1962","desc":"1888"},{"messageId":"1883","fix":"1963","desc":"1885"},{"messageId":"1886","fix":"1964","desc":"1888"},{"messageId":"1883","fix":"1965","desc":"1885"},{"messageId":"1886","fix":"1966","desc":"1888"},{"messageId":"1883","fix":"1967","desc":"1885"},{"messageId":"1886","fix":"1968","desc":"1888"},{"messageId":"1883","fix":"1969","desc":"1885"},{"messageId":"1886","fix":"1970","desc":"1888"},{"messageId":"1883","fix":"1971","desc":"1885"},{"messageId":"1886","fix":"1972","desc":"1888"},{"messageId":"1883","fix":"1973","desc":"1885"},{"messageId":"1886","fix":"1974","desc":"1888"},{"messageId":"1883","fix":"1975","desc":"1885"},{"messageId":"1886","fix":"1976","desc":"1888"},{"messageId":"1883","fix":"1977","desc":"1885"},{"messageId":"1886","fix":"1978","desc":"1888"},{"messageId":"1883","fix":"1979","desc":"1885"},{"messageId":"1886","fix":"1980","desc":"1888"},{"messageId":"1883","fix":"1981","desc":"1885"},{"messageId":"1886","fix":"1982","desc":"1888"},{"messageId":"1883","fix":"1983","desc":"1885"},{"messageId":"1886","fix":"1984","desc":"1888"},{"messageId":"1883","fix":"1985","desc":"1885"},{"messageId":"1886","fix":"1986","desc":"1888"},{"messageId":"1883","fix":"1987","desc":"1885"},{"messageId":"1886","fix":"1988","desc":"1888"},{"messageId":"1883","fix":"1989","desc":"1885"},{"messageId":"1886","fix":"1990","desc":"1888"},{"messageId":"1883","fix":"1991","desc":"1885"},{"messageId":"1886","fix":"1992","desc":"1888"},{"messageId":"1883","fix":"1993","desc":"1885"},{"messageId":"1886","fix":"1994","desc":"1888"},{"messageId":"1883","fix":"1995","desc":"1885"},{"messageId":"1886","fix":"1996","desc":"1888"},{"messageId":"1883","fix":"1997","desc":"1885"},{"messageId":"1886","fix":"1998","desc":"1888"},{"messageId":"1760","fix":"1999","desc":"1762"},{"messageId":"1763","fix":"2000","desc":"1765"},{"messageId":"1760","fix":"2001","desc":"1762"},{"messageId":"1763","fix":"2002","desc":"1765"},{"messageId":"1760","fix":"2003","desc":"1762"},{"messageId":"1763","fix":"2004","desc":"1765"},{"messageId":"1760","fix":"2005","desc":"1762"},{"messageId":"1763","fix":"2006","desc":"1765"},{"messageId":"1760","fix":"2007","desc":"1762"},{"messageId":"1763","fix":"2008","desc":"1765"},{"messageId":"1760","fix":"2009","desc":"1762"},{"messageId":"1763","fix":"2010","desc":"1765"},{"messageId":"1760","fix":"2011","desc":"1762"},{"messageId":"1763","fix":"2012","desc":"1765"},{"messageId":"1760","fix":"2013","desc":"1762"},{"messageId":"1763","fix":"2014","desc":"1765"},{"messageId":"1760","fix":"2015","desc":"1762"},{"messageId":"1763","fix":"2016","desc":"1765"},{"messageId":"1760","fix":"2017","desc":"1762"},{"messageId":"1763","fix":"2018","desc":"1765"},{"messageId":"1760","fix":"2019","desc":"1762"},{"messageId":"1763","fix":"2020","desc":"1765"},{"messageId":"1760","fix":"2021","desc":"1762"},{"messageId":"1763","fix":"2022","desc":"1765"},{"messageId":"1760","fix":"2023","desc":"1762"},{"messageId":"1763","fix":"2024","desc":"1765"},{"messageId":"1760","fix":"2025","desc":"1762"},{"messageId":"1763","fix":"2026","desc":"1765"},{"messageId":"1760","fix":"2027","desc":"1762"},{"messageId":"1763","fix":"2028","desc":"1765"},{"messageId":"1760","fix":"2029","desc":"1762"},{"messageId":"1763","fix":"2030","desc":"1765"},{"messageId":"1760","fix":"2031","desc":"1762"},{"messageId":"1763","fix":"2032","desc":"1765"},{"messageId":"1760","fix":"2033","desc":"1762"},{"messageId":"1763","fix":"2034","desc":"1765"},{"messageId":"1760","fix":"2035","desc":"1762"},{"messageId":"1763","fix":"2036","desc":"1765"},{"messageId":"1760","fix":"2037","desc":"1762"},{"messageId":"1763","fix":"2038","desc":"1765"},{"messageId":"1760","fix":"2039","desc":"1762"},{"messageId":"1763","fix":"2040","desc":"1765"},{"messageId":"1760","fix":"2041","desc":"1762"},{"messageId":"1763","fix":"2042","desc":"1765"},{"messageId":"1760","fix":"2043","desc":"1762"},{"messageId":"1763","fix":"2044","desc":"1765"},{"messageId":"1760","fix":"2045","desc":"1762"},{"messageId":"1763","fix":"2046","desc":"1765"},{"messageId":"1760","fix":"2047","desc":"1762"},{"messageId":"1763","fix":"2048","desc":"1765"},{"messageId":"1760","fix":"2049","desc":"1762"},{"messageId":"1763","fix":"2050","desc":"1765"},{"messageId":"1760","fix":"2051","desc":"1762"},{"messageId":"1763","fix":"2052","desc":"1765"},{"messageId":"1760","fix":"2053","desc":"1762"},{"messageId":"1763","fix":"2054","desc":"1765"},{"messageId":"1760","fix":"2055","desc":"1762"},{"messageId":"1763","fix":"2056","desc":"1765"},{"messageId":"1760","fix":"2057","desc":"1762"},{"messageId":"1763","fix":"2058","desc":"1765"},{"messageId":"1760","fix":"2059","desc":"1762"},{"messageId":"1763","fix":"2060","desc":"1765"},{"messageId":"1760","fix":"2061","desc":"1762"},{"messageId":"1763","fix":"2062","desc":"1765"},{"messageId":"1760","fix":"2063","desc":"1762"},{"messageId":"1763","fix":"2064","desc":"1765"},{"messageId":"1760","fix":"2065","desc":"1762"},{"messageId":"1763","fix":"2066","desc":"1765"},{"messageId":"1760","fix":"2067","desc":"1762"},{"messageId":"1763","fix":"2068","desc":"1765"},{"messageId":"1760","fix":"2069","desc":"1762"},{"messageId":"1763","fix":"2070","desc":"1765"},[16088,16089],"",{"messageId":"1760","fix":"2071","desc":"1762"},{"messageId":"1763","fix":"2072","desc":"1765"},{"messageId":"1760","fix":"2073","desc":"1762"},{"messageId":"1763","fix":"2074","desc":"1765"},{"messageId":"1760","fix":"2075","desc":"1762"},{"messageId":"1763","fix":"2076","desc":"1765"},{"messageId":"1760","fix":"2077","desc":"1762"},{"messageId":"1763","fix":"2078","desc":"1765"},{"messageId":"1760","fix":"2079","desc":"1762"},{"messageId":"1763","fix":"2080","desc":"1765"},{"messageId":"1760","fix":"2081","desc":"1762"},{"messageId":"1763","fix":"2082","desc":"1765"},{"messageId":"1760","fix":"2083","desc":"1762"},{"messageId":"1763","fix":"2084","desc":"1765"},{"messageId":"1760","fix":"2085","desc":"1762"},{"messageId":"1763","fix":"2086","desc":"1765"},{"messageId":"1760","fix":"2087","desc":"1762"},{"messageId":"1763","fix":"2088","desc":"1765"},{"messageId":"1760","fix":"2089","desc":"1762"},{"messageId":"1763","fix":"2090","desc":"1765"},{"messageId":"1760","fix":"2091","desc":"1762"},{"messageId":"1763","fix":"2092","desc":"1765"},{"messageId":"1760","fix":"2093","desc":"1762"},{"messageId":"1763","fix":"2094","desc":"1765"},{"messageId":"1760","fix":"2095","desc":"1762"},{"messageId":"1763","fix":"2096","desc":"1765"},{"messageId":"1760","fix":"2097","desc":"1762"},{"messageId":"1763","fix":"2098","desc":"1765"},{"messageId":"1760","fix":"2099","desc":"1762"},{"messageId":"1763","fix":"2100","desc":"1765"},{"messageId":"1760","fix":"2101","desc":"1762"},{"messageId":"1763","fix":"2102","desc":"1765"},{"messageId":"1760","fix":"2103","desc":"1762"},{"messageId":"1763","fix":"2104","desc":"1765"},{"messageId":"1760","fix":"2105","desc":"1762"},{"messageId":"1763","fix":"2106","desc":"1765"},{"messageId":"1760","fix":"2107","desc":"1762"},{"messageId":"1763","fix":"2108","desc":"1765"},{"messageId":"1760","fix":"2109","desc":"1762"},{"messageId":"1763","fix":"2110","desc":"1765"},{"messageId":"1760","fix":"2111","desc":"1762"},{"messageId":"1763","fix":"2112","desc":"1765"},{"messageId":"1760","fix":"2113","desc":"1762"},{"messageId":"1763","fix":"2114","desc":"1765"},{"messageId":"1760","fix":"2115","desc":"1762"},{"messageId":"1763","fix":"2116","desc":"1765"},{"messageId":"1760","fix":"2117","desc":"1762"},{"messageId":"1763","fix":"2118","desc":"1765"},"suggestUnknown",{"range":"2119","text":"2120"},"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct.","suggestNever",{"range":"2119","text":"2121"},"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of.",{"range":"2122","text":"2120"},{"range":"2122","text":"2121"},{"range":"2123","text":"2120"},{"range":"2123","text":"2121"},{"range":"2124","text":"2120"},{"range":"2124","text":"2121"},{"range":"2125","text":"2120"},{"range":"2125","text":"2121"},{"range":"2126","text":"2120"},{"range":"2126","text":"2121"},{"range":"2127","text":"2120"},{"range":"2127","text":"2121"},{"range":"2128","text":"2120"},{"range":"2128","text":"2121"},{"range":"2129","text":"2120"},{"range":"2129","text":"2121"},{"range":"2130","text":"2120"},{"range":"2130","text":"2121"},{"range":"2131","text":"2120"},{"range":"2131","text":"2121"},{"range":"2132","text":"2120"},{"range":"2132","text":"2121"},{"range":"2133","text":"2120"},{"range":"2133","text":"2121"},{"range":"2134","text":"2120"},{"range":"2134","text":"2121"},{"range":"2135","text":"2120"},{"range":"2135","text":"2121"},{"range":"2136","text":"2120"},{"range":"2136","text":"2121"},{"range":"2137","text":"2120"},{"range":"2137","text":"2121"},{"range":"2138","text":"2120"},{"range":"2138","text":"2121"},{"range":"2139","text":"2120"},{"range":"2139","text":"2121"},{"range":"2140","text":"2120"},{"range":"2140","text":"2121"},{"range":"2141","text":"2120"},{"range":"2141","text":"2121"},{"range":"2142","text":"2120"},{"range":"2142","text":"2121"},{"range":"2143","text":"2120"},{"range":"2143","text":"2121"},{"range":"2144","text":"2120"},{"range":"2144","text":"2121"},{"range":"2145","text":"2120"},{"range":"2145","text":"2121"},{"range":"2146","text":"2120"},{"range":"2146","text":"2121"},{"range":"2147","text":"2120"},{"range":"2147","text":"2121"},{"range":"2148","text":"2120"},{"range":"2148","text":"2121"},{"range":"2149","text":"2120"},{"range":"2149","text":"2121"},{"range":"2150","text":"2120"},{"range":"2150","text":"2121"},{"range":"2151","text":"2120"},{"range":"2151","text":"2121"},{"range":"2152","text":"2120"},{"range":"2152","text":"2121"},{"range":"2153","text":"2120"},{"range":"2153","text":"2121"},{"range":"2154","text":"2120"},{"range":"2154","text":"2121"},{"range":"2155","text":"2120"},{"range":"2155","text":"2121"},{"range":"2156","text":"2120"},{"range":"2156","text":"2121"},{"range":"2157","text":"2120"},{"range":"2157","text":"2121"},{"range":"2158","text":"2120"},{"range":"2158","text":"2121"},{"range":"2159","text":"2120"},{"range":"2159","text":"2121"},{"range":"2160","text":"2120"},{"range":"2160","text":"2121"},{"range":"2161","text":"2120"},{"range":"2161","text":"2121"},{"range":"2162","text":"2120"},{"range":"2162","text":"2121"},{"range":"2163","text":"2120"},{"range":"2163","text":"2121"},{"range":"2164","text":"2120"},{"range":"2164","text":"2121"},{"range":"2165","text":"2120"},{"range":"2165","text":"2121"},{"range":"2166","text":"2120"},{"range":"2166","text":"2121"},"suggestOptionalChain",{"range":"2167","text":"2168"},"Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.",{"range":"2169","text":"2120"},{"range":"2169","text":"2121"},{"range":"2170","text":"2120"},{"range":"2170","text":"2121"},{"range":"2171","text":"2120"},{"range":"2171","text":"2121"},{"range":"2172","text":"2120"},{"range":"2172","text":"2121"},{"range":"2173","text":"2120"},{"range":"2173","text":"2121"},{"range":"2174","text":"2120"},{"range":"2174","text":"2121"},{"range":"2175","text":"2120"},{"range":"2175","text":"2121"},{"range":"2176","text":"2120"},{"range":"2176","text":"2121"},{"range":"2177","text":"2120"},{"range":"2177","text":"2121"},{"range":"2178","text":"2120"},{"range":"2178","text":"2121"},{"range":"2179","text":"2120"},{"range":"2179","text":"2121"},{"range":"2180","text":"2120"},{"range":"2180","text":"2121"},"removeEscape",{"range":"2181","text":"1711"},"Remove the `\\`. This maintains the current functionality.","escapeBackslash",{"range":"2182","text":"2183"},"Replace the `\\` with `\\\\` to include the actual backslash character.",{"range":"2184","text":"1711"},{"range":"2185","text":"2183"},{"range":"2186","text":"1711"},{"range":"2187","text":"2183"},{"range":"2188","text":"1711"},{"range":"2189","text":"2183"},{"range":"2190","text":"1711"},{"range":"2191","text":"2183"},{"range":"2192","text":"1711"},{"range":"2193","text":"2183"},{"range":"2194","text":"1711"},{"range":"2195","text":"2183"},{"range":"2196","text":"1711"},{"range":"2197","text":"2183"},{"range":"2198","text":"1711"},{"range":"2199","text":"2183"},{"range":"2200","text":"1711"},{"range":"2201","text":"2183"},{"range":"2202","text":"1711"},{"range":"2203","text":"2183"},{"range":"2204","text":"1711"},{"range":"2205","text":"2183"},{"range":"2206","text":"1711"},{"range":"2207","text":"2183"},{"range":"2208","text":"1711"},{"range":"2209","text":"2183"},{"range":"2210","text":"1711"},{"range":"2211","text":"2183"},{"range":"2212","text":"1711"},{"range":"2213","text":"2183"},{"range":"2214","text":"1711"},{"range":"2215","text":"2183"},{"range":"2216","text":"1711"},{"range":"2217","text":"2183"},{"range":"2218","text":"1711"},{"range":"2219","text":"2183"},{"range":"2220","text":"1711"},{"range":"2221","text":"2183"},{"range":"2222","text":"1711"},{"range":"2223","text":"2183"},{"range":"2224","text":"1711"},{"range":"2225","text":"2183"},{"range":"2226","text":"1711"},{"range":"2227","text":"2183"},{"range":"2228","text":"1711"},{"range":"2229","text":"2183"},{"range":"2230","text":"1711"},{"range":"2231","text":"2183"},{"range":"2232","text":"1711"},{"range":"2233","text":"2183"},{"range":"2234","text":"1711"},{"range":"2235","text":"2183"},{"range":"2236","text":"1711"},{"range":"2237","text":"2183"},{"range":"2238","text":"1711"},{"range":"2239","text":"2183"},{"range":"2240","text":"1711"},{"range":"2241","text":"2183"},{"range":"2242","text":"1711"},{"range":"2243","text":"2183"},{"range":"2244","text":"1711"},{"range":"2245","text":"2183"},{"range":"2246","text":"1711"},{"range":"2247","text":"2183"},{"range":"2248","text":"1711"},{"range":"2249","text":"2183"},{"range":"2250","text":"1711"},{"range":"2251","text":"2183"},{"range":"2252","text":"1711"},{"range":"2253","text":"2183"},{"range":"2254","text":"1711"},{"range":"2255","text":"2183"},{"range":"2256","text":"1711"},{"range":"2257","text":"2183"},{"range":"2258","text":"1711"},{"range":"2259","text":"2183"},{"range":"2260","text":"1711"},{"range":"2261","text":"2183"},{"range":"2262","text":"1711"},{"range":"2263","text":"2183"},{"range":"2264","text":"1711"},{"range":"2265","text":"2183"},{"range":"2266","text":"1711"},{"range":"2267","text":"2183"},{"range":"2268","text":"1711"},{"range":"2269","text":"2183"},{"range":"2270","text":"1711"},{"range":"2271","text":"2183"},{"range":"2272","text":"1711"},{"range":"2273","text":"2183"},{"range":"2274","text":"1711"},{"range":"2275","text":"2183"},{"range":"2276","text":"1711"},{"range":"2277","text":"2183"},{"range":"2278","text":"1711"},{"range":"2279","text":"2183"},{"range":"2280","text":"1711"},{"range":"2281","text":"2183"},{"range":"2282","text":"1711"},{"range":"2283","text":"2183"},{"range":"2284","text":"1711"},{"range":"2285","text":"2183"},{"range":"2286","text":"1711"},{"range":"2287","text":"2183"},{"range":"2288","text":"1711"},{"range":"2289","text":"2183"},{"range":"2290","text":"1711"},{"range":"2291","text":"2183"},{"range":"2292","text":"1711"},{"range":"2293","text":"2183"},{"range":"2294","text":"2120"},{"range":"2294","text":"2121"},{"range":"2295","text":"2120"},{"range":"2295","text":"2121"},{"range":"2296","text":"2120"},{"range":"2296","text":"2121"},{"range":"2297","text":"2120"},{"range":"2297","text":"2121"},{"range":"2298","text":"2120"},{"range":"2298","text":"2121"},{"range":"2299","text":"2120"},{"range":"2299","text":"2121"},{"range":"2300","text":"2120"},{"range":"2300","text":"2121"},{"range":"2301","text":"2120"},{"range":"2301","text":"2121"},{"range":"2302","text":"2120"},{"range":"2302","text":"2121"},{"range":"2303","text":"2120"},{"range":"2303","text":"2121"},{"range":"2304","text":"2120"},{"range":"2304","text":"2121"},{"range":"2305","text":"2120"},{"range":"2305","text":"2121"},{"range":"2306","text":"2120"},{"range":"2306","text":"2121"},{"range":"2307","text":"2120"},{"range":"2307","text":"2121"},{"range":"2308","text":"2120"},{"range":"2308","text":"2121"},{"range":"2309","text":"2120"},{"range":"2309","text":"2121"},{"range":"2310","text":"2120"},{"range":"2310","text":"2121"},{"range":"2311","text":"2120"},{"range":"2311","text":"2121"},{"range":"2312","text":"2120"},{"range":"2312","text":"2121"},{"range":"2313","text":"2120"},{"range":"2313","text":"2121"},{"range":"2314","text":"2120"},{"range":"2314","text":"2121"},{"range":"2315","text":"2120"},{"range":"2315","text":"2121"},{"range":"2316","text":"2120"},{"range":"2316","text":"2121"},{"range":"2317","text":"2120"},{"range":"2317","text":"2121"},{"range":"2318","text":"2120"},{"range":"2318","text":"2121"},{"range":"2319","text":"2120"},{"range":"2319","text":"2121"},{"range":"2320","text":"2120"},{"range":"2320","text":"2121"},{"range":"2321","text":"2120"},{"range":"2321","text":"2121"},{"range":"2322","text":"2120"},{"range":"2322","text":"2121"},{"range":"2323","text":"2120"},{"range":"2323","text":"2121"},{"range":"2324","text":"2120"},{"range":"2324","text":"2121"},{"range":"2325","text":"2120"},{"range":"2325","text":"2121"},{"range":"2326","text":"2120"},{"range":"2326","text":"2121"},{"range":"2327","text":"2120"},{"range":"2327","text":"2121"},{"range":"2328","text":"2120"},{"range":"2328","text":"2121"},{"range":"2329","text":"2120"},{"range":"2329","text":"2121"},{"range":"2330","text":"2120"},{"range":"2330","text":"2121"},{"range":"2331","text":"2120"},{"range":"2331","text":"2121"},{"range":"2332","text":"2120"},{"range":"2332","text":"2121"},{"range":"2333","text":"2120"},{"range":"2333","text":"2121"},{"range":"2334","text":"2120"},{"range":"2334","text":"2121"},{"range":"2335","text":"2120"},{"range":"2335","text":"2121"},{"range":"2336","text":"2120"},{"range":"2336","text":"2121"},{"range":"2337","text":"2120"},{"range":"2337","text":"2121"},{"range":"2338","text":"2120"},{"range":"2338","text":"2121"},{"range":"2339","text":"2120"},{"range":"2339","text":"2121"},{"range":"2340","text":"2120"},{"range":"2340","text":"2121"},{"range":"2341","text":"2120"},{"range":"2341","text":"2121"},{"range":"2342","text":"2120"},{"range":"2342","text":"2121"},{"range":"2343","text":"2120"},{"range":"2343","text":"2121"},{"range":"2344","text":"2120"},{"range":"2344","text":"2121"},{"range":"2345","text":"2120"},{"range":"2345","text":"2121"},{"range":"2346","text":"2120"},{"range":"2346","text":"2121"},{"range":"2347","text":"2120"},{"range":"2347","text":"2121"},{"range":"2348","text":"2120"},{"range":"2348","text":"2121"},{"range":"2349","text":"2120"},{"range":"2349","text":"2121"},{"range":"2350","text":"2120"},{"range":"2350","text":"2121"},{"range":"2351","text":"2120"},{"range":"2351","text":"2121"},{"range":"2352","text":"2120"},{"range":"2352","text":"2121"},{"range":"2353","text":"2120"},{"range":"2353","text":"2121"},[25508,25511],"unknown","never",[683,686],[6549,6552],[7583,7586],[8410,8413],[8699,8702],[14583,14586],[15764,15767],[16551,16554],[17120,17123],[334,337],[1592,1595],[250,253],[632,635],[1102,1105],[1121,1124],[1654,1657],[2841,2844],[2860,2863],[1003,1006],[2844,2847],[3799,3802],[1123,1126],[1388,1391],[3227,3230],[8750,8753],[13653,13656],[14176,14179],[16241,16244],[35305,35308],[7192,7195],[7370,7373],[6080,6083],[12636,12639],[14692,14695],[666,669],[736,739],[804,807],[1928,1931],[20438,20441],[23126,23129],[23910,23913],[29190,29193],[3573,3576],[3655,3658],[5784,5787],[6277,6278],"?",[1021,1024],[6080,6083],[6119,6122],[6182,6185],[6305,6308],[6344,6347],[6498,6501],[6525,6528],[7118,7121],[40187,40190],[41762,41765],[2322,2325],[11854,11855],[11854,11854],"\\",[11879,11880],[11879,11879],[11896,11897],[11896,11896],[11903,11904],[11903,11903],[11908,11909],[11908,11908],[11910,11911],[11910,11910],[11915,11916],[11915,11915],[11924,11925],[11924,11924],[11941,11942],[11941,11941],[11950,11951],[11950,11950],[11955,11956],[11955,11955],[11957,11958],[11957,11957],[11962,11963],[11962,11962],[11969,11970],[11969,11969],[11986,11987],[11986,11986],[11993,11994],[11993,11993],[11998,11999],[11998,11998],[12000,12001],[12000,12000],[12005,12006],[12005,12005],[12020,12021],[12020,12020],[12038,12039],[12038,12038],[12053,12054],[12053,12053],[12066,12067],[12066,12066],[12081,12082],[12081,12081],[12087,12088],[12087,12087],[12089,12090],[12089,12089],[12098,12099],[12098,12098],[12113,12114],[12113,12113],[12268,12269],[12268,12268],[12295,12296],[12295,12295],[12314,12315],[12314,12314],[12321,12322],[12321,12321],[12326,12327],[12326,12326],[12328,12329],[12328,12328],[12335,12336],[12335,12335],[12348,12349],[12348,12348],[12367,12368],[12367,12367],[12376,12377],[12376,12376],[12381,12382],[12381,12381],[12383,12384],[12383,12383],[12390,12391],[12390,12390],[12401,12402],[12401,12401],[12420,12421],[12420,12420],[12427,12428],[12427,12427],[12432,12433],[12432,12432],[12434,12435],[12434,12434],[12441,12442],[12441,12441],[12460,12461],[12460,12460],[12480,12481],[12480,12480],[12495,12496],[12495,12495],[12508,12509],[12508,12508],[12523,12524],[12523,12523],[12529,12530],[12529,12529],[12531,12532],[12531,12531],[12540,12541],[12540,12540],[12555,12556],[12555,12555],[8379,8382],[4489,4492],[16767,16770],[27450,27453],[27503,27506],[27576,27579],[27951,27954],[28634,28637],[28688,28691],[29791,29794],[29821,29824],[8719,8722],[8782,8785],[32306,32309],[32346,32349],[32371,32374],[32390,32393],[32408,32411],[32745,32748],[32869,32872],[33371,33374],[33513,33516],[34004,34007],[34130,34133],[311,314],[1735,1738],[1888,1891],[11643,11646],[11676,11679],[11975,11978],[19286,19289],[2154,2157],[5171,5174],[5747,5750],[6323,6326],[2242,2245],[1026,1029],[9393,9396],[15400,15403],[17701,17704],[17819,17822],[19540,19543],[19563,19566],[14650,14653],[728,731],[1481,1484],[683,686],[1066,1069],[1095,1098],[1158,1161],[1180,1183],[1252,1255],[1269,1272],[1281,1284],[1430,1433],[1459,1462],[1526,1529],[4017,4020],[4338,4341],[2003,2006]] \ No newline at end of file diff --git a/.gitignore b/.gitignore index cf8a9f2..c9762f2 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ package-lock.json !.yarn/releases !.yarn/sdks !.yarn/versions + +# venv +.venv/ diff --git a/Makefile b/Makefile index 5e14a86..c9a66c4 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,13 @@ .DEFAULT_GOAL := help pkg_src = coral -flake8 = flake8 $(pkg_src) setup.py -isort = isort $(pkg_src) setup.py black = black --line-length 140 $(pkg_src) setup.py +pyright = echo 'pyright not used' # pyright $(pkg_src) setup.py +ruff = ruff $(pkg_src) setup.py --line-length 140 --select E,W,F,N,I,C,B,UP,PT,SIM,RUF --ignore E501,C901,B008 + +.PHONY: start ## Start the development server +start: + python $(pkg_src) .PHONY: all ## Perform the most common development-time rules all: format lint test @@ -13,25 +17,25 @@ ci: check-format lint test .PHONY: format ## Auto-format the source code format: - $(isort) + $(ruff) --fix $(black) .PHONY: check-format ## Check the source code format without changes check-format: - $(isort) --check-only $(black) --check -.PHONY: lint ## Run flake8 +.PHONY: lint ## Run flake8 and pyright lint: - $(flake8) + $(ruff) --format=github + $(pyright) .PHONY: test ## Run tests test: pytest $(pkg_src) .PHONEY: documentation ## Generate docs -documentation: - mkdocs build +documentation: + echo "TODO" .PHONY: install ## Install the requirements install: @@ -41,6 +45,20 @@ install: develop: pip install -e .[develop] +.PHONY: env_encrypt ## Encrypts the current .//.env +env_encrypt: + openssl aes-256-cbc -pbkdf2 -in ./$(pkg_src)/.env -out ./$(pkg_src)/.env.enc + +.PHONY: env_decrypt ## Decrypts the .//.env.enc +env_decrypt: + @if [ -z "${ENV_PASSWORD}" ]; then \ + echo "No ENV_PASSWORD set, prompting for password..."; \ + openssl aes-256-cbc -pbkdf2 -d -in ./$(pkg_src)/.env.enc -out ./$(pkg_src)/.env; \ + else \ + echo "ENV_PASSWORD set, using it..."; \ + openssl aes-256-cbc -pbkdf2 -d -in ./$(pkg_src)/.env.enc -out ./$(pkg_src)/.env -pass env:ENV_PASSWORD; \ + fi + .PHONY: build ## Build a wheel build: python setup.py sdist bdist_wheel --dist-dir dist_python diff --git a/coral/__init__.py b/coral/__init__.py index ccdc298..0eaba97 100644 --- a/coral/__init__.py +++ b/coral/__init__.py @@ -4,10 +4,8 @@ # Licensed under the new BSD license, available at http://caleydo.org/license ############################################################################### from os import path -from typing import Type -from pydantic import BaseModel -from tdp_core.plugin.model import AVisynPlugin, RegHelper +from visyn_core.plugin.model import AVisynPlugin, RegHelper from .settings import CoralSettings @@ -34,5 +32,5 @@ def register(self, registry: RegHelper): ) @property - def setting_class(self) -> Type[BaseModel]: + def setting_class(self) -> type[CoralSettings]: return CoralSettings diff --git a/coral/db.py b/coral/db.py index f76a859..0e49ed8 100644 --- a/coral/db.py +++ b/coral/db.py @@ -12,7 +12,7 @@ _log.info("Setting up the db view.") # main dictionary containing all views registered for this plugin -views = dict() +views = {} schema = "cohort" # create a view + idtype for each available table diff --git a/coral/migration/env.py b/coral/migration/env.py index ea7b7dd..5a09426 100644 --- a/coral/migration/env.py +++ b/coral/migration/env.py @@ -1,3 +1,3 @@ -import tdp_core.dbmigration.env # NOQA +import visyn_core.dbmigration.env -tdp_core.dbmigration.env.run_migrations_online() +visyn_core.dbmigration.env.run_migrations_online() diff --git a/coral/settings.py b/coral/settings.py index 822d0d3..3526516 100644 --- a/coral/settings.py +++ b/coral/settings.py @@ -1,7 +1,5 @@ -from typing import Dict - from pydantic import BaseModel -from tdp_core import manager +from visyn_core import manager class CoralSettings(BaseModel): @@ -11,7 +9,7 @@ class CoralSettings(BaseModel): statement_timeout: str = "300000" supp_statement_timeout: str = "40000" statement_timeout_query: str = "set statement_timeout to {}" - logging: Dict = {"version": 1, "disable_existing_loggers": False, "loggers": {"coral": {"level": "DEBUG"}}} + logging: dict = {"version": 1, "disable_existing_loggers": False, "loggers": {"coral": {"level": "DEBUG"}}} def get_settings() -> CoralSettings: diff --git a/coral/sql.py b/coral/sql.py index a51ce6a..f703f4a 100644 --- a/coral/sql.py +++ b/coral/sql.py @@ -1,7 +1,7 @@ import logging from flask import Flask, abort, jsonify, request -from tdp_core.security import login_required +from visyn_core.security import login_required from .settings import get_settings from .sql_query_mapper import QueryElements diff --git a/coral/sql_query_mapper.py b/coral/sql_query_mapper.py index d5e4d9b..c73c2da 100644 --- a/coral/sql_query_mapper.py +++ b/coral/sql_query_mapper.py @@ -7,7 +7,7 @@ from sqlalchemy import create_engine, exc, inspect, text from sqlalchemy.exc import NoInspectionAvailable from sqlalchemy.orm import sessionmaker -from tdp_core import manager +from visyn_core import manager from .settings import get_settings from .sql_tables import Cohort @@ -95,7 +95,7 @@ def to_dict(self, row): try: return {c.key: getattr(row, c.key) for c in inspect(row).mapper.column_attrs} except NoInspectionAvailable: # this exception is raised when the result is not of a registered sqlalchemy type due to which the inspection fails - result = dict() + result = {} result[COLUMN_LABEL_SCORE] = row[0] result[COLUMN_LABEL_ID] = row[1] return result @@ -145,10 +145,7 @@ def get_cohorts_by_id_sql(self, args, error_msg): str_values = str_values + ("{val}, ".format(val=curr_val)) cnt += 1 - if cnt > 0: - str_values = str_values[:-2] # remove the last ', ' from the value list - else: - str_values = "-1" # returns no cohorts -> ids are only positiv + str_values = str_values[:-2] if cnt > 0 else "-1" # returns no cohorts -> ids are only positiv sql_text = "SELECT id, name, is_initial, previous_cohort, entity_database, entity_schema, entity_table FROM cohort.cohort c WHERE c.id IN ({str_values})".format( str_values=str_values @@ -173,7 +170,7 @@ def update_cohort_name_sql(self, args, error_msg): session_data.query(Cohort).filter(Cohort.id == cohort_id).update({Cohort.name: name}, synchronize_session=False) # synchronize_session - # False - don’t synchronize the session. This option is the most efficient and is reliable once the session is expired, + # False - don`t synchronize the session. This option is the most efficient and is reliable once the session is expired, # which typically occurs after a commit(), or explicitly using expire_all(). Before the expiration, # updated objects may still remain in the session with stale values on their attributes, which can lead to confusing results. @@ -341,19 +338,13 @@ def equals_filter_statement(self, prefix, attribute, values, numeric): if val.startswith("!"): add_equals_not_value = True val = val[1:] - if numeric in ["true"]: # determine if the values are strings or numbers - curr_val = "{val}".format(val=val) - else: - curr_val = "'{val}'".format(val=val) + curr_val = "{val}".format(val=val) if numeric in ["true"] else "'{val}'".format(val=val) str_not_values = str_not_values + ("{val}, ".format(val=curr_val)) else: add_equals_value = True - if numeric in ["true"]: # determine if the values are strings or numbers - curr_val = "{val}".format(val=val) - else: - curr_val = "'{val}'".format(val=val) + curr_val = "{val}".format(val=val) if numeric in ["true"] else "'{val}'".format(val=val) str_values = str_values + ("{val}, ".format(val=curr_val)) @@ -455,10 +446,7 @@ def create_cohort_treatment_filtered(self, args, cohort, error_msg): raise RuntimeError(error_msg) array_operation = "=" - if base_agent in ["true"]: # determine if the values are strings or numbers - array_operation = "@>" - else: - array_operation = "=" + array_operation = "@>" if base_agent in ["true"] else "=" entity_id_col = "" if cohort.entity_table == "tdp_tissue": @@ -508,10 +496,7 @@ def create_cohort_treatment_filtered(self, args, cohort, error_msg): # regimen is defined if regimen is not None: reg_sql = ("tmp.rn::int = {val}").format(val=regimen) - if agent is not None: - sql_where = sql_where + " AND " + reg_sql - else: - sql_where = reg_sql + sql_where = sql_where + " AND " + reg_sql if agent is not None else reg_sql # SQL with the combined filter (actual agent values, null values) sql_refiend = "" @@ -555,10 +540,7 @@ def create_cohort_treatment_filtered(self, args, cohort, error_msg): base_table=cohort.entity_table, ) - if agent_exists: - sql_refiend = sql_refiend + " UNION " + sql_null - else: - sql_refiend = sql_refiend + sql_null + sql_refiend = sql_refiend + " UNION " + sql_null if agent_exists else sql_refiend + sql_null # complete SQL statement that filters the data based on the given cohort new_sql_text = """SELECT cohort.* FROM ({entities}) cohort diff --git a/coral/tests/conftest.py b/coral/tests/conftest.py new file mode 100755 index 0000000..9db3a89 --- /dev/null +++ b/coral/tests/conftest.py @@ -0,0 +1,34 @@ +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient +from visyn_core.security.manager import SecurityManager +from visyn_core.security.model import User +from visyn_core.server.visyn_server import create_visyn_server +from visyn_core.tests.fixtures.postgres_db import postgres_db + +assert postgres_db # silence unused import warning + + +@pytest.fixture(scope="session") +def app(postgres_db) -> FastAPI: + server = create_visyn_server( + workspace_config={ + "tdp_core": {"enabled_plugins": ["tdp_core", "coral"]}, + "coral": { + "dburl": postgres_db.url, + }, + } + ) + + return server + + +@pytest.fixture() +def client(monkeypatch, app: FastAPI): + def mock_current_user_in_manager(self): + return User(id="admin") + + monkeypatch.setattr(SecurityManager, "current_user", property(mock_current_user_in_manager)) + + with TestClient(app) as client: + yield client diff --git a/coral/tests/test_api.py b/coral/tests/test_api.py new file mode 100644 index 0000000..6ed2de4 --- /dev/null +++ b/coral/tests/test_api.py @@ -0,0 +1,5 @@ +from fastapi.testclient import TestClient + + +def test_loggedinas(client: TestClient): + assert client.get("/loggedinas").json()["name"] == "admin" diff --git a/coral/tests/test_dummy.py b/coral/tests/test_dummy.py deleted file mode 100644 index 360271b..0000000 --- a/coral/tests/test_dummy.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_dummy(): - assert "it works" diff --git a/package.json b/package.json index c845baa..195cc9d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "coral", "description": "Coral is a web-based cohort analysis tool to interactively create, refine, and analyze patient cohorts.", "homepage": "https://caleydo.org", - "version": "4.0.1-SNAPSHOT", + "version": "4.1.1-SNAPSHOT", "author": { "name": "PatrickAdelberger", "email": "coral@caleydo.org", diff --git a/requirements_dev.txt b/requirements_dev.txt index 6d5e044..4193b14 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,9 +1,5 @@ -black~=22.3.0 -debugpy~=1.5.1 -flake8~=4.0.1 -isort~=5.10.1 -mkdocs-material~=8.2.8 -pep8-naming~=0.12.1 +black~=22.12.0 +pyright~=1.1.285 pytest-runner~=6.0.0 -pytest~=7.1.1 -recommonmark~=0.7.1 \ No newline at end of file +pytest~=7.2.0 +ruff==0.0.218 diff --git a/setup.py b/setup.py index 2921a2e..40c967f 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ def requirements(file): package_data={}, # Although 'package_data' is the preferred approach, in some case you may # need to place data files outside of your packages. See: - # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa + # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # In this case, 'data_file' will be installed into '/my_data' data_files=[], # [('my_data', ['data/data_file'])], ) diff --git a/src/Cohort.ts b/src/Cohort.ts index a31deda..736915d 100644 --- a/src/Cohort.ts +++ b/src/Cohort.ts @@ -1,4 +1,6 @@ -import { IAllFilters, IDType, IDTypeLike, IDTypeManager, IRow, IServerColumn, UniqueIdManager } from 'tdp_core'; +/* eslint-disable @typescript-eslint/no-use-before-define */ +import { IDType, IDTypeLike, IDTypeManager, IRow, IServerColumn } from 'visyn_core'; +import { IAllFilters, UniqueIdManager } from 'tdp_core'; import { ECloneFilterTypes, EElementProvType, @@ -30,6 +32,17 @@ import { dataDBCohortWithNumFilter, getCohortData, getCohortSize, + sizeDBCohortDepletionScoreFilter, + sizeDBCohortGeneWithEqualsFilter, + sizeDBCohortGeneWithNumFilter, + sizeDBCohortPanelAnnotationFilter, + sizeDBCohortWithEqualsFilter, + sizeDBCohortWithNumFilter, + updateCohortName, +} from './base/rest'; +import type { Task } from './Tasks'; +import { deepCopy, handleDataLoadError, handleDataSaveError, log, mergeTwoAllFilters } from './util'; +import { ICohortDBDataParams, ICohortDBParams, ICohortDBSizeParams, @@ -49,17 +62,8 @@ import { ICohortRow, IEqualsList, INumRange, - sizeDBCohortDepletionScoreFilter, - sizeDBCohortGeneWithEqualsFilter, - sizeDBCohortGeneWithNumFilter, - sizeDBCohortPanelAnnotationFilter, - sizeDBCohortWithEqualsFilter, - sizeDBCohortWithNumFilter, - updateCohortName, valueListDelimiter, -} from './base/rest'; -import { mergeTwoAllFilters, Task } from './Tasks'; -import { deepCopy, handleDataLoadError, handleDataSaveError, log } from './util'; +} from './base/interfaces'; type ICreateMethod = ( params: diff --git a/src/CohortContext.ts b/src/CohortContext.ts new file mode 100644 index 0000000..c1837bd --- /dev/null +++ b/src/CohortContext.ts @@ -0,0 +1,15 @@ +import type { ICohort } from './app/interfaces'; +import type { CohortOverview } from './Overview/CohortOverview'; +import type Taskview from './Taskview/Taskview'; + +export interface ICohortContext { + cohortOverview: CohortOverview; + taskview: Taskview; + referenceCohort: ICohort; +} + +export const CohortContext: ICohortContext = { + cohortOverview: null, + taskview: null, + referenceCohort: null, +}; diff --git a/src/CohortRepresentations.ts b/src/CohortRepresentations.ts index e6a9be9..a277140 100644 --- a/src/CohortRepresentations.ts +++ b/src/CohortRepresentations.ts @@ -1,7 +1,7 @@ import { hsl } from 'd3v7'; import tippy from 'tippy.js'; import { IBloodlineElement, ICohort, IElement, IRectCohortRep } from './app/interfaces'; -import { getRootCohort } from './cohortview'; +import { CohortContext } from './CohortContext'; import { log } from './util'; import { CohortRemoveEvent, CohortSelectionEvent } from './base/events'; import { labelFromFilter } from './utils/labels'; @@ -136,7 +136,7 @@ export class RectCohortRep implements IRectCohortRep { container.addEventListener('mouseenter', (event) => { event.stopPropagation(); // remove icon (=trash can) - const isRoot = this._cohort.dbId === getRootCohort().dbId; + const isRoot = this._cohort.dbId === CohortContext.referenceCohort?.dbId; const isPreview = this._representation.classList.contains('preview'); // onyl if it is not a preview if (!isPreview) { @@ -507,9 +507,9 @@ export class RectCohortRep implements IRectCohortRep { log.debug('cohortValues: ', cohortValues); const detailedLabelArray = []; const labels = cohortObj.labelOne.split(', '); - for (let i = 0; i < cohortValues.length; i++) { - const val = cohortValues[i]; - const label = labels[i]; + for (let j = 0; j < cohortValues.length; j++) { + const val = cohortValues[j]; + const label = labels[j]; let labelpart = ''; if (Array.isArray(val)) { labelpart = val.map((a) => labelFromFilter(a, label)).join(' / '); @@ -517,7 +517,7 @@ export class RectCohortRep implements IRectCohortRep { labelpart = labelFromFilter(val, label); } detailedLabelArray.push(labelpart); - ttInfo.attr.push({ name: attrLabel[i], value: labelpart }); + ttInfo.attr.push({ name: attrLabel[j], value: labelpart }); } const detailedLabel = detailedLabelArray.join(', '); const valueLabel = detailedLabel.replace(htmlLte, '<=').replace(htmlLt, '<').replace(htmlGte, '>=').replace(htmlGt, '>'); @@ -568,21 +568,22 @@ export class RectCohortRep implements IRectCohortRep { // cohort info // name - const currChtInfo = ttInfo[ttInfo.length - 1]; - const chtName = document.createElement('div'); - chtName.innerHTML = currChtInfo.chtName; - chtName.style.fontWeight = 'bold'; - // values - const chtValues = document.createElement('div'); - for (const a of currChtInfo.attr) { - const currAttr = document.createElement('div'); - currAttr.innerHTML = `${a.name}: ${a.value}`; - currAttr.style.paddingLeft = '15px'; - chtValues.appendChild(currAttr); + { + const currChtInfo = ttInfo[ttInfo.length - 1]; + const chtName = document.createElement('div'); + chtName.innerHTML = currChtInfo.chtName; + chtName.style.fontWeight = 'bold'; + // values + const chtValues = document.createElement('div'); + for (const a of currChtInfo.attr) { + const currAttr = document.createElement('div'); + currAttr.innerHTML = `${a.name}: ${a.value}`; + currAttr.style.paddingLeft = '15px'; + chtValues.appendChild(currAttr); + } + ctrCohort.appendChild(chtName); + ctrCohort.appendChild(chtValues); } - ctrCohort.appendChild(chtName); - ctrCohort.appendChild(chtValues); - if (!root) { // provenance // label for provenance diff --git a/src/OnboardingManager.ts b/src/OnboardingManager.ts index 81558ce..e3162a2 100644 --- a/src/OnboardingManager.ts +++ b/src/OnboardingManager.ts @@ -1,5 +1,5 @@ import * as loMerge from 'lodash.merge'; -import tippy, { hideAll, Props, Instance as TippyInstance } from 'tippy.js'; +import tippy, { Props, Instance as TippyInstance } from 'tippy.js'; import { CONFIG_ONBOARDING } from './config/onboarding'; import { log, hasCookie } from './util'; @@ -17,7 +17,7 @@ export class OnboardingManager { const tipConfig = CONFIG_ONBOARDING.tooltips[tipId]; let tip; if (tipConfig) { - this.tooltips.forEach((tip) => tip.clearDelayTimeouts()); + this.tooltips.forEach((ttip) => ttip.clearDelayTimeouts()); const cookieID = `${tipId}_onboarded`; const showOnCreate = !hasCookie(cookieID) || forceShow; // show if there is no cookie tip = tippy(elem, { @@ -26,6 +26,7 @@ export class OnboardingManager { showOnCreate, }); this.tooltips.set(tipId, tip); + // eslint-disable-next-line @typescript-eslint/no-use-before-define setOnboardingCookie(cookieID); } else { log.warn('No config for tooltip', tipId, 'Skip tooltip creation'); diff --git a/src/Overview/CohortOverview.ts b/src/Overview/CohortOverview.ts index 5ce1970..aa79261 100644 --- a/src/Overview/CohortOverview.ts +++ b/src/Overview/CohortOverview.ts @@ -1,6 +1,6 @@ import { IObjectRef, ObjectRefUtils, ProvenanceGraph, UniqueIdManager, IDatabaseViewDesc } from 'tdp_core'; import tippy from 'tippy.js'; -import { CoralApp } from '../app/CoralApp'; +import type { CoralApp } from '../app/CoralApp'; import { Cohort, createCohortFromDB } from '../Cohort'; import { EElementProvType, @@ -15,8 +15,6 @@ import { TaskType, } from '../app/interfaces'; import { RectCohortRep } from '../CohortRepresentations'; -import { IAttribute } from '../data/Attribute'; -import { addOverviewCohortAction, removeOverviewCohortAction } from '../Provenance/CohortEV'; import { getDBCohortData } from '../base/rest'; import { RectTaskRep } from '../TaskRepresentations'; import { createTaskFromProvJSON, Task, TaskFilter, TaskSplit } from '../Tasks'; @@ -34,6 +32,8 @@ import { } from '../base/events'; import { niceName } from '../utils/labels'; import { RectangleLayout } from './OverviewLayout'; +import { IAttribute } from '../data/IAttribute'; +import { addOverviewCohortAction, removeOverviewCohortAction } from '../Provenance/CohortEV'; export class CohortOverview { private root: ICohort; diff --git a/src/Provenance/CohortEV.ts b/src/Provenance/CohortEV.ts index 69956b5..f2200fe 100644 --- a/src/Provenance/CohortEV.ts +++ b/src/Provenance/CohortEV.ts @@ -1,7 +1,8 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ import { ActionMetaData, ActionUtils, ICmdResult, IObjectRef, ObjectRefUtils } from 'tdp_core'; -import { CoralApp } from '../app/CoralApp'; +import type { CoralApp } from '../app/CoralApp'; import { IElementProvJSON } from '../app/interfaces'; -import { CohortOverview } from '../Overview/CohortOverview'; +import type { CohortOverview } from '../Overview/CohortOverview'; import { log } from '../util'; /** ********************************************* diff --git a/src/Provenance/General.ts b/src/Provenance/General.ts index 1f2172d..4a5429a 100644 --- a/src/Provenance/General.ts +++ b/src/Provenance/General.ts @@ -1,5 +1,5 @@ import { ActionMetaData, ActionUtils, ICmdResult, IObjectRef, ObjectRefUtils } from 'tdp_core'; -import { CoralApp } from '../app/CoralApp'; +import type { CoralApp } from '../app/CoralApp'; import { IElementProvJSON, IElementProvJSONCohort } from '../app/interfaces'; import { log } from '../util'; import { IEntitySourceConfig } from '../config/entities'; @@ -16,6 +16,7 @@ export function setDatasetAction(provider: IObjectRef, newDataset: IDa return ActionUtils.action( ActionMetaData.actionMeta('Change Dataset', ObjectRefUtils.category.data, ObjectRefUtils.operation.update), 'chtSetDataset', + // eslint-disable-next-line @typescript-eslint/no-use-before-define setDatasetImpl, [provider], { diff --git a/src/Tasks.ts b/src/Tasks.ts index efcd659..a969c04 100644 --- a/src/Tasks.ts +++ b/src/Tasks.ts @@ -1,7 +1,53 @@ -import { IAllFilters, IParams } from 'tdp_core'; +import { IServerColumn } from 'visyn_core'; +import { log } from './util'; import { EElementProvType, IElement, IElementProvJSON, IElementProvJSONTask, ITask, ITaskRep, TaskType } from './app/interfaces'; -import { IAttribute, toAttribute } from './data/Attribute'; -import { deepCopy, log } from './util'; +import { GeneScoreAttribute, PanelScoreAttribute, ServerColumnAttribute, SpecialAttribute } from './data/Attribute'; +import type { IAttribute, IOption, IScoreOption, IServerColumnOption } from './data/IAttribute'; +import type { ISpecialAttribute } from './data/ISpecialAttribute'; + +export interface ISpecialOption extends IServerColumnOption { + optionData: { + serverColumn: IServerColumn; + sAttrId: string; + attrOption: string; + spAttribute: ISpecialAttribute; + }; +} + +export function toAttribute(option: IOption, currentDB, currentView): IAttribute { + if (option.optionType === 'dbc') { + if (option.optionData && (option as ISpecialOption).optionData.spAttribute) { + // Create Special Attribtues + // if (option.optionData.spA === 'treatment') { + log.debug('create special Attribute: ', option.optionId); + log.debug('special Attribute object: ', option.optionData.spAttribute); + return new SpecialAttribute( + option.optionId, + currentView, + currentDB, + (option as ISpecialOption).optionData.spAttribute, + (option as ISpecialOption).optionData.attrOption, + ); + } + // Create Attribute + return new ServerColumnAttribute(option.optionId, currentView, currentDB, (option as IServerColumnOption).optionData.serverColumn); + } + // Create ScoreAttribute + if (option.optionType === 'gene') { + return new GeneScoreAttribute( + option.optionId, + option.optionText, + currentView, + currentDB, + (option as IScoreOption).optionData.type, + (option as IScoreOption).optionData.subType, + ); + } + if (option.optionType === 'panel') { + return new PanelScoreAttribute(option.optionId, currentView, currentDB, 'categorical'); + } + return null; +} export abstract class Task implements ITask { id: string; @@ -84,94 +130,3 @@ export function createTaskFromProvJSON(config: IElementProvJSON): Task { } return new TaskSplit(config.id, config.label, attributes); } - -function mergerAllFilterPart(filterType: FilterType, existingFilter: IParams, newFilter: IParams): IParams { - let mergedFilter = deepCopy(existingFilter); - // const currType : FilterType = FilterType.normal; - let filterContradiction = false; - // go through all attribuets of the new filter - for (const attr in newFilter) { - // check if attribute exists in the new filter - if (Object.prototype.hasOwnProperty.call(newFilter, attr)) { - // check if attribute exists in the existing filter - if (Object.prototype.hasOwnProperty.call(existingFilter, attr)) { - const newVal = newFilter[attr]; // current value for attribute - const existVal = existingFilter[attr]; // current value for attribute - - if (filterType === FilterType.normal) { - // convert newVal to an array of values - const newValArray = Array.isArray(newVal) ? (newVal as string[] | number[] | boolean[]) : new Array(newVal as string | number | boolean); - // convert existVal to an array of values - const existValArray = Array.isArray(existVal) ? (existVal as string[] | number[] | boolean[]) : new Array(existVal as string | number | boolean); - // if all new values are part of the existing values, then the filter is OK - for (const nv of newValArray) { - if (!existValArray.includes(nv)) { - filterContradiction = true; - } - } - // no filter contradiction -> new filter values can be used - if (!filterContradiction) { - // outside of for-loop - mergedFilter[attr] = newFilter[attr]; - } - } else if (filterType === FilterType.lt || filterType === FilterType.lte) { - // smaller is OK - if (newVal <= existVal) { - mergedFilter[attr] = newFilter[attr]; - } else { - filterContradiction = true; - } - } else if (filterType === FilterType.gt || filterType === FilterType.gte) { - // bigger is OK - if (newVal >= existVal) { - mergedFilter[attr] = newFilter[attr]; - } else { - filterContradiction = true; - } - } - } else { - // the new filter attribute is not existing -> add to the resulting filter - mergedFilter[attr] = newFilter[attr]; - } - } - } - - if (filterContradiction) { - mergedFilter = null; - } - log.debug('Filter Contradiction: ', filterContradiction); - return mergedFilter; -} - -export function mergeTwoAllFilters(existingFilter: IAllFilters, newFilter: IAllFilters): IAllFilters { - let mergerAllFilter: IAllFilters = null; - if (existingFilter !== null) { - const normal: IParams = mergerAllFilterPart(FilterType.normal, existingFilter.normal, newFilter.normal); - const lt: IParams = mergerAllFilterPart(FilterType.lt, existingFilter.lt, newFilter.lt); - const lte: IParams = mergerAllFilterPart(FilterType.lte, existingFilter.lte, newFilter.lte); - const gt: IParams = mergerAllFilterPart(FilterType.gt, existingFilter.gt, newFilter.gt); - const gte: IParams = mergerAllFilterPart(FilterType.gte, existingFilter.gte, newFilter.gte); - - // if the filters are all !== null, that means there is no filter contradiction -> set all filters of mergerAllFilter - // otherwise the mergerAllFilter stays null - if (normal !== null && lte !== null && lte !== null && gt !== null && gte !== null) { - mergerAllFilter = { - normal, - lt, - lte, - gt, - gte, - }; - } - } - - return mergerAllFilter; -} - -enum FilterType { - normal, - lt, - lte, - gt, - gte, -} diff --git a/src/Taskview/SearchBar.ts b/src/Taskview/SearchBar.ts index c86b401..3f5fe72 100644 --- a/src/Taskview/SearchBar.ts +++ b/src/Taskview/SearchBar.ts @@ -1,11 +1,14 @@ import { select } from 'd3v7'; -import { IdTextPair, IServerColumn, RestBaseUtils } from 'tdp_core'; +import { IServerColumn } from 'visyn_core'; +import { IdTextPair, RestBaseUtils } from 'tdp_core'; import { dataTypes, depletion, IDataSubtypeConfig, IDataTypeConfig } from 'tdp_publicdb'; import { colors } from '../config/colors'; -import { ScoreType } from '../data/Attribute'; -import { checkSpecialAttribute, ISpecialAttribute } from '../data/SpecialAttribute'; import { deepCopy, getAnimatedLoadingText, log } from '../util'; import { niceName } from '../utils/labels'; +import { IOption, IPanelOption, ISearchBarGroup, IServerColumnOption, OptionType } from '../data/IAttribute'; +import { ISpecialOption } from '../Tasks'; +import type { ISpecialAttribute } from '../data/ISpecialAttribute'; +import { checkSpecialAttribute } from '../data/SpecialAttribute'; export class SearchBar { private _container: HTMLDivElement; @@ -392,7 +395,7 @@ export class SearchBar { }; switch (type) { - case 'dbc': + case 'dbc': { const spAttr: ISpecialAttribute = checkSpecialAttribute(option.optionId); if (spAttr) { (option as ISpecialOption).optionData = { @@ -406,6 +409,7 @@ export class SearchBar { } break; + } case 'gene': // Nothing to do here, added in clickHandler break; @@ -415,6 +419,8 @@ export class SearchBar { species: data.species, }; break; + default: + break; } return option; @@ -919,15 +925,15 @@ export class SearchBar { .enter() .append('div') .attr('class', 'detail-info-option option-element') - .attr('data-optid', (d: IDataSubtypeConfig) => { - return this._composeGeneDataTypeOptId(optionId, d.id); + .attr('data-optid', (v: IDataSubtypeConfig) => { + return this._composeGeneDataTypeOptId(optionId, v.id); }) - .classed('option-selected', (d: IDataSubtypeConfig) => { - return badgeIds.indexOf(this._composeGeneDataTypeOptId(optionId, d.id)) !== -1; + .classed('option-selected', (v: IDataSubtypeConfig) => { + return badgeIds.indexOf(this._composeGeneDataTypeOptId(optionId, v.id)) !== -1; }) - .html((d: IDataSubtypeConfig) => d.name) - .on('click', (event, d: IDataSubtypeConfig) => { - const badgeName = this._composeGeneDataTypeName(data.optionText, d.name); + .html((v: IDataSubtypeConfig) => v.name) + .on('click', (event, v: IDataSubtypeConfig) => { + const badgeName = this._composeGeneDataTypeName(data.optionText, v.name); const badgeData = deepCopy(data); badgeData.optionData = { subType: d, type: (d as any).dataTypeId }; this._clickHandlerDetail(data, badgeData, badgeName, event as MouseEvent, event.currentTarget as HTMLElement); @@ -952,8 +958,7 @@ export class SearchBar { const detailText = document.createElement('p'); detailText.classList.add('option-element'); detail.appendChild(detailText); - detailText.innerHTML = - `ID: ${option.optionId}
` + `Description: ${option.optionData.description}`; + detailText.innerHTML = `ID: ${option.optionId}
Description: ${option.optionData.description}`; return detail; } @@ -1085,49 +1090,3 @@ export class SearchBar { this._setPlaceholder(); } } - -export interface ISearchBarGroup { - groupLabel: string; - data: Array; -} - -export type OptionType = 'dbc' | 'gene' | 'panel'; - -export interface IOption { - // id: string; - optionId: string; // e.g. gender or ensg00000141510 - optionType: OptionType; // e.g. dbc or gene - optionText: string; // e.g. Gender or TP53 - optionData?: { - [key: string]: any; // keys are always strings, so we just specify it to be key/value pairs with values of any type - }; -} - -export interface IPanelOption extends IOption { - optionData: { - description: string; // e.g. "Cancer Cell Line Encyclopedia" - species: string; // e.g. human - }; -} - -export interface IScoreOption extends IOption { - optionData: { - type: ScoreType; // id of = IDataTypeConfig; - subType: IDataSubtypeConfig; - }; -} - -export interface IServerColumnOption extends IOption { - optionData: { - serverColumn: IServerColumn; - }; -} - -export interface ISpecialOption extends IServerColumnOption { - optionData: { - serverColumn: IServerColumn; - sAttrId: string; - attrOption: string; - spAttribute: ISpecialAttribute; - }; -} diff --git a/src/Taskview/SearchColumn.ts b/src/Taskview/SearchColumn.ts index e0a1305..43b976d 100644 --- a/src/Taskview/SearchColumn.ts +++ b/src/Taskview/SearchColumn.ts @@ -1,14 +1,15 @@ import { select } from 'd3v7'; import * as $ from 'jquery'; import tippy from 'tippy.js'; -import { ICohort } from '../app'; -import { IAttribute, toAttribute } from '../data/Attribute'; +import type { ICohort } from '../app/interfaces'; +import { IAttribute } from '../data/IAttribute'; +import { toAttribute } from '../Tasks'; import searchHtml from '../templates/SearchColumn.html'; // webpack imports html to variable import { log } from '../util'; import { SearchBar } from './SearchBar'; import { ATask, TaskCloseEvent, TASK_CLOSE_EVENT_TYPE } from './tasks/ATask'; import { TASKLIST } from './tasks/TaskList'; -import Taskview from './Taskview'; +import type Taskview from './Taskview'; export default class SearchColumn { private $searchColumn: HTMLDivElement; @@ -137,6 +138,7 @@ export default class SearchColumn { private enableAddButtons(enable: boolean): void { if (enable) { // add eventListeners + // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this; select(this.$searchColumn) .selectAll('div.action.add') diff --git a/src/Taskview/Taskview.ts b/src/Taskview/Taskview.ts index d33a2d2..be7f825 100644 --- a/src/Taskview/Taskview.ts +++ b/src/Taskview/Taskview.ts @@ -1,11 +1,4 @@ -import { EMPTY_COHORT_ID, getEmptyCohort, getLoaderCohort, LOADER_COHORT_ID } from '../Cohort'; import { ICohort, IInputCohort, IOutputCohort, ITaskParams, TaskType } from '../app/interfaces'; -import { RectCohortRep } from '../CohortRepresentations'; -import { IAttribute, multiAttributeFilter } from '../data/Attribute'; -import { RectangleLayout } from '../Overview/OverviewLayout'; -import { log, removeFromArray, SortType } from '../util'; -import { CoralColorSchema } from '../config/colors'; -import { ScrollLinker } from '../utils/ScrollLinker'; import { CohortSelectionEvent, COHORT_SELECTION_EVENT_TYPE, @@ -22,658 +15,670 @@ import { SplitEvent, SPLIT_EVENT_TYPE, } from '../base/events'; +import { RectCohortRep } from '../CohortRepresentations'; +import { CoralColorSchema } from '../config/colors'; +import { IAttribute } from '../data/IAttribute'; +import { SortType, removeFromArray, log } from '../util'; +import { ScrollLinker } from '../utils/ScrollLinker'; +import SearchColumn from './SearchColumn'; import AddColumnColumn, { AColumn, ADataColumn, EmptyColumn } from './columns/AColumn'; -import AttributeColumn from './columns/AttributeColumn'; import { InputCohortColumn, OutputCohortColumn } from './columns/CohortColumn'; -import SearchColumn from './SearchColumn'; - -export default class Taskview { - public destroy() { - this.$node.removeEventListener(FILTER_EVENT_TYPE, this.filterListener); - this.$node.removeEventListener(SPLIT_EVENT_TYPE, this.splitListener); - this.$node.removeEventListener(CONFIRM_OUTPUT_EVENT_TYPE, this.confirmListener); - - this.scrollLink.destroy(); - - this.search.destroy(); - this.input.destroy(); - this.output.destroy(); - - this.$node.classList.remove('task-view'); - while (this.$node.hasChildNodes()) { - this.$node.removeChild(this.$node.lastChild); - } - } - - clearOutput() { - this.output.clear(); - this.input.update(); - } - - private input: TaskviewInput; - - private search: SearchColumn; +import { EMPTY_COHORT_ID, getEmptyCohort, getLoaderCohort, LOADER_COHORT_ID } from '../Cohort'; +import { multiAttributeFilter } from '../data/Attribute'; +import AttributeColumn from './columns/AttributeColumn'; +import { RectangleLayout } from '../Overview/OverviewLayout'; - private output: TaskviewOutput; +abstract class TaskviewTable { + $node: HTMLDivElement; - private scrollLink: ScrollLinker; + $floatingBtns: HTMLDivElement; - private taskParams: ITaskParams[]; + columns: AColumn[] = []; - private taskAttributes: IAttribute[]; + cohorts: ICohort[] = []; - private filterListener: EventListenerOrEventListenerObject = (ev) => this.handleFilterEvent(ev as FilterEvent); + constructor(private $wrapper: HTMLDivElement) { + this.$wrapper.classList.add('task-view-table-button-wrapper'); - private splitListener: EventListenerOrEventListenerObject = (ev) => this.handleFilterEvent(ev as SplitEvent); + const tableWrapper = document.createElement('div'); + tableWrapper.classList.add('task-view-scroll-wrapper'); // this div can only be around half the height of the browser (which would lead to column border ending to early for example) --> therefore this is a container holding scrollbars + this.$wrapper.appendChild(tableWrapper); - private confirmListener: EventListenerOrEventListenerObject = (ev) => this.confirmTask(); // event data (cohorts) currently ignored + this.$node = document.createElement('div'); // this div will contain the columns of our table that can be higher than half of the viewport + this.$node.classList.add('task-view-table'); + tableWrapper.appendChild(this.$node); + $wrapper.appendChild(tableWrapper); - private columnSortListener: EventListenerOrEventListenerObject = (ev) => this.handleColumnSortEvent(ev as ColumnSortEvent); + $wrapper.insertAdjacentHTML( + 'beforeend', + ` + + `, + ); - constructor(public readonly $node: HTMLDivElement, private reference: ICohort) { - this.$node.classList.add('task-view'); + this.$floatingBtns = $wrapper.querySelector('div.floating-confirm'); + $wrapper.querySelector('div.floating-confirm button.clearBtn').addEventListener('click', () => this.clear()); - const inNode = document.createElement('div'); - this.input = new TaskviewInput(inNode, this.reference, this); - this.$node.appendChild(inNode); + this.$node.addEventListener(COLUMN_CLOSE_EVENT_TYPE, (ev) => this.removeColumn((ev as ColumnCloseEvent).detail.column)); // arrow function to keep "this" working in eventhandler + } - const searchNode = document.createElement('div'); - this.search = new SearchColumn(searchNode, this.reference, this); - this.$node.appendChild(searchNode); + destroy() { + this.$node.addEventListener(COLUMN_CLOSE_EVENT_TYPE, null); + this.$node.remove(); + } - const outNode = document.createElement('div'); - this.output = new TaskviewOutput(outNode, this.reference, this); - this.$node.appendChild(outNode); + update() { + this.setCohorts(this.cohorts); + } - this.scrollLink = new ScrollLinker(inNode.querySelector('div.task-view-scroll-wrapper'), outNode.querySelector('div.task-view-scroll-wrapper')); + public setCohorts(cohorts: ICohort[]) { + this.cohorts = cohorts; + for (const column of this.columns) { + column.setCohorts(cohorts); + } - // Listen to cohort creation events fired by histograms in the input part - this.$node.addEventListener(FILTER_EVENT_TYPE, this.filterListener); - this.$node.addEventListener(SPLIT_EVENT_TYPE, this.splitListener); + this.$floatingBtns.hidden = cohorts.filter((cht) => cht.id !== LOADER_COHORT_ID && cht.id !== EMPTY_COHORT_ID).length === 0; + } - // confirmation event that adds the cohrots to cohort graph - this.$node.addEventListener(CONFIRM_OUTPUT_EVENT_TYPE, this.confirmListener); + public addCohort(cht: ICohort) { + this.cohorts.push(cht); + this.setCohorts(this.cohorts); + } - // sort event that sorts the input cohorts - this.$node.addEventListener(COLUMN_SORT_EVENT_TYPE, this.columnSortListener); + public getAttributeColumns(): AttributeColumn[] { + const attCol = []; + for (const ac of this.columns) { + if (ac instanceof AttributeColumn) { + attCol.push(ac); + } + } + return attCol; } - public async handleColumnSortEvent(ev: ColumnSortEvent) { - const sortDetail = ev.detail; - const inputChts = this.getInputCohorts() as IInputCohort[]; - if (sortDetail.sortInputChts) { - // sort input cohort - const defaultOrder = this.input.cohortOrder; - await this.sortCohorts(sortDetail.type, inputChts, defaultOrder); - } else { - // sort output cohort for each input cohort - for (const inCht of inputChts) { - const currOutChts = inCht.outputCohorts as IOutputCohort[]; - const outputOrder = this.output.cohortOrders.filter((order) => order.inputCht === inCht.dbId); - const defaultOrder = outputOrder.length === 1 ? outputOrder[0].cohorts : currOutChts.map((cht) => cht.dbId); - await this.sortCohorts(sortDetail.type, currOutChts, defaultOrder); - const sizeOutCht = currOutChts.length; - // define the first and last cohort - for (let i = 0; i < sizeOutCht; i++) { - const currCht = currOutChts[i]; - currCht.isFirstOutputCohort = false; - currCht.isLastOutputCohort = false; - // first cohort - if (i === 0) { - currCht.isFirstOutputCohort = true; - } - // last cohort - if (i === sizeOutCht - 1) { - currCht.isLastOutputCohort = true; - } + public getUnpinnedAttributeColumns(): AttributeColumn[] { + const attCol = []; + for (const ac of this.columns) { + if (ac instanceof AttributeColumn) { + if (!ac.pinned) { + attCol.push(ac); } } } - - this.orderCohorts(); + return attCol; } - private async sortCohorts(type: SortType, cohortsToSort: ICohort[], defaultOrder: number[]) { - if (type === SortType.Default) { - // sort by default - const sortingArray = defaultOrder; - cohortsToSort.sort((a, b) => this.sortWithDbIdArray(a.dbId, b.dbId, sortingArray)); // default - } else if (type === SortType.Alpha_AZ) { - // sort by label name - cohortsToSort.sort((a, b) => this.sortLabelAlpha(a, b)); // A -> Z - } else if (type === SortType.Alpha_ZA) { - cohortsToSort.sort((a, b) => this.sortLabelAlpha(b, a)); // Z -> A - } else if (type === SortType.Size_19) { - // sort by cohort size - await this.sortCohortsBySize(false, cohortsToSort); - } else if (type === SortType.Size_91) { - // sort by cohort size - await this.sortCohortsBySize(true, cohortsToSort); + public addAttributeColumn(attr: IAttribute, allowDuplicates = false, onInputCohortSide = true, color = false, pinned = false) { + if ( + allowDuplicates || + !this.columns.find((column) => column instanceof AttributeColumn && (column as AttributeColumn).attribute.dataKey === attr.dataKey) + ) { + // if duplicates are allowed or the column wasn't added yet + const newCol = new AttributeColumn(attr, this.$node, onInputCohortSide, color, pinned); + newCol.setCohorts(this.cohorts); + this.columns.unshift(newCol); // add columnt as first element + // only adding the new column as first element doesn't reorder column HTML elements + // because the new one gets added at the end -> set order attribute to define order of columns + let orderCnt = 0; + this.columns.forEach((elem) => { + if (elem instanceof AttributeColumn) { + elem.setOrder(orderCnt); + orderCnt++; + } + }); + window.dispatchEvent(new Event('resize')); // update vega chart sizes in case the columns became narrower } } - private sortWithDbIdArray(a, b, sortingArr) { - return sortingArr.indexOf(a) - sortingArr.indexOf(b); + public removeColumn(col: AColumn) { + removeFromArray(this.columns, col); + window.dispatchEvent(new Event('resize')); // update vega chart sizes in case the columns became wider } - private sortLabelAlpha(a, b) { - if (a.label < b.label) { - return -1; - } - if (a.label > b.label) { - return 1; - } - return 0; + clear() { + // this.columns.filter((col) => !(col instanceof OutputCohortColumn || col instanceof EmptyColumn)).forEach((col) => col.close()); //close all but default columns + this.setCohorts([]); } +} - private sortSizeNum(a, b) { - if (a.size > b.size) { - return -1; - } - if (a.size < b.size) { - return 1; - } - return 0; - } +class TaskviewInput extends TaskviewTable { + cohortOrder: number[]; - private async sortCohortsBySize(descending: boolean, cohortsToSort: ICohort[]) { - const chtSizes = []; - for (const cht of cohortsToSort) { - const currSize = await cht.size; - chtSizes.push({ id: cht.id, size: currSize }); - } + cohorts: IInputCohort[]; - if (descending) { - chtSizes.sort((a, b) => this.sortSizeNum(a, b)); // 9 -> 1 - } else { - chtSizes.sort((a, b) => this.sortSizeNum(b, a)); // 1 -> 9 - } + usedColorsForCohorts = CoralColorSchema.COLOR_SCHEME.map((elem) => { + return { color: elem, cohorts: [] }; + }); - cohortsToSort.sort(function (a, b) { - const aId = a.id; - const bId = b.id; - const chtSizesIds = chtSizes.map((elem) => elem.id); - if (chtSizesIds.indexOf(aId) > chtSizesIds.indexOf(bId)) { - return 1; - } - if (chtSizesIds.indexOf(aId) < chtSizesIds.indexOf(bId)) { - return -1; - } - return 0; - }); - } + private inputCohortCol: InputCohortColumn; - public orderCohorts() { - // order input side - const inputColumns: ADataColumn[] = this.input.columns as any; - inputColumns.forEach((element) => element.orderCohorts(this.getInputCohorts())); - // order input side - const outputColumns: ADataColumn[] = this.output.columns as any; - const outChts = [].concat(...this.input.cohorts.map((cht) => cht.outputCohorts)); - outputColumns.forEach((element) => element.orderCohorts(outChts)); - } + maxColors: number; - public confirmTask() { - this.search.clear(); - this.removeAttributeColumns(); - this.$node.dispatchEvent(new ConfirmTaskEvent(this.taskParams, this.taskAttributes)); - } + constructor($wrapper: HTMLDivElement, reference: ICohort, private taskview: Taskview) { + super($wrapper); + this.$node.classList.add('input'); - public setInputCohorts(cohorts: ICohort[]): void { - this.input.setCohorts(cohorts as IInputCohort[]); - this.search.updateTasks(); - const outChts = [].concat(...this.input.cohorts.map((cht) => cht.outputCohorts)); - this.setOutputCohorts(outChts); + // this.columns.push(new NumberColumn(this.$node)); + this.inputCohortCol = new InputCohortColumn(this.$node); + this.columns.push(new AddColumnColumn(this.$node, taskview, reference.database, reference.view)); + this.columns.push(this.inputCohortCol); + // this.columns.push(new PrevalenceColumn(reference, this.$node)); + this.columns.push(new EmptyColumn(this.$node)); + this.clearColorCohorts(); } - public updateInput() { - this.input.update(); // adjust height after removing output cohorts + private clearColorCohorts() { + // setup color options + this.usedColorsForCohorts = CoralColorSchema.COLOR_SCHEME.map((elem) => { + return { color: elem, cohorts: [] }; + }); + this.maxColors = this.usedColorsForCohorts.length; } - public getInputCohorts(): IInputCohort[] { - return this.input.cohorts; + public setCohorts(cohorts: IInputCohort[]) { + this.clearColorCohorts(); + cohorts.filter((cht) => !cht.outputCohorts).forEach((cht) => (cht.outputCohorts = [])); // handle undefined outputCohort array + + // set selction order of cohorts + this.cohortOrder = cohorts.map((cht) => cht.dbId); + this.inputCohortCol.setDefaultSort(); + + // get all cohorts that have already a color + cohorts + .filter((cht) => cht.colorTaskView) + .forEach((cht) => { + const colorIndex = this.usedColorsForCohorts.map((a) => a.color).indexOf(cht.colorTaskView); + if (colorIndex !== -1) { + this.usedColorsForCohorts[colorIndex].cohorts.push(cht.id); + } + }); + + // assign color to cohort without one + const maxCohortPerColor = Math.ceil(cohorts.length / this.maxColors); + cohorts.forEach((elem, i) => { + const colorIndex = i % this.maxColors; // circle through all colors + + // check if color exists + if (elem.colorTaskView === null) { + // check if the color for the current cohort index is available + if (this.usedColorsForCohorts[colorIndex].cohorts.length < maxCohortPerColor) { + this.usedColorsForCohorts[colorIndex].cohorts.push(elem.id); // add cohort to the cohort array for the color + elem.colorTaskView = this.usedColorsForCohorts[colorIndex].color; // set color for the cohort + } else { + // go through all possible color and use the first one not used + for (const cc of this.usedColorsForCohorts) { + if (cc.cohorts.length < maxCohortPerColor) { + cc.cohorts.push(elem.id); // add cohort to the cohort array for the color + elem.colorTaskView = cc.color; // set color for the cohort + break; + } + } + } + } + }); + super.setCohorts(cohorts); } - public getOutputCohorts(): ICohort[] { - return this.output ? this.output.cohorts : []; + public addAttributeColumn(attr: IAttribute, allowDuplicates = false, pinned = false) { + super.addAttributeColumn(attr, allowDuplicates, true, true, pinned); } - public setOutputCohorts(cohorts: ICohort[]): void { - this.output.setCohorts(cohorts); + clear() { + this.cohorts + .slice() // duplicate because the cohort array will change through the event + .forEach((cht) => this.$node.dispatchEvent(new CohortSelectionEvent(cht))); // deselect each + super.clear(); } +} - public setReference(reference: ICohort): void { - this.reference = reference; +class TaskviewOutput extends TaskviewTable { + cohortOrders: { inputCht: number; cohorts: number[] }[]; + + private outputCohortCol: OutputCohortColumn; + + constructor($wrapper: HTMLDivElement, reference: ICohort, private taskview: Taskview) { + super($wrapper); + $wrapper.classList.add('output'); + + this.$floatingBtns.insertAdjacentHTML( + `beforeend`, + ` + + `, + ); + $wrapper + .querySelector('div.floating-confirm button.confirmBtn') + .addEventListener('click', (clickEv) => clickEv.target.dispatchEvent(new ConfirmOutputEvent(this.cohorts))); + + this.columns.push(new EmptyColumn(this.$node)); // same flex order as outputcohort column -> ordered by position in DOM + this.columns.push(new AddColumnColumn(this.$node, taskview, reference.database, reference.view, false)); + this.outputCohortCol = new OutputCohortColumn(this.$node); + this.columns.push(this.outputCohortCol); + + this.$node.addEventListener(COHORT_SELECTION_EVENT_TYPE, (ev: CohortSelectionEvent) => { + ev.stopPropagation(); // prevent this ev from going to the global selection handler that sets the taskview inputs + ev.detail.cohort.selected = !ev.detail.cohort.selected; + const updatedTaskParams: ITaskParams[] = []; + // only add the output cohorts that are selected to each of the task parameters + for (const tsk of this.taskview.getTaskParams()) { + const selectedOutputCohorts = tsk.outputCohorts.filter((a) => a.selected === true); + // check if task has output cohorts + if (selectedOutputCohorts.length > 0) { + updatedTaskParams.push({ + inputCohorts: tsk.inputCohorts, + outputCohorts: selectedOutputCohorts, + type: tsk.type, + label: tsk.label, + }); + } + } + // update the preview to only show the selected output cohorts + this.$node.dispatchEvent(new PreviewChangeEvent(updatedTaskParams, this.taskview.getTaskAttributes())); + this.update(); + }); } - public addMultipleAttributeColumns(dArray: IAttribute[], allowDuplicates = false, pinned = false) { - dArray.forEach((d) => this.addAttributeColumn(d, allowDuplicates, pinned)); + clear() { + super.clear(); + + this.taskview.getInputCohorts().forEach((cht) => ((cht as IInputCohort).outputCohorts = [])); + this.taskview.updateInput(); + // remove the preview by setting the taskParams to an empty array + this.$node.dispatchEvent(new PreviewChangeEvent([], null)); } - public addAttributeColumn(d: IAttribute, allowDuplicates = false, pinned = false) { - if (this.reference.idColumn.column !== d.id) { - this.addAttributeColumnForInput(d, allowDuplicates, pinned); - this.addAttributeColumnForOutput(d, allowDuplicates, pinned); + public setCohorts(cohorts: ICohort[]) { + super.setCohorts(cohorts); + + // set order of output cohorts for each input cohort + this.cohortOrders = []; + for (const cht of cohorts) { + const existingParents = this.cohortOrders.map((o) => o.inputCht); + const parentdbID = cht.getCohortParents()[0].dbId; + const index = existingParents.indexOf(parentdbID); + if (index === -1) { + this.cohortOrders.push({ + inputCht: parentdbID, + cohorts: [cht.dbId], + }); + } else { + this.cohortOrders[index].cohorts.push(cht.dbId); + } } + this.outputCohortCol.setDefaultSort(); } +} - public addAttributeColumnForInput(d: IAttribute, allowDuplicates = false, pinned = false) { - this.input.addAttributeColumn(d, allowDuplicates, pinned); // function is overwritten in and does not need the onInputCohortSide = true variable - } +export default class Taskview { + public destroy() { + this.$node.removeEventListener(FILTER_EVENT_TYPE, this.filterListener); + this.$node.removeEventListener(SPLIT_EVENT_TYPE, this.splitListener); + this.$node.removeEventListener(CONFIRM_OUTPUT_EVENT_TYPE, this.confirmListener); - public addAttributeColumnForOutput(d: IAttribute, allowDuplicates = false, pinned = false) { - this.output.addAttributeColumn(d, allowDuplicates, false, false, pinned); - } + this.scrollLink.destroy(); - public removeAttributeColumns() { - this.input.getUnpinnedAttributeColumns().forEach((col) => col.close()); - this.output.getUnpinnedAttributeColumns().forEach((col) => col.close()); - } + this.search.destroy(); + this.input.destroy(); + this.output.destroy(); - public getTaskParams(): ITaskParams[] { - return this.taskParams; + this.$node.classList.remove('task-view'); + while (this.$node.hasChildNodes()) { + this.$node.removeChild(this.$node.lastChild); + } } - public getTaskAttributes(): IAttribute[] { - return this.taskAttributes; + clearOutput() { + this.output.clear(); + this.input.update(); } - private currentEvent = 0; + private input: TaskviewInput; - async handleFilterEvent(ev: FilterEvent | SplitEvent) { - const currentEv = ++this.currentEvent; - let outputCohorts: IOutputCohort[] = []; // Stores the output cohorts in correct order, will replace the array currently used - const taskWithSelectedOutput: ITaskParams[] = []; - this.taskParams = []; - this.taskAttributes = ev.detail.desc[0].filter.map((filter) => filter.attr); + private search: SearchColumn; - for (const inCht of this.input.cohorts) { - const chtBins = ev.detail.desc.filter((bin) => inCht.id === bin.cohort.id); - const outChts: IOutputCohort[] = new Array(Math.max(chtBins.length, 1)).fill(null).map(() => getLoaderCohort(inCht) as unknown as IOutputCohort); + private output: TaskviewOutput; - outChts[outChts.length - 1].isLastOutputCohort = true; - outChts[0].isFirstOutputCohort = true; + private scrollLink: ScrollLinker; - inCht.outputCohorts = outChts; - outputCohorts.push(...outChts); - } + private taskParams: ITaskParams[]; - // Update the input cohorts - this.updateInput(); - // Add the new cohorts to the output side - this.setOutputCohorts(outputCohorts); + private taskAttributes: IAttribute[]; - // await DebugTools.sleep(1500); - outputCohorts = []; + private filterListener: EventListenerOrEventListenerObject = (ev) => this.handleFilterEvent(ev as FilterEvent); - // For each input cohort, create a new output cohort - for (const cht of this.input.cohorts) { - log.debug('filter/split event: ', ev); - cht.outputCohorts = []; - const chtBins = ev.detail.desc.filter((bin) => cht.id === bin.cohort.id); - if (chtBins.length > 0) { - const chtPromises = []; - for (const bin of chtBins) { - chtPromises.push(multiAttributeFilter(cht, bin.filter)); - } + private splitListener: EventListenerOrEventListenerObject = (ev) => this.handleFilterEvent(ev as SplitEvent); - const newChts = await Promise.all(chtPromises); - if (currentEv !== this.currentEvent) { - return; - } + private confirmListener: EventListenerOrEventListenerObject = (ev) => this.confirmTask(); // event data (cohorts) currently ignored - const chtSizes = []; - newChts.sort((a, b) => this.sortLabelAlpha(a, b)); // sort output cohorts: A -> Z - for (const newOutCht of newChts) { - // Set representation of new cohort - const layout = new RectangleLayout(); - newOutCht.representation = new RectCohortRep(newOutCht, layout.rowHeight, layout.cohortWidth); - const sizePromises = [newOutCht.size, this.reference.size]; - chtSizes.push(...sizePromises); - Promise.all(sizePromises).then(([newSize, refSize]) => { - newOutCht.representation.setSize(newSize, refSize); - if (newSize > 0) { - newOutCht.selected = true; - } - }); + private columnSortListener: EventListenerOrEventListenerObject = (ev) => this.handleColumnSortEvent(ev as ColumnSortEvent); - newOutCht.setCohortParents([cht]); - cht.outputCohorts.push(newOutCht); // Add new output cohort to existing cohorts - } + constructor(public readonly $node: HTMLDivElement, private reference: ICohort) { + this.$node.classList.add('task-view'); - await Promise.all(chtSizes); // wait for the cohort sizes to properly display the representation - } else { - cht.outputCohorts.push(getEmptyCohort(cht) as IOutputCohort); - } + const inNode = document.createElement('div'); + this.input = new TaskviewInput(inNode, this.reference, this); + this.$node.appendChild(inNode); - // the async/time instensive stuff is done now, check if we should continue: - if (currentEv !== this.currentEvent) { - return; - } + const searchNode = document.createElement('div'); + this.search = new SearchColumn(searchNode, this.reference, this); + this.$node.appendChild(searchNode); - cht.outputCohorts[cht.outputCohorts.length - 1].isLastOutputCohort = true; - cht.outputCohorts[0].isFirstOutputCohort = true; - outputCohorts.push(...cht.outputCohorts); // add all output cohorts of current input cohorts (ensures correct order in output side) + const outNode = document.createElement('div'); + this.output = new TaskviewOutput(outNode, this.reference, this); + this.$node.appendChild(outNode); - if (chtBins.length > 0) { - // create current task params - const currTaskParam: ITaskParams = { - inputCohorts: [cht], - outputCohorts: cht.outputCohorts, - type: ev instanceof FilterEvent ? TaskType.Filter : TaskType.Split, - label: ev.detail.desc[0].filter.map((filter) => filter.attr.label).join(', '), - }; - // save all curertn possibel task params - this.taskParams.push(currTaskParam); - // check if a task generates selected output cohorts - if (cht.outputCohorts.filter((elem) => elem.selected === true).length >= 1) { - // initially onyl selected output cohorts will be shown in overview - taskWithSelectedOutput.push(currTaskParam); + this.scrollLink = new ScrollLinker(inNode.querySelector('div.task-view-scroll-wrapper'), outNode.querySelector('div.task-view-scroll-wrapper')); + + // Listen to cohort creation events fired by histograms in the input part + this.$node.addEventListener(FILTER_EVENT_TYPE, this.filterListener); + this.$node.addEventListener(SPLIT_EVENT_TYPE, this.splitListener); + + // confirmation event that adds the cohrots to cohort graph + this.$node.addEventListener(CONFIRM_OUTPUT_EVENT_TYPE, this.confirmListener); + + // sort event that sorts the input cohorts + this.$node.addEventListener(COLUMN_SORT_EVENT_TYPE, this.columnSortListener); + } + + public async handleColumnSortEvent(ev: ColumnSortEvent) { + const sortDetail = ev.detail; + const inputChts = this.getInputCohorts() as IInputCohort[]; + if (sortDetail.sortInputChts) { + // sort input cohort + const defaultOrder = this.input.cohortOrder; + await this.sortCohorts(sortDetail.type, inputChts, defaultOrder); + } else { + // sort output cohort for each input cohort + for (const inCht of inputChts) { + const currOutChts = inCht.outputCohorts as IOutputCohort[]; + const outputOrder = this.output.cohortOrders.filter((order) => order.inputCht === inCht.dbId); + const defaultOrder = outputOrder.length === 1 ? outputOrder[0].cohorts : currOutChts.map((cht) => cht.dbId); + // TODO: fix me + // eslint-disable-next-line no-await-in-loop + await this.sortCohorts(sortDetail.type, currOutChts, defaultOrder); + const sizeOutCht = currOutChts.length; + // define the first and last cohort + for (let i = 0; i < sizeOutCht; i++) { + const currCht = currOutChts[i]; + currCht.isFirstOutputCohort = false; + currCht.isLastOutputCohort = false; + // first cohort + if (i === 0) { + currCht.isFirstOutputCohort = true; + } + // last cohort + if (i === sizeOutCht - 1) { + currCht.isLastOutputCohort = true; + } } } } - // Update the input cohorts - this.updateInput(); - - // show the preview for the current task - this.$node.dispatchEvent(new PreviewChangeEvent(taskWithSelectedOutput, this.taskAttributes)); + this.orderCohorts(); + } - // Add the new cohorts to the output side - this.setOutputCohorts(outputCohorts); - ev.detail.desc[0].filter.forEach((filter) => this.addAttributeColumn(filter.attr)); + private async sortCohorts(type: SortType, cohortsToSort: ICohort[], defaultOrder: number[]) { + if (type === SortType.Default) { + // sort by default + const sortingArray = defaultOrder; + cohortsToSort.sort((a, b) => this.sortWithDbIdArray(a.dbId, b.dbId, sortingArray)); // default + } else if (type === SortType.Alpha_AZ) { + // sort by label name + cohortsToSort.sort((a, b) => this.sortLabelAlpha(a, b)); // A -> Z + } else if (type === SortType.Alpha_ZA) { + cohortsToSort.sort((a, b) => this.sortLabelAlpha(b, a)); // Z -> A + } else if (type === SortType.Size_19) { + // sort by cohort size + await this.sortCohortsBySize(false, cohortsToSort); + } else if (type === SortType.Size_91) { + // sort by cohort size + await this.sortCohortsBySize(true, cohortsToSort); + } } - showOutput(show: boolean) { - const hide = !show; - // this.output.$node.style.display = hide ? 'none' : 'flex'; // setting hidden does not work with flexbox + private sortWithDbIdArray(a, b, sortingArr) { + return sortingArr.indexOf(a) - sortingArr.indexOf(b); + } - this.$node.classList.toggle('no-output', hide); + private sortLabelAlpha(a, b) { + if (a.label < b.label) { + return -1; + } + if (a.label > b.label) { + return 1; + } + return 0; } -} -abstract class TaskviewTable { - $node: HTMLDivElement; + private sortSizeNum(a, b) { + if (a.size > b.size) { + return -1; + } + if (a.size < b.size) { + return 1; + } + return 0; + } - $floatingBtns: HTMLDivElement; + private async sortCohortsBySize(descending: boolean, cohortsToSort: ICohort[]) { + const chtSizes = await Promise.all( + cohortsToSort.map(async (cht) => { + const currSize = await cht.size; + return { id: cht.id, size: currSize }; + }), + ); + if (descending) { + chtSizes.sort((a, b) => this.sortSizeNum(a, b)); // 9 -> 1 + } else { + chtSizes.sort((a, b) => this.sortSizeNum(b, a)); // 1 -> 9 + } - columns: AColumn[] = []; + cohortsToSort.sort(function (a, b) { + const aId = a.id; + const bId = b.id; + const chtSizesIds = chtSizes.map((elem) => elem.id); + if (chtSizesIds.indexOf(aId) > chtSizesIds.indexOf(bId)) { + return 1; + } + if (chtSizesIds.indexOf(aId) < chtSizesIds.indexOf(bId)) { + return -1; + } + return 0; + }); + } - cohorts: ICohort[] = []; + public orderCohorts() { + // order input side + const inputColumns: ADataColumn[] = this.input.columns as any; + inputColumns.forEach((element) => element.orderCohorts(this.getInputCohorts())); + // order input side + const outputColumns: ADataColumn[] = this.output.columns as any; + const outChts = [].concat(...this.input.cohorts.map((cht) => cht.outputCohorts)); + outputColumns.forEach((element) => element.orderCohorts(outChts)); + } - constructor(private $wrapper: HTMLDivElement) { - this.$wrapper.classList.add('task-view-table-button-wrapper'); + public confirmTask() { + this.search.clear(); + this.removeAttributeColumns(); + this.$node.dispatchEvent(new ConfirmTaskEvent(this.taskParams, this.taskAttributes)); + } - const tableWrapper = document.createElement('div'); - tableWrapper.classList.add('task-view-scroll-wrapper'); // this div can only be around half the height of the browser (which would lead to column border ending to early for example) --> therefore this is a container holding scrollbars - this.$wrapper.appendChild(tableWrapper); + public setInputCohorts(cohorts: ICohort[]): void { + this.input.setCohorts(cohorts as IInputCohort[]); + this.search.updateTasks(); + const outChts = [].concat(...this.input.cohorts.map((cht) => cht.outputCohorts)); + this.setOutputCohorts(outChts); + } - this.$node = document.createElement('div'); // this div will contain the columns of our table that can be higher than half of the viewport - this.$node.classList.add('task-view-table'); - tableWrapper.appendChild(this.$node); - $wrapper.appendChild(tableWrapper); + public updateInput() { + this.input.update(); // adjust height after removing output cohorts + } - $wrapper.insertAdjacentHTML( - 'beforeend', - ` - - `, - ); + public getInputCohorts(): IInputCohort[] { + return this.input.cohorts; + } - this.$floatingBtns = $wrapper.querySelector('div.floating-confirm'); - $wrapper.querySelector('div.floating-confirm button.clearBtn').addEventListener('click', () => this.clear()); + public getOutputCohorts(): ICohort[] { + return this.output ? this.output.cohorts : []; + } - this.$node.addEventListener(COLUMN_CLOSE_EVENT_TYPE, (ev) => this.removeColumn((ev as ColumnCloseEvent).detail.column)); // arrow function to keep "this" working in eventhandler + public setOutputCohorts(cohorts: ICohort[]): void { + this.output.setCohorts(cohorts); } - destroy() { - this.$node.addEventListener(COLUMN_CLOSE_EVENT_TYPE, null); - this.$node.remove(); + public setReference(reference: ICohort): void { + this.reference = reference; } - update() { - this.setCohorts(this.cohorts); + public addMultipleAttributeColumns(dArray: IAttribute[], allowDuplicates = false, pinned = false) { + dArray.forEach((d) => this.addAttributeColumn(d, allowDuplicates, pinned)); } - public setCohorts(cohorts: ICohort[]) { - this.cohorts = cohorts; - for (const column of this.columns) { - column.setCohorts(cohorts); + public addAttributeColumn(d: IAttribute, allowDuplicates = false, pinned = false) { + if (this.reference.idColumn.column !== d.id) { + this.addAttributeColumnForInput(d, allowDuplicates, pinned); + this.addAttributeColumnForOutput(d, allowDuplicates, pinned); } - - this.$floatingBtns.hidden = cohorts.filter((cht) => cht.id !== LOADER_COHORT_ID && cht.id !== EMPTY_COHORT_ID).length === 0; } - public addCohort(cht: ICohort) { - this.cohorts.push(cht); - this.setCohorts(this.cohorts); + public addAttributeColumnForInput(d: IAttribute, allowDuplicates = false, pinned = false) { + this.input.addAttributeColumn(d, allowDuplicates, pinned); // function is overwritten in and does not need the onInputCohortSide = true variable } - public getAttributeColumns(): AttributeColumn[] { - const attCol = []; - for (const ac of this.columns) { - if (ac instanceof AttributeColumn) { - attCol.push(ac); - } - } - return attCol; + public addAttributeColumnForOutput(d: IAttribute, allowDuplicates = false, pinned = false) { + this.output.addAttributeColumn(d, allowDuplicates, false, false, pinned); } - public getUnpinnedAttributeColumns(): AttributeColumn[] { - const attCol = []; - for (const ac of this.columns) { - if (ac instanceof AttributeColumn) { - if (!ac.pinned) { - attCol.push(ac); - } - } - } - return attCol; + public removeAttributeColumns() { + this.input.getUnpinnedAttributeColumns().forEach((col) => col.close()); + this.output.getUnpinnedAttributeColumns().forEach((col) => col.close()); } - public addAttributeColumn(attr: IAttribute, allowDuplicates = false, onInputCohortSide = true, color = false, pinned = false) { - if ( - allowDuplicates || - !this.columns.find((column) => column instanceof AttributeColumn && (column as AttributeColumn).attribute.dataKey === attr.dataKey) - ) { - // if duplicates are allowed or the column wasn't added yet - const newCol = new AttributeColumn(attr, this.$node, onInputCohortSide, color, pinned); - newCol.setCohorts(this.cohorts); - this.columns.unshift(newCol); // add columnt as first element - // only adding the new column as first element doesn't reorder column HTML elements - // because the new one gets added at the end -> set order attribute to define order of columns - let orderCnt = 0; - this.columns.map((elem) => { - if (elem instanceof AttributeColumn) { - elem.setOrder(orderCnt); - orderCnt++; - } - }); - window.dispatchEvent(new Event('resize')); // update vega chart sizes in case the columns became narrower - } - } - - public removeColumn(col: AColumn) { - removeFromArray(this.columns, col); - window.dispatchEvent(new Event('resize')); // update vega chart sizes in case the columns became wider + public getTaskParams(): ITaskParams[] { + return this.taskParams; } - clear() { - // this.columns.filter((col) => !(col instanceof OutputCohortColumn || col instanceof EmptyColumn)).forEach((col) => col.close()); //close all but default columns - this.setCohorts([]); + public getTaskAttributes(): IAttribute[] { + return this.taskAttributes; } -} - -class TaskviewInput extends TaskviewTable { - cohortOrder: number[]; - - cohorts: IInputCohort[]; - usedColorsForCohorts = CoralColorSchema.COLOR_SCHEME.map((elem) => { - return { color: elem, cohorts: [] }; - }); - - private inputCohortCol: InputCohortColumn; + private currentEvent = 0; - maxColors: number; + async handleFilterEvent(ev: FilterEvent | SplitEvent) { + const currentEv = ++this.currentEvent; + let outputCohorts: IOutputCohort[] = []; // Stores the output cohorts in correct order, will replace the array currently used + const taskWithSelectedOutput: ITaskParams[] = []; + this.taskParams = []; + this.taskAttributes = ev.detail.desc[0].filter.map((filter) => filter.attr); - constructor($wrapper: HTMLDivElement, reference: ICohort, private taskview: Taskview) { - super($wrapper); - this.$node.classList.add('input'); + for (const inCht of this.input.cohorts) { + const chtBins = ev.detail.desc.filter((bin) => inCht.id === bin.cohort.id); + const outChts: IOutputCohort[] = new Array(Math.max(chtBins.length, 1)).fill(null).map(() => getLoaderCohort(inCht) as unknown as IOutputCohort); - // this.columns.push(new NumberColumn(this.$node)); - this.inputCohortCol = new InputCohortColumn(this.$node); - this.columns.push(new AddColumnColumn(this.$node, taskview, reference.database, reference.view)); - this.columns.push(this.inputCohortCol); - // this.columns.push(new PrevalenceColumn(reference, this.$node)); - this.columns.push(new EmptyColumn(this.$node)); - this.clearColorCohorts(); - } + outChts[outChts.length - 1].isLastOutputCohort = true; + outChts[0].isFirstOutputCohort = true; - private clearColorCohorts() { - // setup color options - this.usedColorsForCohorts = CoralColorSchema.COLOR_SCHEME.map((elem) => { - return { color: elem, cohorts: [] }; - }); - this.maxColors = this.usedColorsForCohorts.length; - } + inCht.outputCohorts = outChts; + outputCohorts.push(...outChts); + } - public setCohorts(cohorts: IInputCohort[]) { - this.clearColorCohorts(); - cohorts.filter((cht) => !cht.outputCohorts).forEach((cht) => (cht.outputCohorts = [])); // handle undefined outputCohort array + // Update the input cohorts + this.updateInput(); + // Add the new cohorts to the output side + this.setOutputCohorts(outputCohorts); - // set selction order of cohorts - this.cohortOrder = cohorts.map((cht) => cht.dbId); - this.inputCohortCol.setDefaultSort(); + // await DebugTools.sleep(1500); + outputCohorts = []; - // get all cohorts that have already a color - cohorts - .filter((cht) => cht.colorTaskView) - .forEach((cht) => { - const colorIndex = this.usedColorsForCohorts.map((a) => a.color).indexOf(cht.colorTaskView); - if (colorIndex !== -1) { - this.usedColorsForCohorts[colorIndex].cohorts.push(cht.id); + // For each input cohort, create a new output cohort + for (const cht of this.input.cohorts) { + log.debug('filter/split event: ', ev); + cht.outputCohorts = []; + const chtBins = ev.detail.desc.filter((bin) => cht.id === bin.cohort.id); + if (chtBins.length > 0) { + const chtPromises = []; + for (const bin of chtBins) { + chtPromises.push(multiAttributeFilter(cht, bin.filter)); + } + // TODO: fix me + // eslint-disable-next-line no-await-in-loop + const newChts = await Promise.all(chtPromises); + if (currentEv !== this.currentEvent) { + return; } - }); - - // assign color to cohort without one - const maxCohortPerColor = Math.ceil(cohorts.length / this.maxColors); - cohorts.forEach((elem, i) => { - const colorIndex = i % this.maxColors; // circle through all colors - // check if color exists - if (elem.colorTaskView === null) { - // check if the color for the current cohort index is available - if (this.usedColorsForCohorts[colorIndex].cohorts.length < maxCohortPerColor) { - this.usedColorsForCohorts[colorIndex].cohorts.push(elem.id); // add cohort to the cohort array for the color - elem.colorTaskView = this.usedColorsForCohorts[colorIndex].color; // set color for the cohort - } else { - // go through all possible color and use the first one not used - for (const cc of this.usedColorsForCohorts) { - if (cc.cohorts.length < maxCohortPerColor) { - cc.cohorts.push(elem.id); // add cohort to the cohort array for the color - elem.colorTaskView = cc.color; // set color for the cohort - break; + const chtSizes = []; + newChts.sort((a, b) => this.sortLabelAlpha(a, b)); // sort output cohorts: A -> Z + for (const newOutCht of newChts) { + // Set representation of new cohort + const layout = new RectangleLayout(); + newOutCht.representation = new RectCohortRep(newOutCht, layout.rowHeight, layout.cohortWidth); + const sizePromises = [newOutCht.size, this.reference.size]; + chtSizes.push(...sizePromises); + Promise.all(sizePromises).then(([newSize, refSize]) => { + newOutCht.representation.setSize(newSize, refSize); + if (newSize > 0) { + newOutCht.selected = true; } - } + }); + + newOutCht.setCohortParents([cht]); + cht.outputCohorts.push(newOutCht); // Add new output cohort to existing cohorts } + // TODO: fix me + // eslint-disable-next-line no-await-in-loop + await Promise.all(chtSizes); // wait for the cohort sizes to properly display the representation + } else { + cht.outputCohorts.push(getEmptyCohort(cht) as IOutputCohort); } - }); - super.setCohorts(cohorts); - } - - public addAttributeColumn(attr: IAttribute, allowDuplicates = false, pinned = false) { - super.addAttributeColumn(attr, allowDuplicates, true, true, pinned); - } - - clear() { - this.cohorts - .slice() // duplicate because the cohort array will change through the event - .forEach((cht) => this.$node.dispatchEvent(new CohortSelectionEvent(cht))); // deselect each - super.clear(); - } -} - -class TaskviewOutput extends TaskviewTable { - cohortOrders: { inputCht: number; cohorts: number[] }[]; - - private outputCohortCol: OutputCohortColumn; - - constructor($wrapper: HTMLDivElement, reference: ICohort, private taskview: Taskview) { - super($wrapper); - $wrapper.classList.add('output'); - this.$floatingBtns.insertAdjacentHTML( - `beforeend`, - ` - - `, - ); - $wrapper - .querySelector('div.floating-confirm button.confirmBtn') - .addEventListener('click', (clickEv) => clickEv.target.dispatchEvent(new ConfirmOutputEvent(this.cohorts))); + // the async/time instensive stuff is done now, check if we should continue: + if (currentEv !== this.currentEvent) { + return; + } - this.columns.push(new EmptyColumn(this.$node)); // same flex order as outputcohort column -> ordered by position in DOM - this.columns.push(new AddColumnColumn(this.$node, taskview, reference.database, reference.view, false)); - this.outputCohortCol = new OutputCohortColumn(this.$node); - this.columns.push(this.outputCohortCol); + cht.outputCohorts[cht.outputCohorts.length - 1].isLastOutputCohort = true; + cht.outputCohorts[0].isFirstOutputCohort = true; + outputCohorts.push(...cht.outputCohorts); // add all output cohorts of current input cohorts (ensures correct order in output side) - this.$node.addEventListener(COHORT_SELECTION_EVENT_TYPE, (ev: CohortSelectionEvent) => { - ev.stopPropagation(); // prevent this ev from going to the global selection handler that sets the taskview inputs - ev.detail.cohort.selected = !ev.detail.cohort.selected; - const updatedTaskParams: ITaskParams[] = []; - // only add the output cohorts that are selected to each of the task parameters - for (const tsk of this.taskview.getTaskParams()) { - const selectedOutputCohorts = tsk.outputCohorts.filter((a) => a.selected === true); - // check if task has output cohorts - if (selectedOutputCohorts.length > 0) { - updatedTaskParams.push({ - inputCohorts: tsk.inputCohorts, - outputCohorts: selectedOutputCohorts, - type: tsk.type, - label: tsk.label, - }); + if (chtBins.length > 0) { + // create current task params + const currTaskParam: ITaskParams = { + inputCohorts: [cht], + outputCohorts: cht.outputCohorts, + type: ev instanceof FilterEvent ? TaskType.Filter : TaskType.Split, + label: ev.detail.desc[0].filter.map((filter) => filter.attr.label).join(', '), + }; + // save all curertn possibel task params + this.taskParams.push(currTaskParam); + // check if a task generates selected output cohorts + if (cht.outputCohorts.filter((elem) => elem.selected === true).length >= 1) { + // initially onyl selected output cohorts will be shown in overview + taskWithSelectedOutput.push(currTaskParam); } } - // update the preview to only show the selected output cohorts - this.$node.dispatchEvent(new PreviewChangeEvent(updatedTaskParams, this.taskview.getTaskAttributes())); - this.update(); - }); - } + } - clear() { - super.clear(); + // Update the input cohorts + this.updateInput(); - this.taskview.getInputCohorts().forEach((cht) => ((cht as IInputCohort).outputCohorts = [])); - this.taskview.updateInput(); - // remove the preview by setting the taskParams to an empty array - this.$node.dispatchEvent(new PreviewChangeEvent([], null)); + // show the preview for the current task + this.$node.dispatchEvent(new PreviewChangeEvent(taskWithSelectedOutput, this.taskAttributes)); + + // Add the new cohorts to the output side + this.setOutputCohorts(outputCohorts); + ev.detail.desc[0].filter.forEach((filter) => this.addAttributeColumn(filter.attr)); } - public setCohorts(cohorts: ICohort[]) { - super.setCohorts(cohorts); + showOutput(show: boolean) { + const hide = !show; + // this.output.$node.style.display = hide ? 'none' : 'flex'; // setting hidden does not work with flexbox - // set order of output cohorts for each input cohort - this.cohortOrders = []; - for (const cht of cohorts) { - const existingParents = this.cohortOrders.map((o) => o.inputCht); - const parentdbID = cht.getCohortParents()[0].dbId; - const index = existingParents.indexOf(parentdbID); - if (index === -1) { - this.cohortOrders.push({ - inputCht: parentdbID, - cohorts: [cht.dbId], - }); - } else { - this.cohortOrders[index].cohorts.push(cht.dbId); - } - } - this.outputCohortCol.setDefaultSort(); + this.$node.classList.toggle('no-output', hide); } } diff --git a/src/Taskview/columns/AColumn.ts b/src/Taskview/columns/AColumn.ts index 83d96ed..88f410f 100644 --- a/src/Taskview/columns/AColumn.ts +++ b/src/Taskview/columns/AColumn.ts @@ -4,11 +4,11 @@ import { EMPTY_COHORT_ID, LOADER_COHORT_ID } from '../../Cohort'; import { colors } from '../../config/colors'; import { getAnimatedLoadingBars, log } from '../../util'; import { ColumnCloseEvent } from '../../base/events'; -import Taskview from '../Taskview'; -import { taskview } from '../../cohortview'; -import { toAttribute } from '../../data/Attribute'; -import { SearchBar } from '../SearchBar'; +import type Taskview from '../Taskview'; +import { CohortContext } from '../../CohortContext'; import { ICohort, IInputCohort, IOutputCohort } from '../../app/interfaces'; +import { SearchBar } from '../SearchBar'; +import { toAttribute } from '../../Tasks'; function createSearchBarTooltip(elemWithTooltip: HTMLDivElement, cssClassName: string, database: string, view: string, positionStart = true) { // start of tooltip content @@ -32,7 +32,7 @@ function createSearchBarTooltip(elemWithTooltip: HTMLDivElement, cssClassName: s // convert options to attributes const attributes = options.map((opt) => toAttribute(opt, database, view)); // add attributes to taskview - taskview.addMultipleAttributeColumns(attributes, true, true); + CohortContext.taskview.addMultipleAttributeColumns(attributes, true, true); // remove options and close tooltip elemWithTooltip.click(); }); @@ -124,6 +124,8 @@ export abstract class ADataColumn extends AColumn { this.dataCells = select(this.$column) .selectAll('div.data') .data(cohorts, (d) => d.id); + + // eslint-disable-next-line @typescript-eslint/no-this-alias, @typescript-eslint/naming-convention const _thisColumn = this; // Update diff --git a/src/Taskview/columns/AttributeColumn.ts b/src/Taskview/columns/AttributeColumn.ts index fb41144..86ccd37 100644 --- a/src/Taskview/columns/AttributeColumn.ts +++ b/src/Taskview/columns/AttributeColumn.ts @@ -1,13 +1,9 @@ import { select } from 'd3v7'; -import vegaEmbed from 'vega-embed'; -import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; -import { colors } from '../../config/colors'; -import { IAttribute } from '../../data/Attribute'; -import { IEqualsList, INumRange, NumRangeOperators } from '../../base/rest'; -import { getAnimatedLoadingBars, log } from '../../util'; -import { easyLabelFromFilter, niceName } from '../../utils/labels'; +import { niceName } from '../../utils/labels'; import { ADataColumn } from './AColumn'; import { ICohort } from '../../app/interfaces'; +import type { IAttribute } from '../../data/IAttribute'; +import { Histogram } from './Histogram'; export default class AttributeColumn extends ADataColumn { private hists: WeakMap = new WeakMap(); @@ -159,330 +155,3 @@ export default class AttributeColumn extends ADataColumn { cell.appendChild(hist.getNode()); } } - -class Histogram { - readonly $node: HTMLDivElement; - - readonly $loader: HTMLDivElement; - - readonly $hist: HTMLDivElement; - - vegaView: any; - - public showReference = true; - - constructor(private attribute: IAttribute, private cohort: ICohort, showReference, private index, private color) { - this.showReference = showReference; - this.$node = document.createElement('div'); - this.$node.classList.add('hist'); - - this.$loader = document.createElement('div'); - this.$loader.classList.add('loader'); // center content with flexbox - this.$loader.appendChild(getAnimatedLoadingBars()); - - this.$hist = document.createElement('div'); - - this.$node.appendChild(this.$hist); - - const that = this; - setTimeout(() => that.updateNode.bind(that)(), 0); // run async - } - - public async updateNode() { - this.$hist.remove(); - try { - if (!this.cohort.hasfilterConflict()) { - this.$node.appendChild(this.$loader); - // get hist data of attribute for cohort - let data: { bin: string; count: number }[]; - try { - data = await this.attribute.getHist(this.cohort.dbId, this.cohort.filters); - } catch (e) { - log.error('get hist failed', e); - this.$loader.remove(); - this.$node.classList.add('text'); - this.$hist.innerHTML = ` -

- - Request timeout. -

- `; - this.$node.appendChild(this.$hist); - return; - } - - const notZeroData = data.filter((d) => d.count > 0); // filter only for categories/bins with count bigger 0 - let showText = false; - // show only text when 1 bin and not numerical attribute or when numerical attribute only for one bin if it is for null - if ( - this.attribute.type !== 'number' || - (this.attribute.type === 'number' && notZeroData.length === 1 && (notZeroData[0].bin === 'null' || notZeroData[0].bin === null)) - ) { - showText = notZeroData.length === 1; // show text when only one category/bin with count bigger 0 - } - - const chtSize = await this.cohort.size; - // -> size = 0: show only line in data cell with vega vis (all bins/categories have count 0) - - // check if reference should be shown and - // if the size of the current cohort is bigger then 0 - if (this.showReference && chtSize > 0) { - if (this.cohort.getCohortParents().length > 0) { - // get parent cohort - const parentCht = this.cohort.getCohortParents()[0]; - // get hist data of attribtue for parent cohort - const parentData: { bin: string; count: number }[] = await this.attribute.getHist(parentCht.dbId, parentCht.filters); - const notZeroParentData = parentData.filter((d) => d.count > 0); // filter only for categories/bins with count bigger 0 - const showParentText = notZeroParentData.length === 1; // show text when only one category/bin with count bigger 0 - const vegaData = { - data, - parentData, - parentColor: parentCht.colorTaskView, - }; - if (showParentText) { - const { bin } = notZeroParentData[0]; - // TODO labels - // const text = this.formatText(bin); - const text = this.formatText(bin, this.cohort.values); - this.$node.classList.add('text'); - this.$hist.innerHTML = text; - } else { - // show histogram with reference - this.$node.classList.remove('text'); - vegaEmbed(this.$hist, this.getMinimalVegaSpecWithRef(vegaData), { actions: false, renderer: 'svg' }).then((result) => { - this.vegaView = result.view; - }); - } - } - } else { - // when no reference should be shown (is also the case for the input cohort side) - // check if only one category/bin has values - if (showText) { - // show only text - const { bin } = notZeroData[0]; - // TODO labels - // const text = this.formatText(bin); - this.$node.classList.add('text'); - const text = this.formatText(bin, this.cohort.values); - this.$hist.innerHTML = text; - } else { - // show histogram - this.$node.classList.remove('text'); - vegaEmbed(this.$hist, this.getMinimalVegaSpec(data), { actions: false, renderer: 'svg' }).then((result) => { - this.vegaView = result.view; - }); - } - } - - this.$loader.remove(); - this.$node.appendChild(this.$hist); - } else { - throw new Error('Can not show histogram due to filter contradiction'); - } - } catch (e) { - log.error('Cant show histogram', e); - - this.$loader.remove(); - this.$node.classList.add('text'); - this.$hist.innerHTML = ` -

- - Can not display histogram. -

- `; - this.$node.appendChild(this.$hist); - } - } - - // TODO labels - // private formatText(value: string): string { - private formatText(value: string, filters: Array): string { - if (value === 'null' || value === null || value === undefined) { - value = `Missing Values`; - } - let text = String(value); // turn boolean categories into strings - - if (text.indexOf('(') >= 0 || text.indexOf('[') >= 0) { - const values = text.split(', '); - const params = { - firstOp: values[0][0], - firstValue: Number(values[0].substring(1)), - secondOp: values[1][values[1].length - 1], - // TODO labels - // secondValue: Number(values[1].substring(0, values[1].length - 2)) - secondValue: Number(values[1].substring(0, values[1].length - 1)), - }; - // TODO lables - // text = labelFromRanges(params.firstOp === '(' ? '(' : '[', params.firstValue, params.secondValue, params.secondOp === ')' ? ')' : ']', this.attribute); - const range: INumRange = { - operatorOne: params.firstOp === '(' ? NumRangeOperators.gt : NumRangeOperators.gte, - valueOne: params.firstValue, - operatorTwo: params.secondOp === ')' ? NumRangeOperators.lt : NumRangeOperators.lte, - valueTwo: params.secondValue, - }; - - text = easyLabelFromFilter(range, this.attribute.label); - } else { - text = niceName(text); - } - return text; - } - - public getNode() { - return this.$node; - } - - public getMinimalVegaSpec(data): VegaLiteSpec { - let sort: any = 'ascending'; - if (this.attribute.type === 'number') { - sort = { field: 'index' }; - } - return { - $schema: 'https://vega.github.io/schema/vega-lite/v5.json', - width: 'container', // responsive width - height: 50, // responsive height - background: '#ffffff00', // set visualization background to white and opacity 0 -> https://vega.github.io/vega-lite/docs/spec.html#example-background - autosize: { type: 'fit', contains: 'padding' }, // plots fit a bit better, if though they are specified as responsive - data: { values: data }, - mark: { - type: 'bar', - tooltip: true, - }, - encoding: { - x: { - field: 'bin', - type: 'nominal', - axis: { - title: null, // we will have the title in the column header - labels: false, // no space for labels - ticks: false, - }, - sort, - }, - y: { - field: 'count', - type: 'quantitative', - axis: null, // no axis, no title, labels, no grid - }, - color: { - field: 'this_does_not_exist', // field is necessary but does not have to exist - type: 'nominal', // type is also necessary, nominal as we have only one category (or none :) ) - scale: { - range: [this.cohort.colorTaskView || colors.barColor], // [this.color ? Cat16.get(this.index) : colors.barColor] - }, - legend: null, // no legend - }, - tooltip: [ - { field: 'bin', type: 'nominal' }, - { field: 'count', type: 'quantitative' }, - ], - }, - config: { - view: { - stroke: 'transparent', // https://vega.github.io/vega-lite/docs/spec.html#view-background - }, - }, - params: [ - { - name: 'highlight', - select: { - type: 'point', - on: 'mouseover', - clear: 'mouseout', - }, - }, - ], - }; - } - - public getMinimalVegaSpecWithRef(data): VegaLiteSpec { - let sort: any = 'ascending'; - if (this.attribute.type === 'number') { - sort = { field: 'index' }; - } - return { - $schema: 'https://vega.github.io/schema/vega-lite/v5.json', - width: 'container', // responsive width - height: 50, // responsive height - background: '#ffffff00', // set visualization background to white and opacity 0 -> https://vega.github.io/vega-lite/docs/spec.html#example-background - autosize: { type: 'fit', contains: 'padding' }, // plots fit a bit better, if though they are specified as responsive - datasets: { - parentData: data.parentData, - data: data.data, - }, - layer: [ - { - // layer 1: input cohort reference data - data: { name: 'parentData' }, - mark: { - type: 'bar', - tooltip: true, - }, - encoding: { - x: { - field: 'bin', - type: 'nominal', - axis: { - title: null, // we will have the title in the column header - labels: false, // no space for labels - ticks: false, - }, - sort, - }, - y: { - field: 'count', - type: 'quantitative', - axis: null, // no axis, no title, labels, no grid - }, - fill: { - value: data.parentColor, - legend: null, // no legend - }, - tooltip: [ - { field: 'bin', type: 'nominal' }, - { field: 'count', type: 'quantitative' }, - ], - }, - }, - { - // layer 2: output cohort data - data: { name: 'data' }, - mark: { - type: 'bar', - tooltip: true, - }, - encoding: { - x: { - field: 'bin', - type: 'nominal', - axis: { - title: null, // we will have the title in the column header - labels: false, // no space for labels - ticks: false, - }, - sort, - }, - y: { - field: 'count', - type: 'quantitative', - axis: null, // no axis, no title, labels, no grid - }, - fill: { - value: colors.barColor, - legend: null, // no legend - }, - tooltip: [ - { field: 'bin', type: 'nominal' }, - { field: 'count', type: 'quantitative' }, - ], - }, - }, - ], - config: { - view: { - stroke: 'transparent', // https://vega.github.io/vega-lite/docs/spec.html#view-background - }, - }, - }; - } -} diff --git a/src/Taskview/columns/Histogram.ts b/src/Taskview/columns/Histogram.ts new file mode 100644 index 0000000..3d4f4c6 --- /dev/null +++ b/src/Taskview/columns/Histogram.ts @@ -0,0 +1,334 @@ +import vegaEmbed from 'vega-embed'; +import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; +import { colors } from '../../config/colors'; +import { IAttribute } from '../../data'; +import { INumRange, IEqualsList, NumRangeOperators } from '../../base'; +import { getAnimatedLoadingBars, log } from '../../util'; +import { easyLabelFromFilter, niceName } from '../../utils/labels'; +import { ICohort } from '../../app/interfaces'; + +export class Histogram { + readonly $node: HTMLDivElement; + + readonly $loader: HTMLDivElement; + + readonly $hist: HTMLDivElement; + + vegaView: any; + + public showReference = true; + + constructor(private attribute: IAttribute, private cohort: ICohort, showReference, private index, private color) { + this.showReference = showReference; + this.$node = document.createElement('div'); + this.$node.classList.add('hist'); + + this.$loader = document.createElement('div'); + this.$loader.classList.add('loader'); // center content with flexbox + this.$loader.appendChild(getAnimatedLoadingBars()); + + this.$hist = document.createElement('div'); + + this.$node.appendChild(this.$hist); + + // eslint-disable-next-line @typescript-eslint/no-this-alias + const that = this; + setTimeout(() => that.updateNode.bind(that)(), 0); // run async + } + + public async updateNode() { + this.$hist.remove(); + try { + if (!this.cohort.hasfilterConflict()) { + this.$node.appendChild(this.$loader); + // get hist data of attribute for cohort + let data: { bin: string; count: number }[]; + try { + data = await this.attribute.getHist(this.cohort.dbId, this.cohort.filters); + } catch (e) { + log.error('get hist failed', e); + this.$loader.remove(); + this.$node.classList.add('text'); + this.$hist.innerHTML = ` +

+ + Request timeout. +

+ `; + this.$node.appendChild(this.$hist); + return; + } + + const notZeroData = data.filter((d) => d.count > 0); // filter only for categories/bins with count bigger 0 + let showText = false; + // show only text when 1 bin and not numerical attribute or when numerical attribute only for one bin if it is for null + if ( + this.attribute.type !== 'number' || + (this.attribute.type === 'number' && notZeroData.length === 1 && (notZeroData[0].bin === 'null' || notZeroData[0].bin === null)) + ) { + showText = notZeroData.length === 1; // show text when only one category/bin with count bigger 0 + } + + const chtSize = await this.cohort.size; + // -> size = 0: show only line in data cell with vega vis (all bins/categories have count 0) + + // check if reference should be shown and + // if the size of the current cohort is bigger then 0 + if (this.showReference && chtSize > 0) { + if (this.cohort.getCohortParents().length > 0) { + // get parent cohort + const parentCht = this.cohort.getCohortParents()[0]; + // get hist data of attribtue for parent cohort + const parentData: { bin: string; count: number }[] = await this.attribute.getHist(parentCht.dbId, parentCht.filters); + const notZeroParentData = parentData.filter((d) => d.count > 0); // filter only for categories/bins with count bigger 0 + const showParentText = notZeroParentData.length === 1; // show text when only one category/bin with count bigger 0 + const vegaData = { + data, + parentData, + parentColor: parentCht.colorTaskView, + }; + if (showParentText) { + const { bin } = notZeroParentData[0]; + // TODO labels + // const text = this.formatText(bin); + const text = this.formatText(bin, this.cohort.values); + this.$node.classList.add('text'); + this.$hist.innerHTML = text; + } else { + // show histogram with reference + this.$node.classList.remove('text'); + vegaEmbed(this.$hist, this.getMinimalVegaSpecWithRef(vegaData), { actions: false, renderer: 'svg' }).then((result) => { + this.vegaView = result.view; + }); + } + } + } else if (showText) { + // when no reference should be shown (is also the case for the input cohort side) + // check if only one category/bin has values + // show only text + const { bin } = notZeroData[0]; + // TODO labels + // const text = this.formatText(bin); + this.$node.classList.add('text'); + const text = this.formatText(bin, this.cohort.values); + this.$hist.innerHTML = text; + } else { + // show histogram + this.$node.classList.remove('text'); + vegaEmbed(this.$hist, this.getMinimalVegaSpec(data), { actions: false, renderer: 'svg' }).then((result) => { + this.vegaView = result.view; + }); + } + + this.$loader.remove(); + this.$node.appendChild(this.$hist); + } else { + throw new Error('Can not show histogram due to filter contradiction'); + } + } catch (e) { + log.error('Cant show histogram', e); + + this.$loader.remove(); + this.$node.classList.add('text'); + this.$hist.innerHTML = ` +

+ + Can not display histogram. +

+ `; + this.$node.appendChild(this.$hist); + } + } + + // TODO labels + // private formatText(value: string): string { + private formatText(value: string, filters: Array): string { + if (value === 'null' || value === null || value === undefined) { + value = `Missing Values`; + } + let text = String(value); // turn boolean categories into strings + + if (text.indexOf('(') >= 0 || text.indexOf('[') >= 0) { + const values = text.split(', '); + const params = { + firstOp: values[0][0], + firstValue: Number(values[0].substring(1)), + secondOp: values[1][values[1].length - 1], + // TODO labels + // secondValue: Number(values[1].substring(0, values[1].length - 2)) + secondValue: Number(values[1].substring(0, values[1].length - 1)), + }; + // TODO lables + // text = labelFromRanges(params.firstOp === '(' ? '(' : '[', params.firstValue, params.secondValue, params.secondOp === ')' ? ')' : ']', this.attribute); + const range: INumRange = { + operatorOne: params.firstOp === '(' ? NumRangeOperators.gt : NumRangeOperators.gte, + valueOne: params.firstValue, + operatorTwo: params.secondOp === ')' ? NumRangeOperators.lt : NumRangeOperators.lte, + valueTwo: params.secondValue, + }; + + text = easyLabelFromFilter(range, this.attribute.label); + } else { + text = niceName(text); + } + return text; + } + + public getNode() { + return this.$node; + } + + public getMinimalVegaSpec(data): VegaLiteSpec { + let sort: any = 'ascending'; + if (this.attribute.type === 'number') { + sort = { field: 'index' }; + } + return { + $schema: 'https://vega.github.io/schema/vega-lite/v5.json', + width: 'container', // responsive width + height: 50, // responsive height + background: '#ffffff00', // set visualization background to white and opacity 0 -> https://vega.github.io/vega-lite/docs/spec.html#example-background + autosize: { type: 'fit', contains: 'padding' }, // plots fit a bit better, if though they are specified as responsive + data: { values: data }, + mark: { + type: 'bar', + tooltip: true, + }, + encoding: { + x: { + field: 'bin', + type: 'nominal', + axis: { + title: null, // we will have the title in the column header + labels: false, // no space for labels + ticks: false, + }, + sort, + }, + y: { + field: 'count', + type: 'quantitative', + axis: null, // no axis, no title, labels, no grid + }, + color: { + field: 'this_does_not_exist', // field is necessary but does not have to exist + type: 'nominal', // type is also necessary, nominal as we have only one category (or none :) ) + scale: { + range: [this.cohort.colorTaskView || colors.barColor], // [this.color ? Cat16.get(this.index) : colors.barColor] + }, + legend: null, // no legend + }, + tooltip: [ + { field: 'bin', type: 'nominal' }, + { field: 'count', type: 'quantitative' }, + ], + }, + config: { + view: { + stroke: 'transparent', // https://vega.github.io/vega-lite/docs/spec.html#view-background + }, + }, + params: [ + { + name: 'highlight', + select: { + type: 'point', + on: 'mouseover', + clear: 'mouseout', + }, + }, + ], + }; + } + + public getMinimalVegaSpecWithRef(data): VegaLiteSpec { + let sort: any = 'ascending'; + if (this.attribute.type === 'number') { + sort = { field: 'index' }; + } + return { + $schema: 'https://vega.github.io/schema/vega-lite/v5.json', + width: 'container', // responsive width + height: 50, // responsive height + background: '#ffffff00', // set visualization background to white and opacity 0 -> https://vega.github.io/vega-lite/docs/spec.html#example-background + autosize: { type: 'fit', contains: 'padding' }, // plots fit a bit better, if though they are specified as responsive + datasets: { + parentData: data.parentData, + data: data.data, + }, + layer: [ + { + // layer 1: input cohort reference data + data: { name: 'parentData' }, + mark: { + type: 'bar', + tooltip: true, + }, + encoding: { + x: { + field: 'bin', + type: 'nominal', + axis: { + title: null, // we will have the title in the column header + labels: false, // no space for labels + ticks: false, + }, + sort, + }, + y: { + field: 'count', + type: 'quantitative', + axis: null, // no axis, no title, labels, no grid + }, + fill: { + value: data.parentColor, + legend: null, // no legend + }, + tooltip: [ + { field: 'bin', type: 'nominal' }, + { field: 'count', type: 'quantitative' }, + ], + }, + }, + { + // layer 2: output cohort data + data: { name: 'data' }, + mark: { + type: 'bar', + tooltip: true, + }, + encoding: { + x: { + field: 'bin', + type: 'nominal', + axis: { + title: null, // we will have the title in the column header + labels: false, // no space for labels + ticks: false, + }, + sort, + }, + y: { + field: 'count', + type: 'quantitative', + axis: null, // no axis, no title, labels, no grid + }, + fill: { + value: colors.barColor, // TODO: check if this value actually exists + legend: null, // no legend + }, + tooltip: [ + { field: 'bin', type: 'nominal' }, + { field: 'count', type: 'quantitative' }, + ], + }, + }, + ], + config: { + view: { + stroke: 'transparent', // https://vega.github.io/vega-lite/docs/spec.html#view-background + }, + }, + }; + } +} diff --git a/src/Taskview/columns/PrevalenceColumn.ts b/src/Taskview/columns/PrevalenceColumn.ts index 72c2d04..c9d6be7 100644 --- a/src/Taskview/columns/PrevalenceColumn.ts +++ b/src/Taskview/columns/PrevalenceColumn.ts @@ -5,17 +5,6 @@ import { colors } from '../../config/colors'; import { getAnimatedLoadingBars } from '../../util'; import { ADataColumn } from './AColumn'; -export default class PrevalenceColumn extends ADataColumn { - constructor(private reference: ICohort, $container: HTMLDivElement) { - super(`% [${reference.label}]`, $container); - this.$column.classList.add('prevalence'); - } - - async setCellContent(cell: HTMLDivElement, cht: ICohort): Promise { - cell.appendChild(new PrevalenceBar(cht, this.reference).getNode()); - } -} - class PrevalenceBar { readonly $node: HTMLDivElement; @@ -35,6 +24,7 @@ class PrevalenceBar { this.$node.appendChild(this.$hist); + // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this; setTimeout(() => that.updateNode.bind(that)(), 0); // run async } @@ -115,3 +105,14 @@ class PrevalenceBar { }; } } + +export default class PrevalenceColumn extends ADataColumn { + constructor(private reference: ICohort, $container: HTMLDivElement) { + super(`% [${reference.label}]`, $container); + this.$column.classList.add('prevalence'); + } + + async setCellContent(cell: HTMLDivElement, cht: ICohort): Promise { + cell.appendChild(new PrevalenceBar(cht, this.reference).getNode()); + } +} diff --git a/src/Taskview/columns/index.ts b/src/Taskview/columns/index.ts index 49ce44f..65ed462 100644 --- a/src/Taskview/columns/index.ts +++ b/src/Taskview/columns/index.ts @@ -1,8 +1,5 @@ export * from './AColumn'; -export * from './AttributeColumn'; export { default as AttributeColumn } from './AttributeColumn'; export * from './CohortColumn'; -export * from './NumberColumn'; export { default as NumberColumn } from './NumberColumn'; -export * from './PrevalenceColumn'; export { default as PrevalenceColumn } from './PrevalenceColumn'; diff --git a/src/Taskview/index.ts b/src/Taskview/index.ts index f8de926..dab705a 100644 --- a/src/Taskview/index.ts +++ b/src/Taskview/index.ts @@ -3,7 +3,5 @@ export * from './tasks'; export * from './visualizations'; export * from './SearchBar'; -export * from './SearchColumn'; export { default as SearchColumn } from './SearchColumn'; -export * from './Taskview'; export { default as Taskview } from './Taskview'; diff --git a/src/Taskview/interfaces.ts b/src/Taskview/interfaces.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/Taskview/tasks/ATask.ts b/src/Taskview/tasks/ATask.ts index 5bf7ce6..e77f3b4 100644 --- a/src/Taskview/tasks/ATask.ts +++ b/src/Taskview/tasks/ATask.ts @@ -1,6 +1,6 @@ import { select, Selection } from 'd3v7'; import { ICohort } from '../../app/interfaces'; -import { IAttribute } from '../../data/Attribute'; +import { IAttribute } from '../../data/IAttribute'; import { OnboardingManager } from '../../OnboardingManager'; export const TASK_CLOSE_EVENT_TYPE = 'task:close'; @@ -33,6 +33,7 @@ export abstract class ATask { abstract showSearchBar(): boolean; show(columnHeader: HTMLDivElement, container: HTMLDivElement, attributes: IAttribute[], cohorts: ICohort[]) { + // eslint-disable-next-line @typescript-eslint/no-this-alias const task = this; select(container).selectAll('*').remove(); select(columnHeader).selectAll('.task-title').remove(); @@ -56,6 +57,7 @@ export abstract class ATask { ${this.label}`, ) .on('click', function () { + // eslint-disable-next-line @typescript-eslint/no-this-alias const clickedBtn = this; clickedBtn.dispatchEvent(new TaskCloseEvent(task)); }); diff --git a/src/Taskview/tasks/Compare.ts b/src/Taskview/tasks/Compare.ts index 4e08397..ee5a899 100644 --- a/src/Taskview/tasks/Compare.ts +++ b/src/Taskview/tasks/Compare.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ +/* eslint-disable @typescript-eslint/no-use-before-define */ /* ***************************************************************************** * Caleydo - Visualization for Molecular Biology - http://caleydo.org * Copyright (c) The Caleydo Team. All rights reserved. @@ -6,11 +8,12 @@ import { hsl, scaleLinear, select, Selection } from 'd3v7'; import * as d3v3 from 'd3v3'; -import { IMeasureResult, IMeasureVisualization, ISetParameters, ISimilarityMeasure, MethodManager, SCOPE, Type, WorkerManager } from 'tourdino'; -import { Attribute, IAttribute } from '../../data/Attribute'; +import { IMeasureResult, ISetParameters, ISimilarityMeasure, MethodManager, SCOPE, Type, WorkerManager } from 'tourdino'; +import { Attribute } from '../../data/Attribute'; import { log } from '../../util'; import { ATask } from './ATask'; import { ICohort } from '../../app/interfaces'; +import type { IAttribute } from '../../data/IAttribute'; export class Compare extends ATask { public label = `Compare`; @@ -82,8 +85,8 @@ export class Compare extends ATask { const colHeadsChtSpan = colHeadsCht.enter().append('th').attr('class', 'head rotate').append('div').append('span').append('span'); // th.head are the column headers const that = this; // for the function below - const updateTableBody = (bodyData: Array>>, timestamp: string) => { - if (that.body.attr('data-timestamp') !== timestamp) { + const updateTableBody = (bodyData: Array>>, timestampArg: string) => { + if (that.body.attr('data-timestamp') !== timestampArg) { return; // skip outdated result } @@ -220,6 +223,8 @@ export class Compare extends ATask { for (const [rowIndex, rowCht] of cohorts.entries()) { // Get the data of 'attr' for the rows inside 'rowCht' + // TODO: fix me + // eslint-disable-next-line no-await-in-loop const rowData = (await attr.getData(rowCht.dbId)).map((item) => item[attr.dataKey]); for (const [colIndex, colCht] of cohorts.entries()) { const colIndexOffset = rowIndex === 0 ? 2 : 1; // Two columns if the attribute label is in the same line, (otherwise 1 (because rowspan)) @@ -230,6 +235,8 @@ export class Compare extends ATask { } else if (rowIndex4col[rowIndex] >= 0 && rowIndex4col[colIndex] >= 0 && rowIndex4col[colIndex] < rowIndex) { // the cht is also part of the colGroups array, and the colGrp is one of the previous rowGroups --> i.e. already calculated in a table row above the current one } else { + // TODO: fix me + // eslint-disable-next-line no-await-in-loop const colData = (await attr.getData(colCht.dbId)).map((item) => item[attr.dataKey]); const setParameters = { setA: rowData, @@ -316,6 +323,7 @@ export class Compare extends ATask { } toScoreCell(score: IMeasureResult, measure: ISimilarityMeasure, setParameters: ISetParameters): IScoreCell { + // eslint-disable-next-line @typescript-eslint/no-use-before-define let color = score2color(score.pValue); let cellLabel = score.pValue.toFixed(3); @@ -383,7 +391,8 @@ export class Compare extends ATask { const category = rowCategories.pop(); const isColTask = category === row; const cellData = select(tableCell).datum() as IScoreCell; - const scoreValue = typeof cellData.score.scoreValue === 'number' && !isNaN(cellData.score.scoreValue) ? cellData.score.scoreValue.toFixed(3) : 'n/a'; + const scoreValue = + typeof cellData.score.scoreValue === 'number' && !Number.isNaN(cellData.score.scoreValue) ? cellData.score.scoreValue.toFixed(3) : 'n/a'; let scorePvalue: string | number = cellData.score.pValue; if (scorePvalue === -1) { scorePvalue = 'n/a'; @@ -545,7 +554,7 @@ export class Compare extends ATask { detailSetInfo.append('span').html(`${setBLabel} `).append('span').text(`[${measureResult.setSizeB}]`); // test value + p-value - const scoreValue = typeof measureResult.scoreValue === 'number' && !isNaN(measureResult.scoreValue) ? measureResult.scoreValue.toFixed(3) : 'n/a'; + const scoreValue = typeof measureResult.scoreValue === 'number' && !Number.isNaN(measureResult.scoreValue) ? measureResult.scoreValue.toFixed(3) : 'n/a'; const pValue = measureResult.pValue === -1 ? 'n/a' : (measureResult.pValue as number).toExponential(3); const detailInfoValues = divDetailInfo.append('div').classed('detailDiv', true); // .text(`Test-Value: ${scoreValue}, p-Value: ${pValue}`); diff --git a/src/Taskview/tasks/Details.ts b/src/Taskview/tasks/Details.ts index 7257a09..385fac9 100644 --- a/src/Taskview/tasks/Details.ts +++ b/src/Taskview/tasks/Details.ts @@ -3,12 +3,12 @@ import { select } from 'd3v7'; import * as LineUpJS from 'lineupjs'; import { ICohort } from '../../app/interfaces'; import { colors, CoralColorSchema } from '../../config/colors'; -import { IAttribute } from '../../data/Attribute'; import { getCohortData } from '../../base/rest'; import { getAnimatedLoadingText } from '../../util'; import { getIdTypeFromCohort } from '../../config/entities'; import { DATA_LABEL } from '../visualizations'; import { ATask } from './ATask'; +import type { IAttribute } from '../../data/IAttribute'; export class Details extends ATask { public label = `Inspect Items`; @@ -65,15 +65,14 @@ export class Details extends ATask { const idType = getIdTypeFromCohort(cohorts[0] as ICohort); this._entityName = idType.entityName; - const dataPromises = cohorts.map((cht) => { - const promise = new Promise(async (resolve, reject) => { + const dataPromises = cohorts.map(async (cht) => { + const promise = new Promise((resolve, reject) => { const chtDataPromises = attributes.map((attr) => attr.getData(cht.dbId)); if (attributes.length === 0) { // If Lineup is empty, add entityName as single attribute to be able to show something chtDataPromises.push(getCohortData({ cohortId: cht.dbId, attribute: idType.entityName })); } - try { - const chtData = await Promise.all(chtDataPromises); // array with one entry per attribute, which contains an array with one value for every item in the cohort + Promise.all(chtDataPromises).then((chtData) => { let joinedData = aq.from(chtData[0]); for (let i = 1; i < chtData.length; i++) { joinedData = joinedData.join_full(aq.from(chtData[i])); @@ -81,9 +80,7 @@ export class Details extends ATask { const labelTable = aq.table({ [DATA_LABEL]: [[`${cht.dbId}`]], [`id_${cht.dbId}`]: ['true'] }); joinedData = joinedData.join_left(labelTable, (data, label) => true); resolve(joinedData.objects()); - } catch (e) { - reject(e); - } + }); }); return promise; }); diff --git a/src/Taskview/tasks/Filter.ts b/src/Taskview/tasks/Filter.ts index 77ea202..d3e0343 100644 --- a/src/Taskview/tasks/Filter.ts +++ b/src/Taskview/tasks/Filter.ts @@ -1,6 +1,6 @@ import { Selection } from 'd3v7'; import { ICohort } from '../../app/interfaces'; -import { IAttribute } from '../../data/Attribute'; +import { IAttribute } from '../../data/IAttribute'; import { getAnimatedLoadingText, log } from '../../util'; import { AreaChart } from '../visualizations/AreaChart'; import { AVegaVisualization } from '../visualizations/AVegaVisualization'; @@ -160,11 +160,11 @@ export class Filter extends ATask { .classed('selected', (vis) => (vis as any).NAME === (this.vis.constructor as any).NAME) .append('a') .text((vis) => (vis as any).NAME) // cast to any to access static property - .on('click', (event, visClass) => { - if ((visClass as any).NAME !== (this.vis.constructor as any).NAME) { + .on('click', (event, VisClass) => { + if ((VisClass as any).NAME !== (this.vis.constructor as any).NAME) { // check if vis has changed - this.header.selectAll('.vis-selector .vis-type li').classed('selected', (vis) => (vis as any).NAME === (visClass as any).NAME); - this.showWithVis(new visClass()); + this.header.selectAll('.vis-selector .vis-type li').classed('selected', (vis) => (vis as any).NAME === (VisClass as any).NAME); + this.showWithVis(new VisClass()); } }); } diff --git a/src/Taskview/tasks/Prevalence.ts b/src/Taskview/tasks/Prevalence.ts index 776beaf..843a4f4 100644 --- a/src/Taskview/tasks/Prevalence.ts +++ b/src/Taskview/tasks/Prevalence.ts @@ -1,11 +1,11 @@ import { format, select, transition } from 'd3v7'; import tippy from 'tippy.js'; import { ICohort, IBloodlineElement } from '../../app/interfaces'; -import { getRootCohort } from '../../cohortview'; +import { IEqualsList, INumRange } from '../../base/interfaces'; +import { CohortContext } from '../../CohortContext'; import { colors } from '../../config/colors'; -import { IAttribute, multiFilter } from '../../data/Attribute'; -import { IEqualsList, INumRange } from '../../base/rest'; -import { Task } from '../../Tasks'; +import { IAttribute, multiFilter } from '../../data'; +import type { Task } from '../../Tasks'; import { createHTMLElementWithClasses, getSessionStorageItem, setSessionStorageItem } from '../../util'; import { easyLabelFromFilter } from '../../utils/labels'; import { ATask } from './ATask'; @@ -806,6 +806,8 @@ export class Prevalence extends ATask { for (const at of activeTasks) { const attValue = taskpair.filter((elem) => elem.taskId === at.id)[0].values; // create new cohort in db with the attribute and value + // TODO: fix me + // eslint-disable-next-line no-await-in-loop newBaseCohort = await multiFilter(oldBaseCohort, at.attributes, attValue); oldBaseCohort = newBaseCohort; } @@ -816,6 +818,9 @@ export class Prevalence extends ATask { for (const nat of notActiveTasks) { // log.debug('not active Task pair: ', {attribute: nat.attribute, value: valExclMVForTask}); // create new cohort in db with the attribute and value + + // TODO: fix me + // eslint-disable-next-line no-await-in-loop newBaseCohort = await multiFilter(oldBaseCohort, nat.attributes, new Array(nat.attributes.length).fill(valExclMVForTask)); oldBaseCohort = newBaseCohort; } @@ -1016,7 +1021,7 @@ export class Prevalence extends ATask { // sets the base cohort on which all task operations will be applied private setBaseCohort() { - this.baseCohort = getRootCohort(); + this.baseCohort = CohortContext.referenceCohort; this.baseCohortSize = this.baseCohort.getRetrievedSize(); } diff --git a/src/Taskview/visualizations/AVegaVisualization.ts b/src/Taskview/visualizations/AVegaVisualization.ts index cab70ba..fb0f0c0 100644 --- a/src/Taskview/visualizations/AVegaVisualization.ts +++ b/src/Taskview/visualizations/AVegaVisualization.ts @@ -1,18 +1,19 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ import { format, select } from 'd3v7'; import { cloneDeep } from 'lodash'; import tippy from 'tippy.js'; import { View as VegaView } from 'vega'; import vegaEmbed from 'vega-embed'; import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; -import { Cohort, getCohortLabel } from '../../Cohort'; +import { getCohortLabel } from '../../Cohort'; import { ICohort } from '../../app/interfaces'; -import { IAttribute, IdValuePair } from '../../data/Attribute'; -import { IEqualsList, INumRange, NumRangeOperators } from '../../base/rest'; import { IFilterDesc, log } from '../../util'; import { FilterEvent, SplitEvent } from '../../base/events'; import { Option, VisConfig } from './config/VisConfig'; import { DATA_LABEL } from './constants'; import { IVisualization } from './IVisualization'; +import { IAttribute, IdValuePair } from '../../data/IAttribute'; +import { IEqualsList, INumRange, NumRangeOperators } from '../../base/interfaces'; export const MISSING_VALUES_LABEL = 'Missing Values'; export const FACETED_CHARTS_WIDTH = 520; @@ -64,7 +65,7 @@ export abstract class AVegaVisualization implements IVegaVisualization { protected config: VisConfig[] = []; - constructor(protected vegaLiteOptions: Object = {}) {} + constructor(protected vegaLiteOptions: object = {}) {} clearSelection() { if (this.vegaView) { @@ -472,60 +473,58 @@ export abstract class SingleAttributeVisualization extends AVegaVisualization { }, ], }); - } else { + } else if (this.attribute.type === 'number') { // 1 cohort, multiple categories, or one category, multiple cohorts - if (this.attribute.type === 'number') { - // n cohorts, one range (can't be more because we use one interval selection) - const chtRanges: { cht: ICohort; ranges: { from: number; to: number }[] }[] = []; // create a list of chts and their filtered categories - for (const bin of bins) { - const chtRange = chtRanges.find((elem) => elem.cht.id === bin.cohort.id); - if (chtRange !== undefined) { - // I handled this cohort before - chtRange.ranges.push({ from: bin.from as number, to: bin.to as number }); // add range to exisiting list - } else { - // new cht, create object and init range array - chtRanges.push({ cht: bin.cohort, ranges: [{ from: bin.from as number, to: bin.to as number }] }); - } + // n cohorts, one range (can't be more because we use one interval selection) + const chtRanges: { cht: ICohort; ranges: { from: number; to: number }[] }[] = []; // create a list of chts and their filtered categories + for (const bin of bins) { + const chtRange = chtRanges.find((elem) => elem.cht.id === bin.cohort.id); + if (chtRange !== undefined) { + // I handled this cohort before + chtRange.ranges.push({ from: bin.from as number, to: bin.to as number }); // add range to exisiting list + } else { + // new cht, create object and init range array + chtRanges.push({ cht: bin.cohort, ranges: [{ from: bin.from as number, to: bin.to as number }] }); } - // get a filter for the categories per cohort - filterDesc = chtRanges.map((elem) => { - const filters = []; - for (const rg of elem.ranges) { - filters.push(this.getNumericalFilterAllInclusive(rg.from as number, rg.to as number)); - } - return { - cohort: elem.cht, - filter: [ - { - attr: this.attribute, - range: filters, - }, - ], - }; - }); - } else { - const chtCats: { cht: ICohort; cats: string[] }[] = []; // create a list of chts and their filtered categories - for (const bin of bins) { - const chtCat = chtCats.find((elem) => elem.cht.id === bin.cohort.id); - if (chtCat !== undefined) { - // I handled this cohort before - chtCat.cats.push(String(bin.from)); // add category to exisiting list - } else { - // new cht, create object and init category array - chtCats.push({ cht: bin.cohort, cats: [String(bin.from)] }); - } + } + // get a filter for the categories per cohort + filterDesc = chtRanges.map((elem) => { + const filters = []; + for (const rg of elem.ranges) { + filters.push(this.getNumericalFilterAllInclusive(rg.from as number, rg.to as number)); } - // get a filter for the categories per cohort - filterDesc = chtCats.map((elem) => ({ + return { cohort: elem.cht, filter: [ { attr: this.attribute, - range: this.getCategoricalFilter(elem.cats), + range: filters, }, ], - })); + }; + }); + } else { + const chtCats: { cht: ICohort; cats: string[] }[] = []; // create a list of chts and their filtered categories + for (const bin of bins) { + const chtCat = chtCats.find((elem) => elem.cht.id === bin.cohort.id); + if (chtCat !== undefined) { + // I handled this cohort before + chtCat.cats.push(String(bin.from)); // add category to exisiting list + } else { + // new cht, create object and init category array + chtCats.push({ cht: bin.cohort, cats: [String(bin.from)] }); + } } + // get a filter for the categories per cohort + filterDesc = chtCats.map((elem) => ({ + cohort: elem.cht, + filter: [ + { + attr: this.attribute, + range: this.getCategoricalFilter(elem.cats), + }, + ], + })); } log.debug('show filter description: ', filterDesc); this.container.dispatchEvent(new FilterEvent(filterDesc)); @@ -825,11 +824,11 @@ export abstract class SingleAttributeVisualization extends AVegaVisualization { const scale = this.vegaView.scale('x'); // if one or both ranges are set, replace with values - if (range[0] !== undefined && !isNaN(range[0])) { + if (range[0] !== undefined && !Number.isNaN(range[0])) { scaledRange[0] = scale(range[0]); // get min value from input } - if (range[1] !== undefined && !isNaN(range[1])) { + if (range[1] !== undefined && !Number.isNaN(range[1])) { scaledRange[1] = scale(range[1]); // get max value from input if (range[0] === range[1]) { scaledRange[1] = scale(range[1]) + 10 ** -10; // the 10^(-10) are independent of the attribute domain (i.e. values of 0 to 1 or in millions) because we add it after scaling (its a fraction of a pixel) diff --git a/src/Taskview/visualizations/AreaChart.ts b/src/Taskview/visualizations/AreaChart.ts index 597808b..7b71dc0 100644 --- a/src/Taskview/visualizations/AreaChart.ts +++ b/src/Taskview/visualizations/AreaChart.ts @@ -2,14 +2,14 @@ import log from 'loglevel'; import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; import { getCohortLabels } from '../../Cohort'; import { ICohort } from '../../app/interfaces'; -import { IdValuePair } from '../../data/Attribute'; import { DATA_LABEL } from './constants'; import { MultiAttributeVisualization } from './MultiAttributeVisualization'; +import { IdValuePair } from '../../data/IAttribute'; export class AreaChart extends MultiAttributeVisualization { static readonly NAME = 'Area Chart'; - constructor(vegaLiteOptions: Object = {}) { + constructor(vegaLiteOptions: object = {}) { super(vegaLiteOptions); } diff --git a/src/Taskview/visualizations/DensityPlot.ts b/src/Taskview/visualizations/DensityPlot.ts index 7be122f..b7bfccb 100644 --- a/src/Taskview/visualizations/DensityPlot.ts +++ b/src/Taskview/visualizations/DensityPlot.ts @@ -3,10 +3,10 @@ import log from 'loglevel'; import { Spec as VegaSpec } from 'vega'; import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; import { ICohort } from '../../app/interfaces'; -import { IdValuePair } from '../../data/Attribute'; import { AVegaVisualization, SingleAttributeVisualization } from './AVegaVisualization'; import { DATA_LABEL } from './constants'; import { countConfig, counts } from './config/CountCounfig'; +import { IdValuePair } from '../../data/IAttribute'; export class DensityPlot extends SingleAttributeVisualization { static readonly NAME = 'Density Plot'; @@ -15,7 +15,7 @@ export class DensityPlot extends SingleAttributeVisualization { protected readonly type = 'quantitative'; - constructor(vegaLiteOptions: Object = {}) { + constructor(vegaLiteOptions: object = {}) { super(vegaLiteOptions); this.config = [{ icon: '', label: 'Density Estimation Method', groups: [countConfig] }]; @@ -383,11 +383,11 @@ export class DensityPlot extends SingleAttributeVisualization { update: { cursor: { value: 'text' }, tooltip: { - signal: `{\'${this.attribute.label}\': format(datum[\'value\'], \'\'), \'Density\': format(datum[\'density\'], \'\'), \'value\': format(datum[\'value\'], \'\'), \'${DATA_LABEL}\': isValid(datum[\'${DATA_LABEL}\']) ? datum[\'${DATA_LABEL}\'] : \'\'+datum[\'${DATA_LABEL}\']}`, + signal: `{'${this.attribute.label}': format(datum['value'], ''), 'Density': format(datum['density'], ''), 'value': format(datum['value'], ''), '${DATA_LABEL}': isValid(datum['${DATA_LABEL}']) ? datum['${DATA_LABEL}'] : ''+datum['${DATA_LABEL}']}`, }, stroke: { scale: 'color', field: DATA_LABEL }, description: { - signal: `\'${this.attribute.label}: \' + (format(datum[\'value\'], \'\')) + \'; Density: \' + (format(datum[\'density\'], \'\')) + \'; value: \' + (format(datum[\'value\'], \'\')) + \'; ${DATA_LABEL}: \' + (isValid(datum[\'${DATA_LABEL}\']) ? datum[\'${DATA_LABEL}\'] : \'\'+datum[\'${DATA_LABEL}\'])`, + signal: `'${this.attribute.label}: ' + (format(datum['value'], '')) + '; Density: ' + (format(datum['density'], '')) + '; value: ' + (format(datum['value'], '')) + '; ${DATA_LABEL}: ' + (isValid(datum['${DATA_LABEL}']) ? datum['${DATA_LABEL}'] : ''+datum['${DATA_LABEL}'])`, }, x: { scale: 'x', field: 'value' }, y: { scale: 'y', field: 'density' }, diff --git a/src/Taskview/visualizations/GroupedBoxplot.ts b/src/Taskview/visualizations/GroupedBoxplot.ts index 6d33ffa..44de9a6 100644 --- a/src/Taskview/visualizations/GroupedBoxplot.ts +++ b/src/Taskview/visualizations/GroupedBoxplot.ts @@ -1,16 +1,17 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ import { format, select } from 'd3v7'; import log from 'loglevel'; import { Spec as VegaSpec } from 'vega'; import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; import { ICohort } from '../../app/interfaces'; -import { IAttribute, IdValuePair } from '../../data/Attribute'; -import { NumRangeOperators } from '../../base/rest'; import { IAttributeFilter, IFilterDesc } from '../../util'; import { FilterEvent, SplitEvent } from '../../base/events'; import { AVegaVisualization } from './AVegaVisualization'; import { groupByConfig } from './config/GroupConfig'; import { BRUSH_DATA_END, BRUSH_DATA_NAME, BRUSH_DATA_START, DATA_LABEL } from './constants'; import { MultiAttributeVisualization } from './MultiAttributeVisualization'; +import { NumRangeOperators } from '../../base'; +import { IAttribute, IdValuePair } from '../../data'; export class GroupedBoxplot extends MultiAttributeVisualization { static readonly NAME = 'Boxplot'; @@ -21,7 +22,7 @@ export class GroupedBoxplot extends MultiAttributeVisualization { brushData: object[] = []; - constructor(vegaLiteOptions: Object = {}) { + constructor(vegaLiteOptions: object = {}) { super(vegaLiteOptions); this.config = [{ icon: '', label: 'Group', groups: [groupByConfig] }]; @@ -67,10 +68,10 @@ export class GroupedBoxplot extends MultiAttributeVisualization { const newRange = scale.domain(); // if one or both ranges are set, replace with values - if (range[0] !== undefined && !isNaN(range[0])) { + if (range[0] !== undefined && !Number.isNaN(range[0])) { newRange[0] = range[0]; // get min value from input } - if (range[1] !== undefined && !isNaN(range[1])) { + if (range[1] !== undefined && !Number.isNaN(range[1])) { newRange[1] = range[1]; // get max value from input if (range[0] === range[1]) { newRange[1] = scale.invert(scale(range[1]) + 10 ** -10); // the 10^(-10) are independent of the attribute domain (i.e. values of 0 to 1 or in millions) because we add it after scaling (its a fraction of a pixel) diff --git a/src/Taskview/visualizations/GroupedHistogram.ts b/src/Taskview/visualizations/GroupedHistogram.ts index 720b3b2..b32e079 100644 --- a/src/Taskview/visualizations/GroupedHistogram.ts +++ b/src/Taskview/visualizations/GroupedHistogram.ts @@ -4,7 +4,6 @@ import { TopLevelUnitSpec } from 'vega-lite/build/src/spec/unit'; import { getCohortLabel } from '../../Cohort'; import { ICohort } from '../../app/interfaces'; import { colors } from '../../config/colors'; -import { IdValuePair } from '../../data/Attribute'; import { log } from '../../util'; import { AVegaVisualization, FACETED_CHARTS_WIDTH } from './AVegaVisualization'; import { groupByConfig } from './config/GroupConfig'; @@ -12,13 +11,14 @@ import { dataConfig } from './config/ScaleConfig'; import { sortByConfig, sortOrderConfig } from './config/SortConfig'; import { DATA_LABEL } from './constants'; import { VegaHistogram } from './Histogram'; +import { IdValuePair } from '../../data/IAttribute'; export class VegaGroupedHistogram extends VegaHistogram { static readonly COUNT = 'Count'; protected readonly type = 'nominal'; - constructor(vegaLiteOptions: Object = {}) { + constructor(vegaLiteOptions: object = {}) { super(vegaLiteOptions); this.config = [ diff --git a/src/Taskview/visualizations/Histogram.ts b/src/Taskview/visualizations/Histogram.ts index 82078ff..a9fc51d 100644 --- a/src/Taskview/visualizations/Histogram.ts +++ b/src/Taskview/visualizations/Histogram.ts @@ -1,7 +1,7 @@ import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; import { ICohort } from '../../app/interfaces'; -import { IdValuePair } from '../../data/Attribute'; import { colors } from '../../config/colors'; +import { IdValuePair } from '../../data/IAttribute'; import { log } from '../../util'; import { AVegaVisualization, SingleAttributeVisualization } from './AVegaVisualization'; import { DATA_LABEL } from './constants'; diff --git a/src/Taskview/visualizations/IVisualization.ts b/src/Taskview/visualizations/IVisualization.ts index 13190db..33943b1 100644 --- a/src/Taskview/visualizations/IVisualization.ts +++ b/src/Taskview/visualizations/IVisualization.ts @@ -1,5 +1,5 @@ -import { ICohort } from '../../app/interfaces'; -import { IAttribute } from '../../data/Attribute'; +import type { ICohort } from '../../app/interfaces'; +import type { IAttribute } from '../../data/IAttribute'; export interface IVisualization { destroy(); diff --git a/src/Taskview/visualizations/KaplanMeierPlot.ts b/src/Taskview/visualizations/KaplanMeierPlot.ts index 1a65193..d65b5eb 100644 --- a/src/Taskview/visualizations/KaplanMeierPlot.ts +++ b/src/Taskview/visualizations/KaplanMeierPlot.ts @@ -1,16 +1,16 @@ import log from 'loglevel'; -import { select } from 'd3v7'; import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; import { Spec as VegaSpec, None } from 'vega'; import { desc, op, from, rolling, table, not } from 'arquero'; import { OrderKeys, ExprList } from 'arquero/dist/types/table/transformable'; -import { IdValuePair, ServerColumnAttribute } from '../../data/Attribute'; +import { ServerColumnAttribute } from '../../data/Attribute'; import { DATA_LABEL } from './constants'; import { getCohortLabel, getCohortLabels } from '../../Cohort'; import { SingleAttributeVisualization, AVegaVisualization } from './AVegaVisualization'; import { ICohort } from '../../app/interfaces'; import { confidenceToggleGroup, confidenceNone } from './config/ConfidenceConfig'; import confidenceIcon from '../../assets/icons/confidence.svg'; +import { IdValuePair } from '../../data/IAttribute'; export class KaplanMeierPlot extends SingleAttributeVisualization { static readonly NAME = 'Kaplan-Meier Plot'; @@ -24,7 +24,7 @@ export class KaplanMeierPlot extends SingleAttributeVisualization { protected readonly type = 'quantitative'; - constructor(vegaLiteOptions: Object = {}) { + constructor(vegaLiteOptions: object = {}) { super(vegaLiteOptions); this.config = [ @@ -56,6 +56,8 @@ export class KaplanMeierPlot extends SingleAttributeVisualization { } const dataPromises = this.cohorts.map((cht) => { + // TODO: fix me + // eslint-disable-next-line no-async-promise-executor const promise = new Promise(async (resolve, reject) => { const chtDataPromises = attributes.map((attr) => attr.getData(cht.dbId)); try { diff --git a/src/Taskview/visualizations/MultiAttributeVisualization.ts b/src/Taskview/visualizations/MultiAttributeVisualization.ts index c744798..4277ef9 100644 --- a/src/Taskview/visualizations/MultiAttributeVisualization.ts +++ b/src/Taskview/visualizations/MultiAttributeVisualization.ts @@ -1,11 +1,12 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ import * as aq from 'arquero'; import { format, select } from 'd3v7'; import tippy from 'tippy.js'; import vegaEmbed from 'vega-embed'; import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; -import { Cohort, getCohortLabel } from '../../Cohort'; +import { getCohortLabel } from '../../Cohort'; import { ICohort } from '../../app/interfaces'; -import { IAttribute, IdValuePair } from '../../data/Attribute'; +import { IAttribute, IdValuePair } from '../../data'; import { log, selectLast } from '../../util'; import { AVegaVisualization } from './AVegaVisualization'; import { DATA_LABEL } from './constants'; @@ -36,6 +37,8 @@ export abstract class MultiAttributeVisualization extends AVegaVisualization { this.attributes = attributes; // Create an array, with on entry per cohort, which contains an array with one entry per attribute, i.e. for 2 cohorts and 2 attributes (A1,A2) we get [[A1, A2], [A1, A2]] const dataPromises = cohorts.map((cht, chtIndex) => { + // TODO: fix me + // eslint-disable-next-line no-async-promise-executor const promise = new Promise(async (resolve, reject) => { const chtDataPromises = this.attributes.map((attr) => attr.getData(cht.dbId)); try { @@ -339,10 +342,10 @@ export abstract class MultiAttributeVisualization extends AVegaVisualization { const scale = this.vegaView.scale(axis); // if one or both ranges are set, replace with values - if (range[0] !== undefined && !isNaN(range[0])) { + if (range[0] !== undefined && !Number.isNaN(range[0])) { scaledRange[0] = scale(range[0]); // get min value from input } - if (range[1] !== undefined && !isNaN(range[1])) { + if (range[1] !== undefined && !Number.isNaN(range[1])) { scaledRange[1] = scale(range[1]); // get max value from input if (range[0] === range[1]) { scaledRange[1] = scale(range[1]) + 10 ** -10; // the 10^(-10) are independent of the attribute domain (i.e. values of 0 to 1 or in millions) because we add it after scaling (its a fraction of a pixel) @@ -363,11 +366,11 @@ export abstract class MultiAttributeVisualization extends AVegaVisualization { parseFloat((select(this.controls).select(`input.maximum[data-axis="${axis}"]`).node() as HTMLInputElement).value), ]; - if (range.some((rangeNum) => rangeNum === undefined || isNaN(rangeNum))) { + if (range.some((rangeNum) => rangeNum === undefined || Number.isNaN(rangeNum))) { const domain = this.vegaView.scale(axis).domain(); for (const i in range) { - if (range[i] === undefined || isNaN(range[i])) { + if (range[i] === undefined || Number.isNaN(range[i])) { range[i] = domain[i]; } } diff --git a/src/Taskview/visualizations/Scatterplot.ts b/src/Taskview/visualizations/Scatterplot.ts index cd30fc2..be07334 100644 --- a/src/Taskview/visualizations/Scatterplot.ts +++ b/src/Taskview/visualizations/Scatterplot.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable global-require */ import * as Comlink from 'comlink'; import { select } from 'd3v7'; import { Spec as VegaSpec } from 'vega'; @@ -5,8 +7,8 @@ import { TopLevelSpec as VegaLiteSpec } from 'vega-lite'; import { TopLevel, LayerSpec } from 'vega-lite/build/src/spec'; import { Field } from 'vega-lite/build/src/channeldef'; import { ICohort } from '../../app/interfaces'; -import { AttributeType, IAttribute, IdValuePair, ServerColumnAttribute } from '../../data/Attribute'; -import { NumRangeOperators } from '../../base/rest'; +import { AttributeType, IAttribute, IdValuePair, ServerColumnAttribute } from '../../data'; +import { NumRangeOperators } from '../../base'; import { IFilterDesc, inRange } from '../../util'; import { FilterEvent } from '../../base/events'; import { DATA_LABEL } from './constants'; @@ -17,7 +19,7 @@ export class Scatterplot extends MultiAttributeVisualization { protected checkAttributeType = false; - constructor(vegaLiteOptions: Object = {}) { + constructor(vegaLiteOptions: object = {}) { super(vegaLiteOptions); } @@ -1042,7 +1044,7 @@ export class TsneScatterplot extends Scatterplot { originalAttributes: IAttribute[]; - constructor(vegaLiteOptions: Object = {}) { + constructor(vegaLiteOptions: object = {}) { super(vegaLiteOptions); this.checkAttributeType = false; } @@ -1066,8 +1068,8 @@ export class TsneScatterplot extends Scatterplot { this.addProgressBar(); const oneHotWorker = new (require('worker-loader?name=OneHotEncoder.js!./dimreduce/OneHotEncoder.worker'))(); - const oneHotClass = Comlink.wrap(oneHotWorker) as any; - const oneHot = await new oneHotClass(); + const OneHotClass = Comlink.wrap(oneHotWorker) as any; + const oneHot = await new OneHotClass(); // Note: numerical attributes will be normalized const oneHotData = await oneHot.encode(data, this.attributes); oneHot[Comlink.releaseProxy](); @@ -1080,6 +1082,7 @@ export class TsneScatterplot extends Scatterplot { const tsneWorker = new (require('worker-loader?name=tsne.worker.js!./dimreduce/tsne.worker'))(); this.tsneClass = Comlink.wrap(tsneWorker) as any; + // eslint-disable-next-line new-cap this.tsne = await new this.tsneClass(opt); this.tsne.initDataRaw(oneHotData); diff --git a/src/Taskview/visualizations/dimreduce/OneHotEncoder.ts b/src/Taskview/visualizations/dimreduce/OneHotEncoder.ts index 92f38d3..3d85b3c 100644 --- a/src/Taskview/visualizations/dimreduce/OneHotEncoder.ts +++ b/src/Taskview/visualizations/dimreduce/OneHotEncoder.ts @@ -12,7 +12,7 @@ export class OneHotEncoder { encode(data: Array, attributes: Array) { const hotData = []; - if (data?.length ?? 0 > 0) { + if (data?.length > 0) { // gather categories of the categorical attributes for (const attr of attributes) { for (const item of data) { diff --git a/src/app/CoralApp.ts b/src/app/CoralApp.ts index a62b7cb..96f1e37 100644 --- a/src/app/CoralApp.ts +++ b/src/app/CoralApp.ts @@ -1,11 +1,11 @@ import { select, Selection } from 'd3v7'; import SplitGrid from 'split-grid'; +import { IServerColumn } from 'visyn_core'; import { AppContext, CLUEGraphManager, IDatabaseViewDesc, IObjectRef, - IServerColumn, ITDPOptions, NotificationHandler, ObjectRefUtils, @@ -15,8 +15,8 @@ import { import { cellline, tissue } from 'tdp_publicdb'; import { Instance as TippyInstance } from 'tippy.js'; import { createCohort, createCohortFromDB } from '../Cohort'; -import { IElementProvJSON, IElementProvJSONCohort, ITaskParams, ICohort, IDatasetDesc, IPanelDesc } from './interfaces'; -import { cohortOverview, createCohortOverview, destroyOld, loadViewDescription, taskview } from '../cohortview'; +import type { IElementProvJSON, IElementProvJSONCohort, ITaskParams, ICohort, IDatasetDesc, IPanelDesc } from './interfaces'; +import { createCohortOverview, destroyOld, loadViewDescription } from '../cohortview'; import { PanelScoreAttribute } from '../data/Attribute'; import { OnboardingManager } from '../OnboardingManager'; import { CohortOverview } from '../Overview'; @@ -24,13 +24,14 @@ import { setDatasetAction } from '../Provenance/General'; import { getDBCohortData } from '../base/rest'; import { Task } from '../Tasks'; import Taskview from '../Taskview/Taskview'; -import deleteModal from './templates/DeleteModal.html'; -import welcomeHtml from './templates/Welcome.html'; +import deleteModal from '../templates/DeleteModal.html'; +import welcomeHtml from '../templates/Welcome.html'; import { getAnimatedLoadingText, handleDataLoadError, log } from '../util'; import { CohortSelectionEvent, ConfirmTaskEvent, CONFIRM_TASK_EVENT_TYPE, PreviewConfirmEvent } from '../base/events'; import { idCellline, idCovid19, idStudent, idTissue, IEntitySourceConfig } from '../config/entities'; import { niceName } from '../utils/labels'; import { CohortSelectionListener } from './CoralSelectionListener'; +import { CohortContext } from '../CohortContext'; /** * The Cohort app that does the acutal stuff. @@ -109,8 +110,8 @@ export class CoralApp { // confirm the current preview as the result of the task this.$node.node().dispatchEvent(new PreviewConfirmEvent(taskParams, taskAttributes)); // get the new added task (the ones confirmed from the preview) - const tasks: Task[] = cohortOverview.getLastAddedTasks(); - taskview.clearOutput(); // clear taskview output + const tasks: Task[] = CohortContext.cohortOverview.getLastAddedTasks(); + CohortContext.taskview.clearOutput(); // clear taskview output // set output as new input let replace = true; @@ -245,6 +246,8 @@ export class CoralApp { dropdown.append('li').attr('role', 'separator').classed('dropdown-divider', true); dropdown.append('li').classed('dropdown-header', true).text('Predefined Sets'); + // TODO: check if this can be removed in the future + // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this; dropdown .selectAll('li.data-panel') @@ -259,8 +262,8 @@ export class CoralApp { .on('click', async function (event, d) { // click subset // don't toggle data by checkging what is selected in dropdown - const dataSourcesAndPanels = select(this.parentNode.parentNode).datum() as { source: IEntitySourceConfig; panels: IPanelDesc[] }; // a -> parent = li -> parent = dropdown = ul - const newDataset = { source: dataSourcesAndPanels.source, panel: d, rootCohort: null, chtOverviewElements: null }; + const currDataSourcesAndPanels = select(this.parentNode.parentNode).datum() as { source: IEntitySourceConfig; panels: IPanelDesc[] }; // a -> parent = li -> parent = dropdown = ul + const newDataset = { source: currDataSourcesAndPanels.source, panel: d, rootCohort: null, chtOverviewElements: null }; that.handleDatasetClick.bind(that)(newDataset); }); diff --git a/src/app/CoralSelectionListener.ts b/src/app/CoralSelectionListener.ts index 2340402..80dce33 100644 --- a/src/app/CoralSelectionListener.ts +++ b/src/app/CoralSelectionListener.ts @@ -1,8 +1,8 @@ -import { CoralApp } from './CoralApp'; -import Taskview from '../Taskview/Taskview'; +import type { CoralApp } from './CoralApp'; +import type Taskview from '../Taskview/Taskview'; import { log, removeFromArray } from '../util'; import { CohortSelectionEvent, COHORT_SELECTION_EVENT_TYPE } from '../base/events'; -import type { ICohort, IInputCohort } from '.'; +import type { ICohort, IInputCohort } from './interfaces'; export class CohortSelectionListener { private static instance: CohortSelectionListener; diff --git a/src/app/interfaces.ts b/src/app/interfaces.ts index fd672f9..40e9bec 100644 --- a/src/app/interfaces.ts +++ b/src/app/interfaces.ts @@ -1,6 +1,7 @@ -import type { IAllFilters, IDType, IDTypeLike, IRow, IServerColumn } from 'tdp_core'; -import type { IAttribute, IAttributeJSON } from '../data/Attribute'; -import type { +import { IDType, IDTypeLike, IRow, IServerColumn } from 'visyn_core'; +import { IAllFilters } from 'tdp_core'; +import type { IEntitySourceConfig } from '../config/entities'; +import { ICohortDepletionScoreFilterParams, ICohortEqualsFilterParams, ICohortGeneEqualsFilterParams, @@ -9,8 +10,8 @@ import type { ICohortPanelAnnotationFilterParams, IEqualsList, INumRange, -} from '../base/rest'; -import type { IEntitySourceConfig } from '../config/entities'; +} from '../base/interfaces'; +import type { IAttribute, IAttributeJSON } from '../data/IAttribute'; export enum ECloneFilterTypes { none, diff --git a/src/base/events.ts b/src/base/events.ts index 444d8f6..5849aa3 100644 --- a/src/base/events.ts +++ b/src/base/events.ts @@ -1,7 +1,7 @@ -import { ICohort, ITaskParams } from '../app/interfaces'; -import { IAttribute } from '../data/Attribute'; -import { Task } from '../Tasks'; -import { AColumn } from '../Taskview/columns/AColumn'; +import type { ICohort, ITaskParams } from '../app/interfaces'; +import type { IAttribute } from '../data/IAttribute'; +import type { Task } from '../Tasks'; +import type { AColumn } from '../Taskview/columns/AColumn'; import { IFilterDesc, SortType } from '../util'; export const COHORT_REMOVE_EVENT_TYPE = 'cht:remove'; diff --git a/src/base/index.ts b/src/base/index.ts new file mode 100644 index 0000000..05123d5 --- /dev/null +++ b/src/base/index.ts @@ -0,0 +1,2 @@ +export * from './rest'; +export * from './interfaces'; diff --git a/src/base/interfaces.ts b/src/base/interfaces.ts new file mode 100644 index 0000000..013b065 --- /dev/null +++ b/src/base/interfaces.ts @@ -0,0 +1,236 @@ +import type { IParams } from 'tdp_core'; + +export interface ICohortDBParams extends IParams { + name: string; + isInitial: number; // 0 = false, 1 = true + previous: number; + database: string; + schema: string; + table: string; +} + +export interface ICohortEqualsFilterParams extends IParams, IEqualsList { + cohortId: number; + attribute: string; + numeric: 'true' | 'false'; +} + +export interface ICohortDBWithEqualsFilterParams extends ICohortEqualsFilterParams { + name: string; +} + +export interface ICohortNumFilterParams { + cohortId: number; + attribute: string; + ranges: Array; +} + +export interface ICohortGeneNumFilterParams extends ICohortNumFilterParams { + table: string; + ensg: string; +} + +export interface ICohortGeneEqualsFilterParams extends ICohortEqualsFilterParams { + table: string; + ensg: string; +} + +export interface ICohortDBWithNumFilterParams extends ICohortNumFilterParams { + name: string; +} + +export const valueListDelimiter = '⸱'; + +export interface IEqualsList { + values: Array | Array; +} + +/** + * Type Guard 💂‍♂️ helper because interfaces can't be checked if instanceof + * @param filter + */ +export function isEqualsList(filter: INumRange | IEqualsList): filter is IEqualsList { + return (filter as IEqualsList).values && Array.isArray((filter as IEqualsList).values); // checking the type of the array requires to check every item but this should be sufficently safe +} + +export enum NumRangeOperators { + lt = 'lt', + lte = 'lte', + gt = 'gt', + gte = 'gte', +} +export interface INumRange { + operatorOne: NumRangeOperators; + valueOne: number | 'null'; + operatorTwo?: NumRangeOperators; + valueTwo?: number | 'null'; +} + +/** + * Type Guard 💂‍♂️ helper because interfaces can't be checked if instanceof + * @param filter + */ +export function isNumRangeFilter(filter: INumRange | IEqualsList): filter is INumRange { + return (filter as INumRange).operatorOne !== undefined && (filter as INumRange).valueOne !== undefined; // check the non-optional properties +} +export interface ICohortDBDataParams extends IParams { + cohortId: number; + attribute?: string; +} + +export interface ICohortDBSizeParams extends IParams { + cohortId: number; +} + +export interface ICohortDBCohortDataParams extends IParams { + cohortIds: number[]; +} + +export interface ICohortDBUpdateName extends IParams { + cohortId: number; + name: string; +} +export interface ICohortDBGeneScoreParams extends IParams { + cohortId: number; + table: string; + attribute: string; + ensg: string; +} + +export interface ICohortDBDepletionScoreParams extends ICohortDBGeneScoreParams { + depletionscreen: string; +} + +export interface ICohortDBPanelAnnotationParams extends IParams { + cohortId: number; + panel: string; +} + +export interface ICohortDepletionScoreFilterParams { + cohortId: number; + table: string; + attribute: string; + ensg: string; + depletionscreen: string; + ranges: Array; +} + +export interface ICohortDBWithGeneNumFilterParams extends ICohortDBWithNumFilterParams { + table: string; + ensg: string; +} + +export interface ICohortDBWithGeneEqualsFilterParams extends ICohortDBWithEqualsFilterParams { + table: string; + ensg: string; +} + +export interface ICohortDBWithDepletionScoreFilterParams extends ICohortDBWithGeneNumFilterParams { + depletionscreen: string; +} + +export interface ICohortDBWithPanelAnnotationFilterParams extends IParams { + cohortId: number; + name: string; + panel: string; + values: Array; +} + +export interface ICohortPanelAnnotationFilterParams extends IParams { + cohortId: number; + panel: string; + values: Array; +} + +export interface ICohortDBWithTreatmentFilterParams extends IParams { + cohortId: number; + name: string; + baseAgent: boolean; + agent?: Array; + regimen?: number; +} + +export enum CohortRoutes { + create = 'create', + createUseEqulasFilter = 'createUseEqulasFilter', + dataUseEqulasFilter = 'dataUseEqulasFilter', + sizeUseEqulasFilter = 'sizeUseEqulasFilter', + createUseNumFilter = 'createUseNumFilter', + dataUseNumFilter = 'dataUseNumFilter', + sizeUseNumFilter = 'sizeUseNumFilter', + createUseGeneNumFilter = 'createUseGeneNumFilter', + dataUseGeneNumFilter = 'dataUseGeneNumFilter', + sizeUseGeneNumFilter = 'sizeUseGeneNumFilter', + createUseGeneEqualsFilter = 'createUseGeneEqualsFilter', + sizeUseGeneEqualsFilter = 'sizeUseGeneEqualsFilter', + dataUseGeneEqualsFilter = 'dataUseGeneEqualsFilter', + cohortData = 'cohortData', + size = 'size', + getDBCohorts = 'getDBCohorts', + updateCohortName = 'updateCohortName', + geneScore = 'geneScore', + celllineDepletionScore = 'celllineDepletionScore', + createUseDepletionScoreFilter = 'createUseDepletionScoreFilter', + dataUseDepletionScoreFilter = 'dataUseDepletionScoreFilter', + sizeUseDepletionScoreFilter = 'sizeUseDepletionScoreFilter', + panelAnnotation = 'panelAnnotation', + createUsePanelAnnotationFilter = 'createUsePanelAnnotationFilter', + dataUsePanelAnnotationFilter = 'dataUsePanelAnnotationFilter', + sizeUsePanelAnnotationFilter = 'sizeUsePanelAnnotationFilter', + hist = 'hist', + createUseTreatmentFilter = 'createUseTreatmentFilter', +} + +export interface IDataBaseToDisplay { + value: string | number; + name: string; +} + +// copy from tdp_publicdb/src/constants.ts +// for 'Copy Number Class' +// table: copynumber, attribtue: copynumberclass +export const mapCopyNumberCat: IDataBaseToDisplay[] = [ + { value: 2, name: 'Amplification' }, + { value: -2, name: 'Deep Deletion' }, + // {value: -1, name: 'Heterozygous deletion'}, + { value: 0, name: 'NORMAL' }, + // {value: 1, name: 'Low level amplification'}, + // {value: 2, name: 'High level amplification'}, + { value: 'null', name: 'Unknown' }, + { value: '!null', name: '!null' }, +]; + +// for 'AA Mutated' and 'DNA Mutated' +// table: mutation, attribute: aa_mutated / dna_mutated +export const mapMutationCat: IDataBaseToDisplay[] = [ + { value: 'true', name: 'Mutated' }, + { value: 'false', name: 'Non Mutated' }, + { value: 'null', name: 'Unknown' }, + { value: '!null', name: '!null' }, +]; + +/** + * Interface for the cohort tuple (row) in the DB + */ +export interface ICohortRow { + id: number; + name: string; + entity_database: string; + entity_schema: string; + entity_table: string; + is_initial: number; +} + +export enum DataMappingDirection { + DB2Display, + Display2DB, +} + +export enum HistRouteType { + dataCat = 'dataCat', + dataNum = 'dataNum', + geneScoreCat = 'geneScoreCat', + geneScoreNum = 'geneScoreNum', + depletionScore = 'depletionScore', + panelAnnotation = 'panelAnnotation', +} diff --git a/src/base/rest.ts b/src/base/rest.ts index 200c37a..b628675 100644 --- a/src/base/rest.ts +++ b/src/base/rest.ts @@ -1,231 +1,38 @@ -import { Ajax, AppContext, IParams, IRow } from 'tdp_core'; +import { Ajax, AppContext, IRow } from 'visyn_core'; +import { IParams } from 'tdp_core'; import { deepCopy, log } from '../util'; - -export interface ICohortDBParams extends IParams { - name: string; - isInitial: number; // 0 = false, 1 = true - previous: number; - database: string; - schema: string; - table: string; -} - -export interface ICohortEqualsFilterParams extends IParams, IEqualsList { - cohortId: number; - attribute: string; - numeric: 'true' | 'false'; -} - -export interface ICohortDBWithEqualsFilterParams extends ICohortEqualsFilterParams { - name: string; -} - -export interface ICohortNumFilterParams { - cohortId: number; - attribute: string; - ranges: Array; -} - -export interface ICohortGeneNumFilterParams extends ICohortNumFilterParams { - table: string; - ensg: string; -} - -export interface ICohortGeneEqualsFilterParams extends ICohortEqualsFilterParams { - table: string; - ensg: string; -} - -export interface ICohortDBWithNumFilterParams extends ICohortNumFilterParams { - name: string; -} - -export const valueListDelimiter = '⸱'; - -export interface IEqualsList { - values: Array | Array; -} - -/** - * Type Guard 💂‍♂️ helper because interfaces can't be checked if instanceof - * @param filter - */ -export function isEqualsList(filter: INumRange | IEqualsList): filter is IEqualsList { - return (filter as IEqualsList).values && Array.isArray((filter as IEqualsList).values); // checking the type of the array requires to check every item but this should be sufficently safe -} - -export enum NumRangeOperators { - lt = 'lt', - lte = 'lte', - gt = 'gt', - gte = 'gte', -} -export interface INumRange { - operatorOne: NumRangeOperators; - valueOne: number | 'null'; - operatorTwo?: NumRangeOperators; - valueTwo?: number | 'null'; -} - -/** - * Type Guard 💂‍♂️ helper because interfaces can't be checked if instanceof - * @param filter - */ -export function isNumRangeFilter(filter: INumRange | IEqualsList): filter is INumRange { - return (filter as INumRange).operatorOne !== undefined && (filter as INumRange).valueOne !== undefined; // check the non-optional properties -} -export interface ICohortDBDataParams extends IParams { - cohortId: number; - attribute?: string; -} - -export interface ICohortDBSizeParams extends IParams { - cohortId: number; -} - -export interface ICohortDBCohortDataParams extends IParams { - cohortIds: number[]; -} - -export interface ICohortDBUpdateName extends IParams { - cohortId: number; - name: string; -} -export interface ICohortDBGeneScoreParams extends IParams { - cohortId: number; - table: string; - attribute: string; - ensg: string; -} - -export interface ICohortDBDepletionScoreParams extends ICohortDBGeneScoreParams { - depletionscreen: string; -} - -export interface ICohortDBPanelAnnotationParams extends IParams { - cohortId: number; - panel: string; -} - -export interface ICohortDepletionScoreFilterParams { - cohortId: number; - table: string; - attribute: string; - ensg: string; - depletionscreen: string; - ranges: Array; -} - -export interface ICohortDBWithGeneNumFilterParams extends ICohortDBWithNumFilterParams { - table: string; - ensg: string; -} - -export interface ICohortDBWithGeneEqualsFilterParams extends ICohortDBWithEqualsFilterParams { - table: string; - ensg: string; -} - -export interface ICohortDBWithDepletionScoreFilterParams extends ICohortDBWithGeneNumFilterParams { - depletionscreen: string; -} - -export interface ICohortDBWithPanelAnnotationFilterParams extends IParams { - cohortId: number; - name: string; - panel: string; - values: Array; -} - -export interface ICohortPanelAnnotationFilterParams extends IParams { - cohortId: number; - panel: string; - values: Array; -} - -export interface ICohortDBWithTreatmentFilterParams extends IParams { - cohortId: number; - name: string; - baseAgent: boolean; - agent?: Array; - regimen?: number; -} - -enum CohortRoutes { - create = 'create', - createUseEqulasFilter = 'createUseEqulasFilter', - dataUseEqulasFilter = 'dataUseEqulasFilter', - sizeUseEqulasFilter = 'sizeUseEqulasFilter', - createUseNumFilter = 'createUseNumFilter', - dataUseNumFilter = 'dataUseNumFilter', - sizeUseNumFilter = 'sizeUseNumFilter', - createUseGeneNumFilter = 'createUseGeneNumFilter', - dataUseGeneNumFilter = 'dataUseGeneNumFilter', - sizeUseGeneNumFilter = 'sizeUseGeneNumFilter', - createUseGeneEqualsFilter = 'createUseGeneEqualsFilter', - sizeUseGeneEqualsFilter = 'sizeUseGeneEqualsFilter', - dataUseGeneEqualsFilter = 'dataUseGeneEqualsFilter', - cohortData = 'cohortData', - size = 'size', - getDBCohorts = 'getDBCohorts', - updateCohortName = 'updateCohortName', - geneScore = 'geneScore', - celllineDepletionScore = 'celllineDepletionScore', - createUseDepletionScoreFilter = 'createUseDepletionScoreFilter', - dataUseDepletionScoreFilter = 'dataUseDepletionScoreFilter', - sizeUseDepletionScoreFilter = 'sizeUseDepletionScoreFilter', - panelAnnotation = 'panelAnnotation', - createUsePanelAnnotationFilter = 'createUsePanelAnnotationFilter', - dataUsePanelAnnotationFilter = 'dataUsePanelAnnotationFilter', - sizeUsePanelAnnotationFilter = 'sizeUsePanelAnnotationFilter', - hist = 'hist', - createUseTreatmentFilter = 'createUseTreatmentFilter', -} - -interface IDataBaseToDisplay { - value: string | number; - name: string; -} - -// copy from tdp_publicdb/src/constants.ts -// for 'Copy Number Class' -// table: copynumber, attribtue: copynumberclass -const mapCopyNumberCat: IDataBaseToDisplay[] = [ - { value: 2, name: 'Amplification' }, - { value: -2, name: 'Deep Deletion' }, - // {value: -1, name: 'Heterozygous deletion'}, - { value: 0, name: 'NORMAL' }, - // {value: 1, name: 'Low level amplification'}, - // {value: 2, name: 'High level amplification'}, - { value: 'null', name: 'Unknown' }, - { value: '!null', name: '!null' }, -]; - -// for 'AA Mutated' and 'DNA Mutated' -// table: mutation, attribute: aa_mutated / dna_mutated -const mapMutationCat: IDataBaseToDisplay[] = [ - { value: 'true', name: 'Mutated' }, - { value: 'false', name: 'Non Mutated' }, - { value: 'null', name: 'Unknown' }, - { value: '!null', name: '!null' }, -]; - -/** - * Interface for the cohort tuple (row) in the DB - */ -export interface ICohortRow { - id: number; - name: string; - entity_database: string; - entity_schema: string; - entity_table: string; - is_initial: number; -} - -enum DataMappingDirection { - DB2Display, - Display2DB, -} +import { + CohortRoutes, + DataMappingDirection, + HistRouteType, + ICohortDBCohortDataParams, + ICohortDBDataParams, + ICohortDBDepletionScoreParams, + ICohortDBGeneScoreParams, + ICohortDBPanelAnnotationParams, + ICohortDBParams, + ICohortDBSizeParams, + ICohortDBUpdateName, + ICohortDBWithDepletionScoreFilterParams, + ICohortDBWithEqualsFilterParams, + ICohortDBWithGeneEqualsFilterParams, + ICohortDBWithGeneNumFilterParams, + ICohortDBWithNumFilterParams, + ICohortDBWithPanelAnnotationFilterParams, + ICohortDBWithTreatmentFilterParams, + ICohortDepletionScoreFilterParams, + ICohortEqualsFilterParams, + ICohortGeneEqualsFilterParams, + ICohortGeneNumFilterParams, + ICohortNumFilterParams, + ICohortPanelAnnotationFilterParams, + ICohortRow, + IDataBaseToDisplay, + INumRange, + mapCopyNumberCat, + mapMutationCat, + valueListDelimiter, +} from './interfaces'; // maps the database value to a display name function mapDataBaseValueToDisplayName( @@ -567,15 +374,6 @@ export function getCohortPanelAnnotation( return null; } -export enum HistRouteType { - dataCat = 'dataCat', - dataNum = 'dataNum', - geneScoreCat = 'geneScoreCat', - geneScoreNum = 'geneScoreNum', - depletionScore = 'depletionScore', - panelAnnotation = 'panelAnnotation', -} - export interface ICohortDBHistDataParms extends IParams { cohortId: number; attribute: string; diff --git a/src/cohortview.ts b/src/cohortview.ts index adc2a0c..b10594e 100644 --- a/src/cohortview.ts +++ b/src/cohortview.ts @@ -1,6 +1,6 @@ import { IObjectRef, ProvenanceGraph, IDatabaseViewDesc, RestBaseUtils } from 'tdp_core'; import { CohortSelectionListener } from './app/CoralSelectionListener'; -import { CoralApp } from './app/CoralApp'; +import type { CoralApp } from './app/CoralApp'; import { OnboardingManager } from './OnboardingManager'; import { CohortOverview } from './Overview/CohortOverview'; import { RectangleLayout } from './Overview/OverviewLayout'; @@ -8,11 +8,31 @@ import Taskview from './Taskview/Taskview'; import { handleDataLoadError, log } from './util'; import { IEntitySourceConfig } from './config/entities'; import { ICohort } from './app/interfaces'; +import { CohortContext } from './CohortContext'; -export let cohortOverview: CohortOverview; -export let taskview: Taskview; +export function destroyOld() { + if (CohortContext.taskview) { + CohortContext.taskview.destroy(); + CohortContext.taskview = null; + } + if (CohortContext.cohortOverview) { + CohortContext.cohortOverview.destroy(); + CohortContext.cohortOverview = null; + } + CohortContext.referenceCohort = null; +} -let referenceCohort: ICohort; +export async function loadViewDescription(database: string, view: string) { + log.debug('getTDPDesc for: db:', database, ' |view: ', view); + try { + const descr: IDatabaseViewDesc = await RestBaseUtils.getTDPDesc(database, view); + log.debug('descr= ', descr); + return descr; + } catch (e) { + handleDataLoadError(e); + return null; + } +} export async function createCohortOverview( graph: ProvenanceGraph, @@ -28,60 +48,21 @@ export async function createCohortOverview( destroyOld(); // set reference/root cohort - referenceCohort = rootCohort; // save reference cohort + CohortContext.referenceCohort = rootCohort; // save reference cohort log.debug('cohortview - root: ', rootCohort); // create Overview - cohortOverview = new CohortOverview(container, graph, ref, new RectangleLayout(130, 50, 50, 50), rootCohort, viewDescription); + CohortContext.cohortOverview = new CohortOverview(container, graph, ref, new RectangleLayout(130, 50, 50, 50), rootCohort, viewDescription); // create Taskview - taskview = new Taskview(detailView, rootCohort); + CohortContext.taskview = new Taskview(detailView, rootCohort); - CohortSelectionListener.get().taskview = taskview; + CohortSelectionListener.get().taskview = CohortContext.taskview; // draw overview in container element - updateOverview(cohortOverview); + CohortContext.cohortOverview.generateOverview(); OnboardingManager.addTip('rootCohort', rootCohort.representation.getRepresentation()); return { - cohortOV: cohortOverview, - taskV: taskview, + cohortOV: CohortContext.cohortOverview, + taskV: CohortContext.taskview, }; } - -export function getRootCohort(): ICohort { - return referenceCohort; -} - -export function destroyOld() { - if (taskview) { - taskview.destroy(); - taskview = null; - } - if (cohortOverview) { - cohortOverview.destroy(); - cohortOverview = null; - } - referenceCohort = null; -} - -export function getCohortOverview(): CohortOverview { - return cohortOverview; -} - -export function getTaskview(): Taskview { - return taskview; -} - -export function updateOverview(overview: CohortOverview) { - overview.generateOverview(); -} - -export async function loadViewDescription(database: string, view: string) { - log.debug('getTDPDesc for: db:', database, ' |view: ', view); - try { - const descr: IDatabaseViewDesc = await RestBaseUtils.getTDPDesc(database, view); - log.debug('descr= ', descr); - return descr; - } catch (e) { - handleDataLoadError(e); - } -} diff --git a/src/common/config.ts b/src/common/config.ts index f7d1581..84b0447 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -3,7 +3,8 @@ */ import { Categories } from 'tdp_publicdb'; -import { IServerColumn, ColumnDescUtils, IAdditionalColumnDesc } from 'tdp_core'; +import { IServerColumn } from 'visyn_core'; +import { ColumnDescUtils, IAdditionalColumnDesc } from 'tdp_core'; /** * maximal number of rows in which just the subset if fetched instead of all @@ -172,6 +173,10 @@ export const tissue: IDataSourceConfig = { }, }; +function toLineUpCategories(arr: { name: string; value: any; color: string }[]) { + return arr.map((a) => ({ label: a.name, name: String(a.value), color: a.color })); +} + function toChromosomes(categories: string[]) { const order = new Map(); for (let i = 1; i <= 22; ++i) { @@ -257,6 +262,8 @@ export function chooseDataSource(desc: any): IDataSourceConfig { return tissue; case gene.name: return gene; + default: + return null; } } @@ -495,19 +502,6 @@ export const drug: IDataSourceConfig = { export const dataTypes: IDataTypeConfig[] = [expression, copyNumber, mutation]; -function toLineUpCategories(arr: { name: string; value: any; color: string }[]) { - return arr.map((a) => ({ label: a.name, name: String(a.value), color: a.color })); -} - -/** - * splits strings in the form of "DATA_TYPE-DATA_SUBTYPE" and returns the corresponding DATA_TYPE and DATA_SUBTYPE objects - */ -export function splitTypes(toSplit: string): { dataType: IDataTypeConfig; dataSubType: IDataSubtypeConfig } { - console.assert(toSplit.includes('-'), 'The splitTypes method requires the string to contain a dash ("-")'); - const [type, subtype] = toSplit.split('-'); - return resolveDataTypes(type, subtype); -} - export function resolveDataTypes(dataTypeId: string, dataSubTypeId: string) { let dataType: IDataTypeConfig; @@ -527,10 +521,21 @@ export function resolveDataTypes(dataTypeId: string, dataSubTypeId: string) { case drugScreen.id: dataType = drugScreen; break; + default: + dataType = null; } - const dataSubType = dataType.dataSubtypes.find((element) => element.id === dataSubTypeId); + const dataSubType = dataType?.dataSubtypes?.find((element) => element.id === dataSubTypeId); return { dataType, dataSubType, }; } + +/** + * splits strings in the form of "DATA_TYPE-DATA_SUBTYPE" and returns the corresponding DATA_TYPE and DATA_SUBTYPE objects + */ +export function splitTypes(toSplit: string): { dataType: IDataTypeConfig; dataSubType: IDataSubtypeConfig } { + console.assert(toSplit.includes('-'), 'The splitTypes method requires the string to contain a dash ("-")'); + const [type, subtype] = toSplit.split('-'); + return resolveDataTypes(type, subtype); +} diff --git a/src/common/forms.ts b/src/common/forms.ts index 2261ffa..eb1564d 100644 --- a/src/common/forms.ts +++ b/src/common/forms.ts @@ -3,7 +3,8 @@ */ import { SpeciesUtils, FormSubtype } from 'tdp_publicdb'; -import { FormElementType, IFormElement, IFormSelectOption, ValueCache, RestStorageUtils, LineupUtils, RestBaseUtils, IServerColumn } from 'tdp_core'; +import { IServerColumn } from 'visyn_core'; +import { FormElementType, IFormElement, IFormSelectOption, ValueCache, RestStorageUtils, LineupUtils, RestBaseUtils } from 'tdp_core'; import { gene, IDataSourceConfig, tissue, cellline, dataSources, dataTypes, dataSubtypes, depletion, drugScreen } from './config'; import { GeneUtils } from './GeneUtils'; @@ -157,7 +158,7 @@ export const FORM_GENE_FILTER = { RestBaseUtils.getTDPData<{ text: string }>(gene.db, 'gene_unique_all', { column: 'biotype', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -246,7 +247,7 @@ function generateTissueSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'tumortype_adjacent', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -262,7 +263,7 @@ function generateTissueSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'vendorname', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -278,7 +279,7 @@ function generateTissueSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'race', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -294,7 +295,7 @@ function generateTissueSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'ethnicity', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -310,7 +311,7 @@ function generateTissueSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string | boolean }>(d.db, `${d.base}_unique_all`, { column: 'vital_status', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => (d.text === true ? 'true' : 'false'))), + }).then((r) => r.map((v) => (v.text === true ? 'true' : 'false'))), ), options: { placeholder: 'Start typing...', @@ -331,7 +332,7 @@ function generateCelllineSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'age_at_surgery', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -347,7 +348,7 @@ function generateCelllineSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'growth_type', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -363,7 +364,7 @@ function generateCelllineSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'histology_type', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -379,7 +380,7 @@ function generateCelllineSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'metastatic_site', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -395,7 +396,7 @@ function generateCelllineSpecificFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'morphology', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -432,7 +433,7 @@ function generateFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'tumortype', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -448,7 +449,7 @@ function generateFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'organ', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -464,7 +465,7 @@ function generateFilter(d: IDataSourceConfig) { RestBaseUtils.getTDPData<{ text: string }>(d.db, `${d.base}_unique_all`, { column: 'gender', species: SpeciesUtils.getSelectedSpecies(), - }).then((r) => r.map((d) => d.text)), + }).then((r) => r.map((v) => v.text)), ), options: { placeholder: 'Start typing...', @@ -561,6 +562,24 @@ export const FORM_TISSUE_OR_CELLLINE_FILTER = { }, }; +function addEmptyOption(options: IFormSelectOption[]) { + return [{ name: 'None', value: '', data: null }].concat(options); +} + +function selectCategoricalColumn(ds: IDataSourceConfig) { + const dummy: IServerColumn = { + type: 'string', + column: '', + label: '', + categories: [], + min: 0, + max: 1, + }; + const cats = ds.columns(() => dummy).filter((d) => d.type === 'categorical'); + + return cats.map((c) => ({ name: c.label, value: (c).column, data: (c).column })); +} + export const FORM_COLOR_CODING = { type: FormElementType.SELECT, label: 'Color Coding', @@ -582,24 +601,6 @@ export const FORM_COLOR_CODING = { useSession: false, }; -function selectCategoricalColumn(ds: IDataSourceConfig) { - const dummy: IServerColumn = { - type: 'string', - column: '', - label: '', - categories: [], - min: 0, - max: 1, - }; - const cats = ds.columns(() => dummy).filter((d) => d.type === 'categorical'); - - return cats.map((c) => ({ name: c.label, value: (c).column, data: (c).column })); -} - -function addEmptyOption(options: IFormSelectOption[]) { - return [{ name: 'None', value: '', data: null }].concat(options); -} - export const FORM_DATA_HIERARCHICAL_SUBTYPE = { type: FormElementType.SELECT2_MULTIPLE, label: 'Data Type', diff --git a/src/data/Attribute.ts b/src/data/Attribute.ts index cdc6fe8..0e03791 100644 --- a/src/data/Attribute.ts +++ b/src/data/Attribute.ts @@ -1,15 +1,15 @@ -import { IAllFilters, IServerColumn } from 'tdp_core'; -import { IDataSubtypeConfig, IDataTypeConfig, resolveDataTypes } from 'tdp_publicdb'; -import { - Cohort, - createCohortWithDepletionScoreFilter, - createCohortWithEqualsFilter, - createCohortWithGeneEqualsFilter, - createCohortWithGeneNumFilter, - createCohortWithNumFilter, - createCohortWithPanelAnnotationFilter, -} from '../Cohort'; +import { IAllFilters } from 'tdp_core'; +import { IDataTypeConfig, IDataSubtypeConfig, resolveDataTypes } from 'tdp_publicdb'; +import { IServerColumn } from 'visyn_core'; import { ICohort } from '../app/interfaces'; +import { + HistRouteType, + ICohortDBDepletionScoreParams, + ICohortDBGeneScoreParams, + ICohortDBPanelAnnotationParams, + IEqualsList, + INumRange, +} from '../base/interfaces'; import { getCohortData, getCohortDepletionScore, @@ -17,108 +17,23 @@ import { getCohortHist, getCohortPanelAnnotation, getCohortSize, - HistRouteType, - ICohortDBDepletionScoreParams, - ICohortDBGeneScoreParams, ICohortDBHistDataParms, ICohortDBHistPanelParms, ICohortDBHistScoreDepletionParms, ICohortDBHistScoreParms, - ICohortDBPanelAnnotationParams, - IEqualsList, - INumRange, } from '../base/rest'; -import { IOption, IScoreOption, IServerColumnOption, ISpecialOption, OptionType } from '../Taskview/SearchBar'; -import { deepCopy, getSessionStorageItem, IAttributeFilter, log, setSessionStorageItem } from '../util'; +import { + createCohortWithDepletionScoreFilter, + createCohortWithEqualsFilter, + createCohortWithGeneEqualsFilter, + createCohortWithGeneNumFilter, + createCohortWithNumFilter, + createCohortWithPanelAnnotationFilter, +} from '../Cohort'; +import { deepCopy, getSessionStorageItem, IAttributeFilter, setSessionStorageItem } from '../util'; import { easyLabelFromFilter, easyLabelFromFilterArray, niceName } from '../utils/labels'; -import { ISpecialAttribute } from './SpecialAttribute'; - -export type AttributeType = 'categorical' | 'number' | 'string'; - -export type ScoreType = 'depletion' | 'expression' | 'copy_number' | 'mutation'; - -export type IdValuePair = { - id: string; - [key: string]: any; -}; - -export interface IAttributeJSON { - option: IOption; - currentDB: string; - currentView: string; -} - -/** - * base type for ServerColumns and ScoreColumn - * id, view, and database are needed for the methods in rest.ts - */ -export interface IAttribute { - /** - * id database access the data - */ - id: string; - - /** - * datakey with which the attributes data can be accessed frontend - * necessary as GeneScoreAttributes have the same id for TPM, Copy Number, etc of the same gene - */ - dataKey: string; - - /** - * view to access the data - */ - view: string; - - /** - * database to access the data - */ - database: string; - - /** - * label of this column for display to the user by default the column name - */ - label: string; - - /** - * column type - */ - type: AttributeType; - - /** - * the categories in case of type=categorical - */ - categories?: string[]; - - /** - * the minimal value in case of type=number - */ - min?: number; - - /** - * the maxmial value in case of type=number - */ - max?: number; - - /** - * wether the attribute is best shown on a log scale - */ - preferLog: boolean; - - getData(cohortDbId: number, filters?: IAllFilters): Promise; // IRow has _id but IScoreRow has not - - getHist(dbId: number, filters?: IAllFilters, bins?: number): Promise<{ bin: string; count: number }[]>; - - getHistWithStorage( - histType: HistRouteType, - params: ICohortDBHistDataParms | ICohortDBHistScoreParms | ICohortDBHistScoreDepletionParms | ICohortDBHistPanelParms, - ): Promise<{ bin: string; count: number }[]>; - - getCount(cohortDbId: number, filters?: IAllFilters): Promise; - - filter(cht: ICohort, filter: INumRange[] | IEqualsList): Promise; - - toJSON(); -} +import { AttributeType, IAttribute, IAttributeJSON, IdValuePair, OptionType, ScoreType } from './IAttribute'; +import type { ISpecialAttribute } from './ISpecialAttribute'; export abstract class Attribute implements IAttribute { public preferLog = false; @@ -296,6 +211,20 @@ export abstract class AScoreAttribute extends Attribute { } } +function subType2Type(subType: string): AttributeType { + // one of number, string, cat, boxplot + switch (subType) { + case 'number': + case 'string': + return subType; + case 'cat': + return 'categorical'; + case 'boxplot': + default: + throw new Error(`No Support for type: ${subType}`); + } +} + /** * Returns the depletion screen (whatever that is) for the given score sub type id * @param subTypeID known subtype ids are ['rsa', 'ataris', 'ceres'] but we are just checking if it is ceres or not. @@ -535,65 +464,6 @@ export class PanelScoreAttribute extends AScoreAttribute { } } -export function toAttribute(option: IOption, currentDB, currentView): IAttribute { - if (option.optionType === 'dbc') { - if (option.optionData && (option as ISpecialOption).optionData.spAttribute) { - // Create Special Attribtues - // if (option.optionData.spA === 'treatment') { - log.debug('create special Attribute: ', option.optionId); - log.debug('special Attribute object: ', option.optionData.spAttribute); - return new SpecialAttribute( - option.optionId, - currentView, - currentDB, - (option as ISpecialOption).optionData.spAttribute, - (option as ISpecialOption).optionData.attrOption, - ); - } - // Create Attribute - return new ServerColumnAttribute(option.optionId, currentView, currentDB, (option as IServerColumnOption).optionData.serverColumn); - } - // Create ScoreAttribute - if (option.optionType === 'gene') { - return new GeneScoreAttribute( - option.optionId, - option.optionText, - currentView, - currentDB, - (option as IScoreOption).optionData.type, - (option as IScoreOption).optionData.subType, - ); - } - if (option.optionType === 'panel') { - return new PanelScoreAttribute(option.optionId, currentView, currentDB, 'categorical'); - } -} - -function subType2Type(subType: string): AttributeType { - // one of number, string, cat, boxplot - switch (subType) { - case 'number': - case 'string': - return subType; - case 'cat': - return 'categorical'; - case 'boxplot': - default: - throw new Error(`No Support for type: ${subType}`); - } -} - -export async function multiFilter(baseCohort: ICohort, attributes: IAttribute[], filters: Array): Promise { - if (attributes.length !== filters.length) { - throw new Error(`Number of filters has to match the number of attribtues`); - } - - return multiAttributeFilter( - baseCohort, - attributes.map((attr, i) => ({ attr, range: filters[i] })), - ); -} - export async function multiAttributeFilter(baseCohort: ICohort, filters: IAttributeFilter[]): Promise { let newCohort = baseCohort; @@ -602,6 +472,8 @@ export async function multiAttributeFilter(baseCohort: ICohort, filters: IAttrib const values = []; for (const attrFilter of filters) { + // TODO: fix me + // eslint-disable-next-line no-await-in-loop newCohort = await attrFilter.attr.filter(newCohort, attrFilter.range); labelOne.push(newCohort.labelOne); labelTwo.push(newCohort.labelTwo); @@ -617,3 +489,14 @@ export async function multiAttributeFilter(baseCohort: ICohort, filters: IAttrib return newCohort; } + +export async function multiFilter(baseCohort: ICohort, attributes: IAttribute[], filters: Array): Promise { + if (attributes.length !== filters.length) { + throw new Error(`Number of filters has to match the number of attribtues`); + } + + return multiAttributeFilter( + baseCohort, + attributes.map((attr, i) => ({ attr, range: filters[i] })), + ); +} diff --git a/src/data/IAttribute.ts b/src/data/IAttribute.ts new file mode 100644 index 0000000..b9f47ae --- /dev/null +++ b/src/data/IAttribute.ts @@ -0,0 +1,128 @@ +import { IAllFilters, IServerColumn } from 'tdp_core'; +import { IDataSubtypeConfig } from 'tdp_publicdb'; +import { ICohort } from '../app/interfaces'; +import { INumRange, IEqualsList, HistRouteType } from '../base/interfaces'; +import { ICohortDBHistDataParms, ICohortDBHistPanelParms, ICohortDBHistScoreDepletionParms, ICohortDBHistScoreParms } from '../base/rest'; + +export interface ISearchBarGroup { + groupLabel: string; + data: Array; +} + +export type OptionType = 'dbc' | 'gene' | 'panel'; + +export interface IOption { + // id: string; + optionId: string; // e.g. gender or ensg00000141510 + optionType: OptionType; // e.g. dbc or gene + optionText: string; // e.g. Gender or TP53 + optionData?: { + [key: string]: any; // keys are always strings, so we just specify it to be key/value pairs with values of any type + }; +} + +export interface IPanelOption extends IOption { + optionData: { + description: string; // e.g. "Cancer Cell Line Encyclopedia" + species: string; // e.g. human + }; +} + +export type ScoreType = 'depletion' | 'expression' | 'copy_number' | 'mutation'; +export interface IScoreOption extends IOption { + optionData: { + type: ScoreType; // id of = IDataTypeConfig; + subType: IDataSubtypeConfig; + }; +} + +export interface IServerColumnOption extends IOption { + optionData: { + serverColumn: IServerColumn; + }; +} + +export type AttributeType = 'categorical' | 'number' | 'string'; + +export type IdValuePair = { + id: string; + [key: string]: any; +}; + +export interface IAttributeJSON { + option: IOption; + currentDB: string; + currentView: string; +} + +/** + * base type for ServerColumns and ScoreColumn + * id, view, and database are needed for the methods in rest.ts + */ +export interface IAttribute { + /** + * id database access the data + */ + id: string; + + /** + * datakey with which the attributes data can be accessed frontend + * necessary as GeneScoreAttributes have the same id for TPM, Copy Number, etc of the same gene + */ + dataKey: string; + + /** + * view to access the data + */ + view: string; + + /** + * database to access the data + */ + database: string; + + /** + * label of this column for display to the user by default the column name + */ + label: string; + + /** + * column type + */ + type: AttributeType; + + /** + * the categories in case of type=categorical + */ + categories?: string[]; + + /** + * the minimal value in case of type=number + */ + min?: number; + + /** + * the maxmial value in case of type=number + */ + max?: number; + + /** + * wether the attribute is best shown on a log scale + */ + preferLog: boolean; + + getData(cohortDbId: number, filters?: IAllFilters): Promise; // IRow has _id but IScoreRow has not + + getHist(dbId: number, filters?: IAllFilters, bins?: number): Promise<{ bin: string; count: number }[]>; + + getHistWithStorage( + histType: HistRouteType, + params: ICohortDBHistDataParms | ICohortDBHistScoreParms | ICohortDBHistScoreDepletionParms | ICohortDBHistPanelParms, + ): Promise<{ bin: string; count: number }[]>; + + getCount(cohortDbId: number, filters?: IAllFilters): Promise; + + filter(cht: ICohort, filter: INumRange[] | IEqualsList): Promise; + + toJSON(); +} diff --git a/src/data/ISpecialAttribute.ts b/src/data/ISpecialAttribute.ts new file mode 100644 index 0000000..cd99076 --- /dev/null +++ b/src/data/ISpecialAttribute.ts @@ -0,0 +1,53 @@ +import { IAllFilters } from 'tdp_core'; +import { + HistRouteType, + ICohortDBHistDataParms, + ICohortDBHistScoreParms, + ICohortDBHistScoreDepletionParms, + ICohortDBHistPanelParms, + ICohort, + INumRange, + IEqualsList, +} from '../index'; +import { AttributeType, IdValuePair } from './IAttribute'; + +export interface ISpecialAttribute { + readonly overrideSearchBarDetails: boolean; + readonly overrideGetData: boolean; + readonly overrideGetHist: boolean; + readonly overrideGetCount: boolean; + readonly overrideFilter: boolean; + + /** + * id database access the data + */ + readonly id: string; + + label: string; + type: AttributeType; + dataKey: string; + /** + * Possible options the attribute could be formated + */ + options: { id: string; name: string }[]; + + /** + * Option that is should be used to format the data + */ + attributeOption: string; + + getDetailForSearchBar(): HTMLDivElement; + + getData(cohortDbId: number, filters?: IAllFilters): Promise; // IRow has _id but IScoreRow has not + + getHist(dbId: number, filters?: IAllFilters, bins?: number): Promise<{ bin: string; count: number }[]>; + + getHistWithStorage( + histType: HistRouteType, + params: ICohortDBHistDataParms | ICohortDBHistScoreParms | ICohortDBHistScoreDepletionParms | ICohortDBHistPanelParms, + ): Promise<{ bin: string; count: number }[]>; + + getCount(cohortDbId: number, filters?: IAllFilters): Promise; + + filter(cht: ICohort, filter: INumRange[] | IEqualsList, label: string): Promise; +} diff --git a/src/data/SpecialAttribute.ts b/src/data/SpecialAttribute.ts index d99a0d0..5312dce 100644 --- a/src/data/SpecialAttribute.ts +++ b/src/data/SpecialAttribute.ts @@ -1,62 +1,14 @@ import _ from 'lodash'; import { IAllFilters } from 'tdp_core'; -import { Cohort, createCohortWithTreatmentFilter } from '../Cohort'; -import { getRootCohort } from '../cohortview'; -import { - getCohortData, - HistRouteType, - ICohortDBHistDataParms, - ICohortDBHistPanelParms, - ICohortDBHistScoreDepletionParms, - ICohortDBHistScoreParms, - IEqualsList, - INumRange, -} from '../base/rest'; +import { getCohortData, ICohortDBHistDataParms, ICohortDBHistPanelParms, ICohortDBHistScoreDepletionParms, ICohortDBHistScoreParms } from '../base/rest'; import { deepCopy, getSessionStorageItem, log, setSessionStorageItem } from '../util'; import { niceName } from '../utils/labels'; -import { AttributeType, IdValuePair } from './Attribute'; import { ICohort } from '../app/interfaces'; - -export interface ISpecialAttribute { - readonly overrideSearchBarDetails: boolean; - readonly overrideGetData: boolean; - readonly overrideGetHist: boolean; - readonly overrideGetCount: boolean; - readonly overrideFilter: boolean; - - /** - * id database access the data - */ - readonly id: string; - - label: string; - type: AttributeType; - dataKey: string; - /** - * Possible options the attribute could be formated - */ - options: { id: string; name: string }[]; - - /** - * Option that is should be used to format the data - */ - attributeOption: string; - - getDetailForSearchBar(): HTMLDivElement; - - getData(cohortDbId: number, filters?: IAllFilters): Promise; // IRow has _id but IScoreRow has not - - getHist(dbId: number, filters?: IAllFilters, bins?: number): Promise<{ bin: string; count: number }[]>; - - getHistWithStorage( - histType: HistRouteType, - params: ICohortDBHistDataParms | ICohortDBHistScoreParms | ICohortDBHistScoreDepletionParms | ICohortDBHistPanelParms, - ): Promise<{ bin: string; count: number }[]>; - - getCount(cohortDbId: number, filters?: IAllFilters): Promise; - - filter(cht: ICohort, filter: INumRange[] | IEqualsList, label: string): Promise; -} +import { createCohortWithTreatmentFilter } from '../Cohort'; +import { CohortContext } from '../CohortContext'; +import { INumRange, IEqualsList, HistRouteType } from '../base'; +import { AttributeType, IdValuePair } from './IAttribute'; +import type { ISpecialAttribute } from './ISpecialAttribute'; export class SATreatment implements ISpecialAttribute { readonly overrideSearchBarDetails: boolean = true; @@ -121,7 +73,7 @@ export class SATreatment implements ISpecialAttribute { // check if histogram was saved in sesison storage if (maxRegimen === null || getSessionStorageItem('treatment#categoriesHist') === null || getSessionStorageItem('treatment#categoriesBaseHist') === null) { let calcMaxRegimen = 0; - const rootCohort = getRootCohort(); + const rootCohort = CohortContext.referenceCohort; const rows = await getCohortData({ cohortId: rootCohort.dbId, attribute: this.id }); // const maxRegimenNumbRows = Math.max(...rows.map((r) => r.treatment).map((t) => t.REGIMEN_NUMBER)); @@ -386,10 +338,11 @@ export class SATreatment implements ISpecialAttribute { return createCohortWithTreatmentFilter(cht, niceName(`${this.id}:${optName}`), label, baseAgent, agent, rgNumb); } } + return null; } } -const specAttributes: { id: string; getClass: Function }[] = [ +const specAttributes: { id: string; getClass: () => ISpecialAttribute }[] = [ { id: SATreatment.ID, getClass: (): ISpecialAttribute => { diff --git a/src/data/index.ts b/src/data/index.ts index d800a15..3dfdeee 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -1 +1,2 @@ export * from './Attribute'; +export * from './IAttribute'; diff --git a/src/index.ts b/src/index.ts index 2d065f0..33d575a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,11 +3,10 @@ export * from './data'; export * from './Overview'; export * from './Taskview'; -export * from './app/Coral'; export * from './Cohort'; export * from './CohortRepresentations'; export * from './cohortview'; -export * from './base/rest'; +export * from './base'; export * from './util'; export * from './utils'; diff --git a/src/phovea.ts b/src/phovea.ts index c0f565f..15c4c10 100644 --- a/src/phovea.ts +++ b/src/phovea.ts @@ -3,12 +3,12 @@ * Copyright (c) The Caleydo Team. All rights reserved. * Licensed under the new BSD license, available at http://caleydo.org/license **************************************************************************** */ -import { IRegistry } from 'tdp_core'; +import type { IRegistry } from 'visyn_core'; export default function (registry: IRegistry) { // helper functions copied from tdp_core: // ------------------------------------------------------------------------------------------------------- - function actionFunction(id: string, factory: string, loader: () => any, options?: {}) { + function actionFunction(id: string, factory: string, loader: () => unknown, options?: object) { registry.push('actionFunction', id, loader, { factory, ...options }); } @@ -25,7 +25,7 @@ export default function (registry: IRegistry) { /** * Set the base data-set */ - actionFunction('chtSetDataset', 'setDatasetImpl', () => import('./Provenance/General'), { + actionFunction('chtSetDataset', 'setDatasetImpl', () => import('./Provenance/General.js'), { // setDatasetImpl = function that acutally sets the dataset analytics: { category: 'data', // this one is a data operation (other options are visual, selections, layout, and analysis) @@ -39,7 +39,7 @@ export default function (registry: IRegistry) { /** * Add Cohorts */ - actionFunction('addCohorts', 'addOverviewCohortImpl', () => import('./Provenance/CohortEV').then(), { + actionFunction('addCohorts', 'addOverviewCohortImpl', () => import('./Provenance/CohortEV.js').then(), { // analytics: { category: 'data', // this one is a data operation (other options are visual, selections, layout, and analysis) @@ -50,7 +50,7 @@ export default function (registry: IRegistry) { /** * Remove Cohorts */ - actionFunction('removeCohorts', 'removeOverviewCohortImpl', () => import('./Provenance/CohortEV').then(), { + actionFunction('removeCohorts', 'removeOverviewCohortImpl', () => import('./Provenance/CohortEV.js').then(), { // analytics: { category: 'data', // this one is a data operation (other options are visual, selections, layout, and analysis) diff --git a/src/phovea_registry.ts b/src/phovea_registry.ts index 9a11b28..a1546fa 100644 --- a/src/phovea_registry.ts +++ b/src/phovea_registry.ts @@ -4,13 +4,14 @@ * Licensed under the new BSD license, available at http://caleydo.org/license **************************************************************************** */ -import { PluginRegistry } from 'tdp_core'; +import { PluginRegistry } from 'visyn_core'; import reg from './phovea'; /** * build a registry by registering all phovea modules */ // other modules +import 'visyn_core/phovea_registry'; import 'tdp_core/dist/phovea_registry'; // self diff --git a/src/util.ts b/src/util.ts index fceade1..72b22d8 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,9 +1,17 @@ import { select, Selection } from 'd3v7'; import * as logger from 'loglevel'; -import { NotificationHandler } from 'tdp_core'; +import { IAllFilters, IParams, NotificationHandler } from 'tdp_core'; import { ICohort } from './app/interfaces'; -import { IAttribute } from './data/Attribute'; -import { IEqualsList, INumRange } from './base/rest'; +import { INumRange, IEqualsList } from './base/interfaces'; +import type { IAttribute } from './data/IAttribute'; + +enum FilterType { + normal, + lt, + lte, + gt, + gte, +} logger.setDefaultLevel(logger.levels.INFO); export const log = logger; @@ -32,7 +40,8 @@ export const deepCopy = (target: T): T => { }); return cp.map((n: any) => deepCopy(n)) as any; } - if (typeof target === 'object' && target !== {}) { + // not empty object + if (typeof target === 'object' && Object.keys(target).length > 0) { const cp = { ...(target as { [key: string]: any }) } as { [key: string]: any }; Object.keys(cp).forEach((k) => { cp[k] = deepCopy(cp[k]); @@ -170,6 +179,91 @@ export class DebugTools { */ static async sleep(millis: number): Promise { log.info('taking a little nap'); - return new Promise((resolve) => setTimeout(resolve, millis)); + return new Promise((resolve) => { + setTimeout(resolve, millis); + }); + } +} + +function mergerAllFilterPart(filterType: FilterType, existingFilter: IParams, newFilter: IParams): IParams { + let mergedFilter = deepCopy(existingFilter); + // const currType : FilterType = FilterType.normal; + let filterContradiction = false; + // go through all attribuets of the new filter + for (const attr in newFilter) { + // check if attribute exists in the new filter + if (Object.prototype.hasOwnProperty.call(newFilter, attr)) { + // check if attribute exists in the existing filter + if (Object.prototype.hasOwnProperty.call(existingFilter, attr)) { + const newVal = newFilter[attr]; // current value for attribute + const existVal = existingFilter[attr]; // current value for attribute + + if (filterType === FilterType.normal) { + // convert newVal to an array of values + const newValArray = Array.isArray(newVal) ? (newVal as string[] | number[] | boolean[]) : new Array(newVal as string | number | boolean); + // convert existVal to an array of values + const existValArray = Array.isArray(existVal) ? (existVal as string[] | number[] | boolean[]) : new Array(existVal as string | number | boolean); + // if all new values are part of the existing values, then the filter is OK + for (const nv of newValArray) { + if (!existValArray.includes(nv)) { + filterContradiction = true; + } + } + // no filter contradiction -> new filter values can be used + if (!filterContradiction) { + // outside of for-loop + mergedFilter[attr] = newFilter[attr]; + } + } else if (filterType === FilterType.lt || filterType === FilterType.lte) { + // smaller is OK + if (newVal <= existVal) { + mergedFilter[attr] = newFilter[attr]; + } else { + filterContradiction = true; + } + } else if (filterType === FilterType.gt || filterType === FilterType.gte) { + // bigger is OK + if (newVal >= existVal) { + mergedFilter[attr] = newFilter[attr]; + } else { + filterContradiction = true; + } + } + } else { + // the new filter attribute is not existing -> add to the resulting filter + mergedFilter[attr] = newFilter[attr]; + } + } } + + if (filterContradiction) { + mergedFilter = null; + } + log.debug('Filter Contradiction: ', filterContradiction); + return mergedFilter; +} + +export function mergeTwoAllFilters(existingFilter: IAllFilters, newFilter: IAllFilters): IAllFilters { + let mergerAllFilter: IAllFilters = null; + if (existingFilter !== null) { + const normal: IParams = mergerAllFilterPart(FilterType.normal, existingFilter.normal, newFilter.normal); + const lt: IParams = mergerAllFilterPart(FilterType.lt, existingFilter.lt, newFilter.lt); + const lte: IParams = mergerAllFilterPart(FilterType.lte, existingFilter.lte, newFilter.lte); + const gt: IParams = mergerAllFilterPart(FilterType.gt, existingFilter.gt, newFilter.gt); + const gte: IParams = mergerAllFilterPart(FilterType.gte, existingFilter.gte, newFilter.gte); + + // if the filters are all !== null, that means there is no filter contradiction -> set all filters of mergerAllFilter + // otherwise the mergerAllFilter stays null + if (normal !== null && lte !== null && lte !== null && gt !== null && gte !== null) { + mergerAllFilter = { + normal, + lt, + lte, + gt, + gte, + }; + } + } + + return mergerAllFilter; } diff --git a/src/utils/labels.ts b/src/utils/labels.ts index 261d210..7ed7c4d 100644 --- a/src/utils/labels.ts +++ b/src/utils/labels.ts @@ -1,5 +1,5 @@ import { format } from 'd3v7'; -import { IEqualsList, INumRange, isEqualsList, isNumRangeFilter, NumRangeOperators } from '../base/rest'; +import { INumRange, IEqualsList, isNumRangeFilter, isEqualsList, NumRangeOperators } from '../base'; export function niceName(label: string): string { return label