From 75493e2a106b3e76226dcc1b93e02417a9c2b8f2 Mon Sep 17 00:00:00 2001 From: Milho001 <l.milhomemfrancochristino@uu.nl> Date: Wed, 26 Jul 2023 19:30:43 +0000 Subject: [PATCH] feat(querybuilder): full rework of querybuilder Inner query in JSON format fully reworked. The interface was reworked to use entity attributes as part of query logic. Handle inner logic and types fully reworked for better type-checking and future-proofing. Includes logic in query building, parsing, and execution. Logic pills can be created from the handle drop and from the side panel. Solves #42 and #53 --- .editorconfig | 2 + .gitignore | 3 +- Dockerfile | 24 +- Makefile | 16 + apps/docs/.eslintrc.js | 2 +- apps/docs/next.config.js | 2 +- apps/docs/pages/index.tsx | 2 +- apps/web/.env | 4 +- apps/web/.env.development | 7 + apps/web/.env.production | 9 +- apps/web/.eslintignore | 7 +- apps/web/node.d.ts | 25 +- apps/web/package.json | 97 +- apps/web/src/app/app.module.scss | 17 +- apps/web/src/app/app.tsx | 80 +- apps/web/src/app/panels/Visualization.tsx | 27 +- .../navbar/AddDatabaseForm/index.tsx | 52 +- .../stories/add-database-form.stories.tsx | 6 +- apps/web/src/components/navbar/navbar.tsx | 58 +- apps/web/src/components/panels/panel.tsx | 8 +- apps/web/src/environments/variables.ts | 1 - apps/web/src/main.tsx | 4 +- apps/web/tsconfig.json | 60 +- apps/web/vite.config.ts | 8 +- libs/shared/.env | 3 - libs/shared/.eslintignore | 7 +- libs/shared/lib/data-access/api/database.ts | 61 +- libs/shared/lib/data-access/api/index.ts | 8 +- libs/shared/lib/data-access/api/query.ts | 95 +- libs/shared/lib/data-access/api/schema.ts | 69 +- libs/shared/lib/data-access/api/user.ts | 8 +- .../lib/data-access/authorization/useAuth.jsx | 27 +- libs/shared/lib/data-access/index.ts | 8 +- .../DatabaseRepository.tsx | 36 - .../QueryRepository.tsx | 27 - .../TranslatedJSONQuery.tsx | 85 - .../BackendMessageReceiverMock.tsx | 4 +- .../WebSocketHandler.tsx | 10 +- .../socket/backend-message-receiver/index.ts | 2 +- .../BackendMessenger.test.tsx | 4 +- .../BackendMessengerMock.tsx | 4 +- .../socket/backend-messenger/index.tsx | 6 +- .../lib/data-access/socket/broker/index.tsx | 15 +- libs/shared/lib/data-access/socket/index.ts | 2 +- .../socket/listeners/SchemaViewModelImpl.tsx | 897 -- .../lib/data-access/socket/query/QueryApi.tsx | 30 - .../shared/lib/data-access/store/authSlice.ts | 18 +- .../store/colorPaletteConfigSlice.ts | 18 +- .../store/graphQueryResultSlice.ts | 56 +- libs/shared/lib/data-access/store/hooks.ts | 15 +- libs/shared/lib/data-access/store/index.ts | 12 +- .../data-access/store/querybuilderSlice.ts | 102 +- .../lib/data-access/store/schemaSlice.ts | 9 +- .../lib/data-access/store/sessionSlice.ts | 14 +- .../lib/data-access/store/store.spec.ts | 2 +- libs/shared/lib/data-access/store/store.ts | 5 +- libs/shared/lib/data-access/theme/colours.tsx | 10 +- .../theme/graphPolarisThemeProvider.spec.tsx | 12 +- .../theme/graphPolarisThemeProvider.tsx | 21 +- .../lib/graph-layout/cytoscape-layouts.ts | 37 +- .../lib/graph-layout/graphology-layouts.ts | 24 +- .../lib/graph-layout/graphology.spec.ts | 3 +- .../layout-creator-usecase.spec.ts | 813 +- .../graph-layout/layout-creator-usecase.ts | 54 +- libs/shared/lib/graph-layout/layout.ts | 11 +- .../lib/graph-layout/mockdata-layout.spec.ts | 2 +- ...yResult.ts => big2ndChamberQueryResult.js} | 2 +- .../query-result/geo-mock-data.js} | 7663 +++++++++-------- .../lib/mock-data/query-result/index.ts | 10 +- .../query-result/mockLargeQueryResults.ts | 6000 ++++++------- .../query-result/mockQueryResults.ts | 4 +- .../query-result/smallFlightsQueryResults.ts | 1 + libs/shared/lib/mock-data/schema/index.ts | 2 +- .../lib/mock-data/schema/mock-data.spec.ts | 18 +- .../lib/mock-data/schema/moviesSchemaRaw.ts | 1 - .../mock-data/schema/northwindSchemaRaw.ts | 3 +- .../lib/mock-data/schema/simpleAirportRaw.ts | 4 +- libs/shared/lib/mock-data/schema/simpleRaw.ts | 6 +- .../lib/mock-data/schema/twitterSchemaRaw.ts | 5 +- libs/shared/lib/querybuilder/index.ts | 2 +- .../querybuilder/model/BackendQueryFormat.tsx | 181 +- .../querybuilder/model/graphology/index.ts | 4 +- .../querybuilder/model/graphology/model.ts | 115 +- .../querybuilder/model/graphology/utils.ts | 211 +- libs/shared/lib/querybuilder/model/index.ts | 32 +- .../lib/querybuilder/model/logic/general.ts | 205 + .../model/logic/graphFunctions.tsx | 146 + .../lib/querybuilder/model/logic/index.ts | 32 +- .../model/logic/mathAggregations.tsx | 745 ++ .../querybuilder/model/logic/mathFilters.tsx | 93 + .../model/logic/mathFunctions.tsx | 67 + .../model/logic/queryFunctions.tsx | 148 - .../model/logic/stringFilters.tsx | 66 + .../model/logic/stringFunctions.tsx | 44 + .../lib/querybuilder/model/logic/utils.ts | 0 .../querybuilder/model/reactflow/handles.tsx | 74 +- .../lib/querybuilder/model/reactflow/index.ts | 8 +- .../querybuilder/model/reactflow/model.tsx | 113 +- .../lib/querybuilder/model/reactflow/utils.ts | 120 +- libs/shared/lib/querybuilder/panel/index.ts | 2 +- .../panel/querybuilder.module.scss | 70 +- .../panel/querybuilder.module.scss.d.ts | 1 + .../panel/querybuilder.stories.tsx | 131 - .../lib/querybuilder/panel/querybuilder.tsx | 830 +- .../panel/shemaquerybuilder.stories.tsx | 14 +- ...erybuilder-simple-disconnected.stories.tsx | 108 + .../stories/querybuilder-simple.stories.tsx | 161 + .../querybuilder-single-entity.stories.tsx | 51 + ...erybuilder-single-relationship.stories.tsx | 53 + .../pills/customFlowLines/connection.tsx | 16 +- .../pills/customFlowLines/connectionDrag.tsx | 33 +- .../pills/customFlowLines/index.ts | 4 +- .../attributepill-full.stories.tsx | 59 - .../attributepill.module.scss.d.ts | 12 + ...s.tsx => attributepill.storiesDEFUNCT.tsx} | 6 +- .../attributepill/attributepill.tsx | 26 +- .../getAttributeBoolOperators.ts | 4 +- .../attributepill/operator/operatorselect.tsx | 16 +- .../attributepill/select-component.tsx | 8 +- .../pills/customFlowPills/edge-line.tsx | 23 +- .../entitypill/entitypill-full.stories.tsx | 15 +- .../entitypill/entitypill.module.scss | 98 +- .../entitypill/entitypill.module.scss.d.ts | 21 +- .../entitypill/entitypill.stories.tsx | 7 +- .../customFlowPills/entitypill/entitypill.tsx | 141 +- .../functionpill/SelectFunction.tsx | 15 +- .../functionpill/functionpill.module.scss | 342 +- .../functionpill.module.scss.d.ts | 23 +- .../functionpill/functionpill.stories.tsx | 10 +- .../functionpill/functionpill.tsx | 41 +- .../customFlowPills/functionpill/index.ts | 2 +- .../pills/customFlowPills/index.ts | 6 +- .../logicpill/logicpill.module.scss | 58 + .../logicpill/logicpill.module.scss.d.ts | 28 + .../customFlowPills/logicpill/logicpill.tsx | 137 + .../modifierpill/modifierpill.module.scss | 55 - .../modifierpill.module.scss.d.ts | 19 - .../modifierpill/modifierpill.tsx | 33 - .../modifierpill/mopdifierpill.stories.tsx | 61 - .../modifierpill/select-modifier.tsx | 88 - .../relation-full_reactflow.stories.tsx | 29 +- .../relationpill.module.scss.d.ts | 12 + .../relationpill/relationpill.stories.tsx | 7 +- .../relationpill/relationpill.tsx | 104 +- .../pills/dragging/dragAttribute.ts | 13 +- .../pills/dragging/dragAttributesAlong.ts | 7 +- .../querybuilder/pills/dragging/dragEntity.ts | 7 +- .../querybuilder/pills/dragging/dragPill.ts | 34 +- .../pills/dragging/dragRelation.ts | 7 +- .../pills/dragging/getClosestPill.ts | 7 +- .../querybuilder/pills/handles.module.scss | 57 + .../pills/handles.module.scss.d.ts | 15 + .../querybuilder/pills/querypills.module.scss | 2 + .../pills/querypills.module.scss.d.ts | 12 + libs/shared/lib/querybuilder/pills/utils.ts | 13 + .../lib/querybuilder/query-utils/index.ts | 4 +- .../querybuilder/query-utils/query-utils.ts | 77 - .../query-utils/query2backend.spec.ts | 960 +++ .../querybuilder/query-utils/query2backend.ts | 311 + libs/shared/lib/schema/index.ts | 3 +- libs/shared/lib/schema/model/FromBackend.ts | 30 +- libs/shared/lib/schema/model/graphology.ts | 8 +- libs/shared/lib/schema/model/index.ts | 6 +- libs/shared/lib/schema/model/reactflow.tsx | 20 +- libs/shared/lib/schema/panel/index.ts | 2 +- libs/shared/lib/schema/panel/schema.spec.ts | 2 +- .../lib/schema/panel/schema.stories.tsx | 6 +- libs/shared/lib/schema/panel/schema.tsx | 70 +- libs/shared/lib/schema/panel/schemaOLD.tsx | 48 +- .../panel/view-model/schemaViewModel.test.t | 2640 +++--- .../lib/schema/pills/edges/node-edge.tsx | 19 +- .../lib/schema/pills/edges/self-edge.tsx | 19 +- .../nodes/entity/entity-node.stories.tsx | 6 +- .../schema/pills/nodes/entity/entity-node.tsx | 316 +- ...attribute-analytics-popup-menu.stories.tsx | 22 +- .../popup/attribute-analytics-popup-menu.tsx | 54 +- .../node-quality-entity-popup.stories.tsx | 6 +- .../nodes/popup/node-quality-entity-popup.tsx | 7 +- .../node-quality-relation-popup.stories.tsx | 6 +- .../popup/node-quality-relation-popup.tsx | 111 +- ...attribute-analytics-popup-menu.stories.tsx | 22 +- .../attribute-analytics-popup-menu.tsx | 80 +- .../popup/popupmenus/filterbar.stories.tsx | 6 +- .../nodes/popup/popupmenus/filterbar.tsx | 77 +- .../node-quality-entity-popup.stories.tsx | 6 +- .../popupmenus/node-quality-entity-popup.tsx | 11 +- .../node-quality-relation-popup.stories.tsx | 6 +- .../node-quality-relation-popup.tsx | 12 +- .../popup/popupmenus/searchbar.stories.tsx | 6 +- .../nodes/relation/relation-node.stories.tsx | 6 +- .../pills/nodes/relation/relation-node.tsx | 284 +- .../schema/schema-utils/flow-utils.test.tsx | 8 +- .../lib/schema/schema-utils/flow-utils.ts | 53 +- .../schema-utils/schema-usecases.spec.ts | 54 +- .../schema/schema-utils/schema-usecases.ts | 17 +- .../schema/schema-utils/schema-utils.spec.ts | 2 +- .../lib/schema/schema-utils/schema-utils.ts | 16 +- .../lib/vis/geovis/GeoNodeLinkMap.stories.tsx | 73 + libs/shared/lib/vis/geovis/GeoNodeLinkMap.tsx | 117 + ...ViewModel.tsx => GeoNodeLinkViewModel.tsx} | 11 +- .../lib/vis/geovis/NodeLinkMap.stories.tsx | 73 - libs/shared/lib/vis/geovis/NodeLinkMap.tsx | 119 - libs/shared/lib/vis/geovis/Tooltip.tsx | 30 +- .../lib/vis/geovis/aggregateCommand.tsx | 32 +- .../lib/vis/geovis/layers/BaseLayer.tsx | 6 +- .../lib/vis/geovis/layers/ClusterLayer.tsx | 37 +- .../lib/vis/geovis/layers/FacetingLayer.tsx | 27 +- .../lib/vis/geovis/layers/NodeLinkLayer.tsx | 21 +- libs/shared/lib/vis/geovis/readability.tsx | 18 +- libs/shared/lib/vis/geovis/unfoldingEdges.tsx | 16 +- libs/shared/lib/vis/geovis/utils.tsx | 46 +- libs/shared/lib/vis/index.ts | 3 + .../lib/vis/nodelink/NodeLinkViewModel.tsx | 165 +- libs/shared/lib/vis/nodelink/Types.tsx | 1 + .../lib/vis/nodelink/nodelinkvis.stories.tsx | 60 +- libs/shared/lib/vis/nodelink/nodelinkvis.tsx | 20 +- .../lib/vis/nodelink/nodelinkviz.types.tsx | 97 - .../paohvis/components/HyperEdgesRange.tsx | 44 +- .../paohvis/components/MakePaohvisMenu.scss | 4 +- .../paohvis/components/MakePaohvisMenu.tsx | 110 +- .../PaohvisFilterComponent.module.scss | 17 +- .../PaohvisFilterComponent.module.scss.d.ts | 2 + .../components/PaohvisFilterComponent.tsx | 203 +- .../vis/paohvis/components/RowLabelColumn.tsx | 16 +- .../lib/vis/paohvis/components/Tooltip.tsx | 20 +- .../vis/paohvis/models/FilterPredicates.tsx | 54 +- .../lib/vis/paohvis/models/PaohvisHolder.tsx | 4 +- .../lib/vis/paohvis/paohvis.module.scss | 20 +- .../lib/vis/paohvis/paohvis.module.scss.d.ts | 4 + .../lib/vis/paohvis/paohvis.stories.tsx | 97 +- libs/shared/lib/vis/paohvis/paohvis.tsx | 208 +- .../paohvis/utils/AttributesFilterUseCase.tsx | 34 +- .../utils/CalcEntitiesFromQueryResult.tsx | 5 +- ...EntityAttrAndRelNamesFromSchemaUseCase.tsx | 10 +- .../lib/vis/paohvis/utils/SortUseCase.tsx | 15 +- .../utils/ToPaohvisDataParserUsecase.tsx | 100 +- libs/shared/lib/vis/paohvis/utils/utils.tsx | 226 +- libs/shared/lib/vis/rawjsonvis/index.ts | 2 +- .../lib/vis/rawjsonvis/rawjsonvis.spec.tsx | 8 +- .../lib/vis/rawjsonvis/rawjsonvis.stories.tsx | 34 +- libs/shared/lib/vis/rawjsonvis/rawjsonvis.tsx | 23 +- .../SemanticSubstratesViewModel.t | 1156 --- .../SemanticSubstratesViewModel.tsx | 68 - .../lib/vis/semanticsubstrates/Types.tsx | 2 +- .../SemanticSubstrateConfigPanel.tsx | 10 +- ...SemanticSubstratesConfigPanelViewModel.tsx | 111 +- .../semanticsubstrates/configpanel/Types.tsx | 6 +- .../lib/vis/semanticsubstrates/index.ts | 2 +- .../semanticsubstrates.stories.tsx | 79 +- .../semanticsubstrates/semanticsubstrates.tsx | 136 +- .../AddPlotButtonComponent.module.scss | 126 +- .../AddPlotButtonComponent.module.scss.d.ts | 1 + .../subcomponents/AddPlotButtonComponent.tsx | 47 +- .../subcomponents/AddPlotPopup.tsx | 37 +- .../subcomponents/BrushComponent.tsx | 32 +- .../LinesBetweenPlotsComponent.tsx | 40 +- .../subcomponents/OptimizedAutocomplete.tsx | 20 +- .../subcomponents/PlotAxisLabelsComponent.tsx | 13 +- .../subcomponents/PlotComponent.tsx | 26 +- .../subcomponents/PlotTitleComponent.tsx | 229 +- .../subcomponents/SVGCheckBoxComponent.tsx | 192 +- .../CalcConnectionLinePositionsUseCase.tsx | 40 +- .../utils/CalcDefaultPlotSpecsUseCase.tsx | 23 +- .../semanticsubstrates/utils/CalcDistance.tsx | 2 +- .../CalcEntityAttrNamesFromResultUseCase.tsx | 25 +- .../CalcEntityAttrNamesFromSchemaUseCase.tsx | 3 +- .../utils/CalcScaledPositionsUseCase.tsx | 29 +- .../utils/CalcXYMinMaxUseCase.tsx | 7 +- .../utils/FilterUseCase.tsx | 65 +- .../semanticsubstrates/utils/RotateVec.tsx | 5 +- .../utils/ToPlotDataParserUseCase.tsx | 64 +- .../lib/vis/shared/AttributeDataType.tsx | 2 +- libs/shared/lib/vis/shared/InputDataTypes.tsx | 17 +- .../shared/ResultNodeLinkParserUseCase.tsx | 70 +- .../lib/vis/shared/SchemaResultType.test.tsx | 50 +- .../lib/vis/shared/SchemaResultType.tsx | 27 +- libs/shared/lib/vis/shared/Types.tsx | 10 +- .../VisConfigPanel/VisConfigPanel.module.scss | 3 +- .../shared/VisConfigPanel/VisConfigPanel.tsx | 11 +- libs/shared/node.d.ts | 13 + libs/shared/package.json | 6 +- libs/shared/tsconfig.json | 69 +- libs/shared/vite.config.ts | 26 +- libs/shared/vitest.setup.ts | 2 - libs/storybook/.storybook/preview.ts | 4 +- libs/storybook/package.json | 2 +- libs/storybook/src/App.tsx | 22 +- libs/storybook/src/main.tsx | 12 +- libs/storybook/src/stories/Button.tsx | 8 +- libs/storybook/src/stories/Header.tsx | 15 +- libs/storybook/src/stories/Page.tsx | 15 +- libs/storybook/vite.config.ts | 19 +- libs/workspace/eslint-config-custom/index.js | 4 +- nginx/nginx.conf | 32 +- package.json | 6 +- pnpm-lock.yaml | 202 +- turbo.json | 44 +- 297 files changed, 16278 insertions(+), 17106 deletions(-) create mode 100644 Makefile create mode 100644 apps/web/.env.development delete mode 100644 apps/web/src/environments/variables.ts delete mode 100644 libs/shared/.env delete mode 100644 libs/shared/lib/data-access/socket/DELETE-repository-interfaces/DatabaseRepository.tsx delete mode 100644 libs/shared/lib/data-access/socket/DELETE-repository-interfaces/QueryRepository.tsx delete mode 100644 libs/shared/lib/data-access/socket/DELETE-repository-interfaces/TranslatedJSONQuery.tsx delete mode 100644 libs/shared/lib/data-access/socket/listeners/SchemaViewModelImpl.tsx delete mode 100644 libs/shared/lib/data-access/socket/query/QueryApi.tsx rename libs/shared/lib/mock-data/query-result/{big2ndChamberQueryResult.ts => big2ndChamberQueryResult.js} (99%) rename libs/shared/lib/{vis/geovis/mock-data.tsx => mock-data/query-result/geo-mock-data.js} (92%) create mode 100644 libs/shared/lib/querybuilder/model/logic/general.ts create mode 100644 libs/shared/lib/querybuilder/model/logic/graphFunctions.tsx create mode 100644 libs/shared/lib/querybuilder/model/logic/mathAggregations.tsx create mode 100644 libs/shared/lib/querybuilder/model/logic/mathFilters.tsx create mode 100644 libs/shared/lib/querybuilder/model/logic/mathFunctions.tsx delete mode 100644 libs/shared/lib/querybuilder/model/logic/queryFunctions.tsx create mode 100644 libs/shared/lib/querybuilder/model/logic/stringFilters.tsx create mode 100644 libs/shared/lib/querybuilder/model/logic/stringFunctions.tsx create mode 100644 libs/shared/lib/querybuilder/model/logic/utils.ts delete mode 100644 libs/shared/lib/querybuilder/panel/querybuilder.stories.tsx create mode 100644 libs/shared/lib/querybuilder/panel/stories/querybuilder-simple-disconnected.stories.tsx create mode 100644 libs/shared/lib/querybuilder/panel/stories/querybuilder-simple.stories.tsx create mode 100644 libs/shared/lib/querybuilder/panel/stories/querybuilder-single-entity.stories.tsx create mode 100644 libs/shared/lib/querybuilder/panel/stories/querybuilder-single-relationship.stories.tsx delete mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill-full.stories.tsx rename libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/{attributepill.stories.tsx => attributepill.storiesDEFUNCT.tsx} (90%) create mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.module.scss create mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.module.scss.d.ts create mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.tsx delete mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.module.scss delete mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.module.scss.d.ts delete mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.tsx delete mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/mopdifierpill.stories.tsx delete mode 100644 libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/select-modifier.tsx create mode 100644 libs/shared/lib/querybuilder/pills/handles.module.scss create mode 100644 libs/shared/lib/querybuilder/pills/handles.module.scss.d.ts create mode 100644 libs/shared/lib/querybuilder/pills/utils.ts delete mode 100644 libs/shared/lib/querybuilder/query-utils/query-utils.ts create mode 100644 libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts create mode 100644 libs/shared/lib/querybuilder/query-utils/query2backend.ts create mode 100644 libs/shared/lib/vis/geovis/GeoNodeLinkMap.stories.tsx create mode 100644 libs/shared/lib/vis/geovis/GeoNodeLinkMap.tsx rename libs/shared/lib/vis/geovis/{NodeLinkViewModel.tsx => GeoNodeLinkViewModel.tsx} (83%) delete mode 100644 libs/shared/lib/vis/geovis/NodeLinkMap.stories.tsx delete mode 100644 libs/shared/lib/vis/geovis/NodeLinkMap.tsx delete mode 100644 libs/shared/lib/vis/nodelink/nodelinkviz.types.tsx delete mode 100644 libs/shared/lib/vis/semanticsubstrates/SemanticSubstratesViewModel.t delete mode 100644 libs/shared/lib/vis/semanticsubstrates/SemanticSubstratesViewModel.tsx create mode 100644 libs/shared/node.d.ts delete mode 100644 libs/shared/vitest.setup.ts diff --git a/.editorconfig b/.editorconfig index 6e87a003d..44b9cee3d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,8 @@ indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true +max_line_length = 140 +end_of_line = lf [*.md] max_line_length = off diff --git a/.gitignore b/.gitignore index 471ba9c61..3d9346567 100644 --- a/.gitignore +++ b/.gitignore @@ -74,4 +74,5 @@ debug.log Thumbs.db # Certs -certs/*.pem \ No newline at end of file +certs/*.pem +node_modules \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 39c702414..b9ba18921 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ -# Prepare nginx -FROM nginx:1.19-alpine -WORKDIR /app - -COPY ./dist/apps/web-graphpolaris /usr/share/nginx/html - -RUN rm /etc/nginx/conf.d/default.conf -COPY nginx/nginx.conf /etc/nginx/conf.d - -# Fire up nginx -EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] +# Prepare nginx +FROM nginx:1.19-alpine +WORKDIR /app + +COPY ./apps/web/dist /usr/share/nginx/html + +RUN rm /etc/nginx/conf.d/default.conf +COPY nginx/nginx.conf /etc/nginx/conf.d + +# Fire up nginx +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..3e03b7696 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ + +# windows: +# pnpm build + +# macos: +# pnpm build + +# linux: +# pnpm build + +docker: login + @docker build -t graphpolaris/frontend:latest . + @docker push graphpolaris/frontend\:latest + +login: + echo -e "machine git.science.uu.nl\nlogin gitlab-ci-token\npassword ${CI_JOB_TOKEN}" > ~/.netrc \ No newline at end of file diff --git a/apps/docs/.eslintrc.js b/apps/docs/.eslintrc.js index c8df60750..03ee7431b 100644 --- a/apps/docs/.eslintrc.js +++ b/apps/docs/.eslintrc.js @@ -1,4 +1,4 @@ module.exports = { root: true, - extends: ["custom"], + extends: ['custom'], }; diff --git a/apps/docs/next.config.js b/apps/docs/next.config.js index fdda6aa12..9863ce48a 100644 --- a/apps/docs/next.config.js +++ b/apps/docs/next.config.js @@ -1,4 +1,4 @@ module.exports = { reactStrictMode: true, - transpilePackages: ["ui"], + transpilePackages: ['ui'], }; diff --git a/apps/docs/pages/index.tsx b/apps/docs/pages/index.tsx index 0d1dabc12..4832691f5 100644 --- a/apps/docs/pages/index.tsx +++ b/apps/docs/pages/index.tsx @@ -1,4 +1,4 @@ -import { Button } from "ui"; +import { Button } from 'ui'; export default function Docs() { return ( diff --git a/apps/web/.env b/apps/web/.env index f651adbd0..31d4bea3b 100644 --- a/apps/web/.env +++ b/apps/web/.env @@ -1,2 +1,2 @@ -VITE_BACKEND_URL=api.graphpolaris.com -VITE_STAGING=local +VITE_BACKEND_URL=https://api.graphpolaris.com +VITE_STAGING=local diff --git a/apps/web/.env.development b/apps/web/.env.development new file mode 100644 index 000000000..ddbadbf75 --- /dev/null +++ b/apps/web/.env.development @@ -0,0 +1,7 @@ +VITE_BACKEND_URL=http://localhost +VITE_BACKEND_WSS_URL=ws://localhost:3001/ +VITE_STAGING=dev +VITE_SKIP_LOGIN=true +VITE_BACKEND_USER=:3000 +VITE_BACKEND_QUERY=:3003 +VITE_BACKEND_SCHEMA=:3002 \ No newline at end of file diff --git a/apps/web/.env.production b/apps/web/.env.production index 895239e15..4c548bc85 100644 --- a/apps/web/.env.production +++ b/apps/web/.env.production @@ -1,2 +1,7 @@ -VITE_BACKEND_URL=api.graphpolaris.com -VITE_STAGING=prod \ No newline at end of file +VITE_BACKEND_URL=https://api.graphpolaris.com +VITE_BACKEND_WSS_URL=ws://api.graphpolaris.com/socket/ +VITE_STAGING=prod +VITE_SKIP_LOGIN=false +VITE_BACKEND_USER=/user +VITE_BACKEND_QUERY=/query +VITE_BACKEND_SCHEMA=/schema \ No newline at end of file diff --git a/apps/web/.eslintignore b/apps/web/.eslintignore index 3421a7d28..ebd538ec9 100644 --- a/apps/web/.eslintignore +++ b/apps/web/.eslintignore @@ -1,3 +1,4 @@ -node_modules/* -node_modules/ -node_modules +node_modules/* +node_modules/ +node_modules +*.d.ts \ No newline at end of file diff --git a/apps/web/node.d.ts b/apps/web/node.d.ts index 9c681853c..544d25be2 100644 --- a/apps/web/node.d.ts +++ b/apps/web/node.d.ts @@ -1,9 +1,18 @@ interface ImportMeta { - env: { - VITE_BACKEND_URL: string; - VITE_STAGING: string; - VITE_KEYCLOAK_URL: string; - VITE_KEYCLOAK_REALM: string; - VITE_KEYCLOAK_CLIENT: string; - } -} \ No newline at end of file + env: { + VITE_BACKEND_URL: string; + VITE_BACKEND_WSS_URL: string; + VITE_BACKEND_USER: string; + VITE_BACKEND_SCHEMA: string; + VITE_BACKEND_QUERY: string; + VITE_STAGING: string; + VITE_KEYCLOAK_URL: string; + VITE_KEYCLOAK_REALM: string; + VITE_KEYCLOAK_CLIENT: string; + }; +} + +declare module "*.png"; +declare module "*.svg"; +declare module "*.jpeg"; +declare module "*.jpg"; \ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index 9dc529d75..24c4e5bfc 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,48 +1,49 @@ -{ - "name": "web", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite --host local.graphpolaris.com --port 4200", - "dev2": "vite --host local.graphpolaris.com --port 4200", - "build": "tsc && vite build", - "preview": "vite preview", - "lint": "eslint *.ts*", - "test": "vitest run" - }, - "dependencies": { - "@graphpolaris/shared": "workspace:*", - "@mui/base": "5.0.0-alpha.118", - "@mui/icons-material": "^5.11.11", - "@mui/material": "^5.11.13", - "@reduxjs/toolkit": "^1.9.2", - "graphology": "^0.25.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-grid-layout": "^1.3.4", - "react-redux": "^8.0.5", - "react-router-dom": "^6.8.1", - "reactflow": "11.4.0-next.1", - "styled-components": "^5.3.6" - }, - "devDependencies": { - "@storybook/react": "7.0.0-rc.5", - "@testing-library/react": "14.0.0", - "@types/react": "^18.0.28", - "@types/react-dom": "^18.0.11", - "@types/react-grid-layout": "^1.3.2", - "@types/styled-components": "^5.1.26", - "@vitejs/plugin-basic-ssl": "^1.0.1", - "@vitejs/plugin-react-swc": "^3.0.0", - "autoprefixer": "^10.4.14", - "graphology-types": "^0.24.7", - "postcss": "^8.4.21", - "react-is": "^18.2.0", - "tailwindcss": "^3.3.1", - "typescript": "^4.9.3", - "vite": "^4.2.0", - "vite-plugin-dts": "^2.1.0", - "vitest": "^0.29.2" - } -} +{ + "name": "web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev2": "vite --host local.graphpolaris.com --port 4200", + "dev": "vite --port 4200", + "build": "tsc && vite build", + "build-dev": "tsc && vite build --mode development", + "preview": "vite preview", + "lint": "eslint *.ts*", + "test": "vitest run" + }, + "dependencies": { + "@graphpolaris/shared": "workspace:*", + "@mui/base": "5.0.0-alpha.118", + "@mui/icons-material": "^5.11.11", + "@mui/material": "^5.11.13", + "@reduxjs/toolkit": "^1.9.2", + "graphology": "^0.25.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-grid-layout": "^1.3.4", + "react-redux": "^8.0.5", + "react-router-dom": "^6.8.1", + "reactflow": "11.4.0-next.1", + "styled-components": "^5.3.6" + }, + "devDependencies": { + "@storybook/react": "7.0.0-rc.5", + "@testing-library/react": "14.0.0", + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "@types/react-grid-layout": "^1.3.2", + "@types/styled-components": "^5.1.26", + "@vitejs/plugin-basic-ssl": "^1.0.1", + "@vitejs/plugin-react-swc": "^3.0.0", + "autoprefixer": "^10.4.14", + "graphology-types": "^0.24.7", + "postcss": "^8.4.21", + "react-is": "^18.2.0", + "tailwindcss": "^3.3.1", + "typescript": "^4.9.3", + "vite": "^4.2.0", + "vite-plugin-dts": "^2.1.0", + "vitest": "^0.29.2" + } +} diff --git a/apps/web/src/app/app.module.scss b/apps/web/src/app/app.module.scss index b04f78a7d..1dc1ca7dd 100644 --- a/apps/web/src/app/app.module.scss +++ b/apps/web/src/app/app.module.scss @@ -1,8 +1,8 @@ /* Your styles goes here. */ .mainContainer { - padding: 2.5rem; - gap: 1rem; + padding: 0.8rem; + gap: 0.7rem; height: 100%; display: flex; flex-direction: row; @@ -11,10 +11,13 @@ max-width: 33%; height: 100%; max-height: 100%; + h1 { + margin-left: 0.8em; + } } .panel { display: flex; - gap: 1rem; + gap: 0.7rem; width: 100%; max-width: 67%; flex-direction: column; @@ -24,14 +27,18 @@ height: 100%; max-height: 70%; overflow-y: clip; - border-radius: 8px; h1 { - margin-left: 2em; + margin-left: 0.8em; } } .queryBuilder { height: 100%; max-height: 30%; + overflow-y: clip; + h1 { + margin-top: 0.25em; + margin-left: 0.8em; + } } } } diff --git a/apps/web/src/app/app.tsx b/apps/web/src/app/app.tsx index eb99a9a08..c616b6452 100644 --- a/apps/web/src/app/app.tsx +++ b/apps/web/src/app/app.tsx @@ -7,44 +7,39 @@ import { useQuerybuilderGraph, useQuerybuilderHash, useSchemaAPI, - useSessionCache + useSessionCache, } from '@graphpolaris/shared/lib/data-access'; import { WebSocketHandler } from '@graphpolaris/shared/lib/data-access/socket'; import Broker from '@graphpolaris/shared/lib/data-access/socket/broker'; +import { assignNewGraphQueryResult, useAppDispatch } from '@graphpolaris/shared/lib/data-access/store'; import { - assignNewGraphQueryResult, - useAppDispatch, -} from '@graphpolaris/shared/lib/data-access/store'; -import { GraphQueryResultFromBackend, resetGraphQueryResults } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice'; -import { - Query2BackendQuery, - QueryBuilder, -} from '@graphpolaris/shared/lib/querybuilder'; + GraphQueryResultFromBackend, + GraphQueryResultFromBackendPayload, + resetGraphQueryResults, +} from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice'; +import { Query2BackendQuery, QueryBuilder } from '@graphpolaris/shared/lib/querybuilder'; import { Schema } from '@graphpolaris/shared/lib/schema/panel'; -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { Navbar } from '../components/navbar/navbar'; import Panel from '../components/panels/panel'; -import { domain } from '../environments/variables'; import { VisualizationPanel } from './panels/Visualization'; import styles from './app.module.scss'; import { logout } from '@graphpolaris/shared/lib/data-access/store/authSlice'; import { SchemaFromBackend } from '@graphpolaris/shared/lib/schema'; -export interface App { - styles: any; -} +export interface App {} export function App(props: App) { const isLogin = useAuth(); - const auth = useAuthorizationCache() - const api = useDatabaseAPI(domain); - const api_schema = useSchemaAPI(domain); - const api_query = useQueryAPI(domain); + const auth = useAuthorizationCache(); + const api = useDatabaseAPI(); + const api_schema = useSchemaAPI(); + const api_query = useQueryAPI(); const dispatch = useAppDispatch(); const session = useSessionCache(); const query = useQuerybuilderGraph(); const queryHash = useQuerybuilderHash(); - const ws = useRef(new WebSocketHandler(domain)); + const ws = useRef(new WebSocketHandler(import.meta.env.VITE_BACKEND_WSS_URL)); const [authCheck, setAuthCheck] = useState(false); // for testing purposes @@ -54,16 +49,9 @@ export function App(props: App) { useEffect(() => { // Default - Broker.instance().subscribe( - (data: SchemaFromBackend) => dispatch(readInSchemaFromBackend(data)), - 'schema_result' - ); + Broker.instance().subscribe((data: SchemaFromBackend) => dispatch(readInSchemaFromBackend(data)), 'schema_result'); - Broker.instance().subscribe( - (data: GraphQueryResultFromBackend) => - dispatch(assignNewGraphQueryResult(data)), - 'query_result' - ); + Broker.instance().subscribe((data: GraphQueryResultFromBackendPayload) => dispatch(assignNewGraphQueryResult(data)), 'query_result'); return () => { Broker.instance().unSubscribeAll('schema_result'); @@ -82,33 +70,33 @@ export function App(props: App) { useEffect(() => { // Newly (un)authorized + // console.log('Auth changed', auth.authorized, isLogin); if (auth.authorized) { - console.info("App is authorized; Getting Datatabases", isLogin); + console.debug('App is authorized; Getting Databases', isLogin); api.GetAllDatabases({ updateSessionCache: true }); setAuthCheck(true); } else { - dispatch(logout()) + // dispatch(logout()); } - }, [isLogin]); + }, [auth]); - useEffect(() => { - // New query + const runQuery = () => { if (session?.currentDatabase && query) { if (query.edges.length === 0) { dispatch(resetGraphQueryResults()); - } else + } else { api_query.execute(Query2BackendQuery(session.currentDatabase, query)); + } } + }; + + useEffect(() => { + runQuery(); }, [queryHash]); return ( <div className="h-screen w-screen"> - <div - className={ - 'flex h-screen w-screen overflow-hidden ' + - (!auth.authorized ? 'blur-sm pointer-events-none ' : '') - } - > + <div className={'flex h-screen w-screen overflow-hidden ' + (!auth.authorized ? 'blur-sm pointer-events-none ' : '')}> <div className="h-full relative flex flex-col flex-1 overflow-y-auto overflow-x-hidden"> <div className="h-fit flex-grow-0"> <Navbar /> @@ -121,11 +109,19 @@ export function App(props: App) { </div> <div className={styles.panel}> <div className={styles.visualization}> - <VisualizationPanel /> + <Panel content="Visualization Panel"> + <VisualizationPanel /> + </Panel> </div> <div className={styles.queryBuilder}> <Panel content="Query Panel"> - <QueryBuilder /> + <QueryBuilder + onRunQuery={() => { + console.log('Run Query'); + + runQuery(); + }} + /> </Panel> </div> </div> diff --git a/apps/web/src/app/panels/Visualization.tsx b/apps/web/src/app/panels/Visualization.tsx index 1b38008d9..10f7820cc 100644 --- a/apps/web/src/app/panels/Visualization.tsx +++ b/apps/web/src/app/panels/Visualization.tsx @@ -1,15 +1,12 @@ -import { RawJSONVis, NodeLinkVis } from '@graphpolaris/shared/lib/vis'; +import { RawJSONVis, NodeLinkVis, PaohVis, SemanticSubstrates } from '@graphpolaris/shared/lib/vis'; import Panel from '../../components/panels/panel'; -import { - assignNewGraphQueryResult, - useAppDispatch, -} from '@graphpolaris/shared/lib/data-access'; +import { assignNewGraphQueryResult, useAppDispatch } from '@graphpolaris/shared/lib/data-access'; export const VisualizationPanel = () => { const dispatch = useAppDispatch(); return ( - <Panel content="Visualization Panel"> + <div> {/* <div> <button onClick={() => @@ -36,12 +33,24 @@ export const VisualizationPanel = () => { Remove mock result </button> </div> */} - <div className="h-[48rem] overflow-y-auto"> + + {/* width: '83%', + height: '95vh', */} + <div className="h-[60vh] overflow-y-auto"> + {/* <div className="h-full overflow-y-auto"> */} + {/* <RawJSONVis /> */} + + {/* <PaohVis + rowHeight={30} + hyperedgeColumnWidth={30} + gapBetweenRanges={3} + /> */} + {/* <RawJSONVis /> */} {/* <RawJSONVis /> */} <NodeLinkVis /> + {/* <SemanticSubstrates /> */} </div> - {/* <SemanticSubstrates /> */} {/* <div /> */} - </Panel> + </div> ); }; diff --git a/apps/web/src/components/navbar/AddDatabaseForm/index.tsx b/apps/web/src/components/navbar/AddDatabaseForm/index.tsx index c13df46c3..8df366cd4 100644 --- a/apps/web/src/components/navbar/AddDatabaseForm/index.tsx +++ b/apps/web/src/components/navbar/AddDatabaseForm/index.tsx @@ -11,11 +11,7 @@ import React, { useState, useEffect } from 'react'; import { TextField, Button, NativeSelect } from '@mui/material'; import styles from './add-database-form.module.scss'; -import { - AddDatabaseRequest, - DatabaseType, - databaseNameMapping, -} from '@graphpolaris/shared/lib/data-access'; +import { AddDatabaseRequest, DatabaseType, databaseNameMapping, databaseProtocolMapping } from '@graphpolaris/shared/lib/data-access'; /** AddDatabaseFormProps is an interface containing the AuthViewModel. */ export interface AddDatabaseFormProps { @@ -48,11 +44,20 @@ export default function AddDatabaseForm(props: AddDatabaseFormProps) { // internal_database_name: 'TweedeKamer', // type: DatabaseType.ArangoDB, + // username: 'neo4j', + // password: 'oL3nNlebrx4le2A0zxaFVqAo3HAvodHxwEiI_7_2JxI', + // url: '635176c8.databases.neo4j.io', + // port: 7687, + // name: 'neo4j', + // internal_database_name: 'neo4j', + // type: DatabaseType.Neo4j, + username: 'neo4j', - password: 'oL3nNlebrx4le2A0zxaFVqAo3HAvodHxwEiI_7_2JxI', - url: '635176c8.databases.neo4j.io', + password: 'StrongPass2022', + url: 'localhost', port: 7687, name: 'neo4j', + protocol: 'neo4j://', internal_database_name: 'neo4j', type: DatabaseType.Neo4j, }); @@ -143,6 +148,24 @@ export default function AddDatabaseForm(props: AddDatabaseFormProps) { ))} </NativeSelect> </div> + <div className={styles.loginContainer}> + <NativeSelect + className={styles.passLabel} + value={state.protocol} + onChange={(event) => { + setState({ + ...state, + protocol: event.currentTarget.value, + }); + }} + > + {databaseProtocolMapping.map((protocol) => ( + <option value={protocol} key={protocol}> + {protocol} + </option> + ))} + </NativeSelect> + </div> <div className={styles.loginContainerRow}> <TextField className={styles.hostLabel} @@ -164,9 +187,7 @@ export default function AddDatabaseForm(props: AddDatabaseFormProps) { label="Port error" type="port" value={state.port} - onChange={(event) => - handlePortChanged(event.currentTarget.value) - } + onChange={(event) => handlePortChanged(event.currentTarget.value)} required /> ) : ( @@ -175,9 +196,7 @@ export default function AddDatabaseForm(props: AddDatabaseFormProps) { label="Port" type="port" value={state.port} - onChange={(event) => - handlePortChanged(event.currentTarget.value) - } + onChange={(event) => handlePortChanged(event.currentTarget.value)} required /> )} @@ -229,12 +248,7 @@ export default function AddDatabaseForm(props: AddDatabaseFormProps) { </div> <div className={styles.loginContainerButton}> {portValidation && portValidation == 'error' ? ( - <Button - disabled - variant="contained" - type="submit" - color="success" - > + <Button disabled variant="contained" type="submit" color="success"> Submit </Button> ) : ( diff --git a/apps/web/src/components/navbar/AddDatabaseForm/stories/add-database-form.stories.tsx b/apps/web/src/components/navbar/AddDatabaseForm/stories/add-database-form.stories.tsx index c1c7dd4c3..446dbf80a 100644 --- a/apps/web/src/components/navbar/AddDatabaseForm/stories/add-database-form.stories.tsx +++ b/apps/web/src/components/navbar/AddDatabaseForm/stories/add-database-form.stories.tsx @@ -1,8 +1,4 @@ -import { - AddDatabaseRequest, - GraphPolarisThemeProvider, - store, -} from '@graphpolaris/shared/lib/data-access'; +import { AddDatabaseRequest, GraphPolarisThemeProvider, store } from '@graphpolaris/shared/lib/data-access'; import AddDatabaseForm, { AddDatabaseFormProps } from '../index'; import { Provider } from 'react-redux'; import { Meta } from '@storybook/react'; diff --git a/apps/web/src/components/navbar/navbar.tsx b/apps/web/src/components/navbar/navbar.tsx index b227e527e..9e1fe7716 100644 --- a/apps/web/src/components/navbar/navbar.tsx +++ b/apps/web/src/components/navbar/navbar.tsx @@ -9,17 +9,7 @@ * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ import React, { useEffect, useState } from 'react'; -import { - AppBar, - Toolbar, - CssBaseline, - Typography, - MenuItem, - ListItemText, - Menu, - IconButton, - Button, -} from '@mui/material'; +import { AppBar, Toolbar, CssBaseline, Typography, MenuItem, ListItemText, Menu, IconButton, Button } from '@mui/material'; import { AccountCircle } from '@mui/icons-material'; import logo from './logogp.png'; import logo_white from './logogpwhite.png'; @@ -27,10 +17,7 @@ import AddDatabaseForm from './AddDatabaseForm'; import { useTheme } from '@mui/material/styles'; import styles from './navbar.module.scss'; -import { - updateCurrentDatabase, - updateDatabaseList, -} from '@graphpolaris/shared/lib/data-access/store/sessionSlice'; +import { updateCurrentDatabase, updateDatabaseList } from '@graphpolaris/shared/lib/data-access/store/sessionSlice'; import { AddDatabaseRequest, useAppDispatch, @@ -39,7 +26,6 @@ import { useSchemaAPI, useSessionCache, } from '@graphpolaris/shared/lib/data-access'; -import { domain } from '../../environments/variables'; /** NavbarComponentProps is an interface containing the NavbarViewModel. */ export interface NavbarComponentProps { @@ -67,12 +53,12 @@ export const Navbar = (props: NavbarComponentProps) => { const theme = useTheme(); const auth = useAuthorizationCache(); const session = useSessionCache(); - const api = useDatabaseAPI(domain); - const schemaApi = useSchemaAPI(domain); + const api = useDatabaseAPI(); + const schemaApi = useSchemaAPI(); const dispatch = useAppDispatch(); useEffect(() => { - console.log(auth); + // console.log(auth); }, [auth.accessToken]); // const { navbarViewModel, currentColours } = props; @@ -129,14 +115,10 @@ export const Navbar = (props: NavbarComponentProps) => { /** * Called when the user clicks on the 'submit' button of the add database form. */ - function onAddDatabaseFormSubmit( - request: AddDatabaseRequest - ): Promise<void | Response> { - return api - .AddDatabase(request, { updateDatabaseCache: true, setAsCurrent: true }) - .then(() => { - schemaApi.RequestSchema(request.name); - }); + function onAddDatabaseFormSubmit(request: AddDatabaseRequest): Promise<void | Response> { + return api.AddDatabase(request, { updateDatabaseCache: true, setAsCurrent: true }).then(() => { + schemaApi.RequestSchema(request.name); + }); } const currentLogo = theme.palette.custom.logo == 'white' ? logo_white : logo; @@ -167,11 +149,7 @@ export const Navbar = (props: NavbarComponentProps) => { > Change Palette </Button> */} - <Button - href="https://graphpolaris.com/" - className={styles.menuText} - style={{ color: theme.palette.custom.menuText }} - > + <Button href="https://graphpolaris.com/" className={styles.menuText} style={{ color: theme.palette.custom.menuText }}> Home </Button> <Button @@ -181,11 +159,7 @@ export const Navbar = (props: NavbarComponentProps) => { > Products </Button> - <Button - href="https://graphpolaris.com#usecases" - className={styles.menuText} - style={{ color: theme.palette.custom.menuText }} - > + <Button href="https://graphpolaris.com#usecases" className={styles.menuText} style={{ color: theme.palette.custom.menuText }}> Use Cases </Button> <Button @@ -252,10 +226,7 @@ export const Navbar = (props: NavbarComponentProps) => { }) } > - <ListItemText - onClick={() => openChangeSubMenu()} - primary={'Change database'} - /> + <ListItemText onClick={() => openChangeSubMenu()} primary={'Change database'} /> </MenuItem> <Menu id="databases-menus" @@ -298,10 +269,7 @@ export const Navbar = (props: NavbarComponentProps) => { }) } > - <ListItemText - onClick={() => openDeleteSubMenu()} - primary={'Delete database'} - /> + <ListItemText onClick={() => openDeleteSubMenu()} primary={'Delete database'} /> </MenuItem> <Menu id="delete-databases-menus" diff --git a/apps/web/src/components/panels/panel.tsx b/apps/web/src/components/panels/panel.tsx index e4d9af086..93ca8bd86 100644 --- a/apps/web/src/components/panels/panel.tsx +++ b/apps/web/src/components/panels/panel.tsx @@ -12,7 +12,8 @@ const Wrapper = styled.div<{ color: string }>` font-family: 'Arial'; // Light shadow - box-shadow: 0 3px 10px rgb(0 0 0 / 0.2); + // box-shadow: 0 3px 10px rgb(0 0 0 / 0.2); + border: 1px solid #dddddd; height: 100%; width: 100%; @@ -20,14 +21,13 @@ const Wrapper = styled.div<{ color: string }>` display: flex; justify-content: center; align-items: center; - border-radius: 8px; `; const Content = styled.div` - margin: 2em; + margin: 0.8em; border-radius: 8px; width: 100%; - height: 95%; + height: 98%; overflow: hidden; `; diff --git a/apps/web/src/environments/variables.ts b/apps/web/src/environments/variables.ts deleted file mode 100644 index f1a86d7fb..000000000 --- a/apps/web/src/environments/variables.ts +++ /dev/null @@ -1 +0,0 @@ -export const domain = import.meta.env.VITE_BACKEND_URL; \ No newline at end of file diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 483b0d21b..b6d8c226a 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -16,8 +16,6 @@ const domNode = document.getElementById('root'); if (domNode) { const root = createRoot(domNode); - - console.log(store); root.render( <Provider store={store}> <GraphPolarisThemeProvider> @@ -29,6 +27,6 @@ if (domNode) { </Routes> </Router> </GraphPolarisThemeProvider> - </Provider> + </Provider>, ); } diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 3dae4981c..e3f42121e 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,30 +1,30 @@ -{ - "compilerOptions": { - "jsx": "react-jsx", - "allowJs": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "baseUrl": ".", - "paths": { - "@graphpolaris/shared/lib/*": ["../../libs/shared/lib/*"] - } - }, - "exclude": ["node_modules", "public"], - "include": ["src", "../../libs/shared/lib/querybuilder/panel/attributepill"], - "files": ["./node.d.ts"], - "references": [{ "path": "./tsconfig.node.json" }] -} +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "baseUrl": ".", + "paths": { + "@graphpolaris/shared/lib/*": ["../../libs/shared/lib/*"] + } + }, + "exclude": ["node_modules", "public"], + "include": ["src", "../../libs/shared/lib/querybuilder/panel/attributepill"], + "files": ["./node.d.ts"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index bc88eb31f..44c0c7d60 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -2,24 +2,20 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react-swc'; import path from 'path'; -import basicSsl from '@vitejs/plugin-basic-ssl'; import dts from 'vite-plugin-dts'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ react(), - basicSsl(), + // basicSsl(), dts({ insertTypesEntry: true, }), ], resolve: { alias: { - '@graphpolaris/shared/lib': path.resolve( - __dirname, - '../../libs/shared/lib' - ), + '@graphpolaris/shared/lib': path.resolve(__dirname, '../../libs/shared/lib'), }, }, }); diff --git a/libs/shared/.env b/libs/shared/.env deleted file mode 100644 index 25874d424..000000000 --- a/libs/shared/.env +++ /dev/null @@ -1,3 +0,0 @@ -VITE_KEYCLOAK_URL=https://login.graphpolaris.com/ -VITE_KEYCLOAK_REALM=graphpolaris -VITE_KEYCLOAK_CLIENT=web \ No newline at end of file diff --git a/libs/shared/.eslintignore b/libs/shared/.eslintignore index 3421a7d28..86439d7da 100644 --- a/libs/shared/.eslintignore +++ b/libs/shared/.eslintignore @@ -1,3 +1,4 @@ -node_modules/* -node_modules/ -node_modules +node_modules/* +node_modules/ +node_modules +*.d.ts diff --git a/libs/shared/lib/data-access/api/database.ts b/libs/shared/lib/data-access/api/database.ts index 090486994..11caee2a6 100644 --- a/libs/shared/lib/data-access/api/database.ts +++ b/libs/shared/lib/data-access/api/database.ts @@ -1,7 +1,7 @@ // All database related API calls -import { useAppDispatch, useAuthorizationCache, useSessionCache } from "../store"; -import { updateCurrentDatabase, updateDatabaseList } from "../store/sessionSlice"; +import { useAppDispatch, useAuthorizationCache, useSessionCache } from '../store'; +import { updateCurrentDatabase, updateDatabaseList } from '../store/sessionSlice'; export enum DatabaseType { ArangoDB = 0, @@ -9,58 +9,57 @@ export enum DatabaseType { } export const databaseNameMapping: string[] = ['arangodb', 'neo4j']; +export const databaseProtocolMapping: string[] = ['neo4j://', 'neo4j+s://']; export type AddDatabaseRequest = { name: string; internal_database_name: string; url: string; port: number; + protocol: string; username: string; password: string; type: DatabaseType; // Database type. 0 = ArangoDB, 1 = Neo4j }; export type AddDatabaseOptions = { - setAsCurrent?: boolean, updateDatabaseCache?: boolean -} + setAsCurrent?: boolean; + updateDatabaseCache?: boolean; +}; export type GetDatabasesOptions = { - updateSessionCache?: boolean -} + updateSessionCache?: boolean; +}; export type DeleteDatabasesOptions = { - updateSessionCache?: boolean -} + updateSessionCache?: boolean; +}; -export const useDatabaseAPI = (domain: string) => { +export const useDatabaseAPI = () => { const { accessToken } = useAuthorizationCache(); const cache = useSessionCache(); const dispatch = useAppDispatch(); + const domain = import.meta.env.VITE_BACKEND_URL; + const useruri = import.meta.env.VITE_BACKEND_USER; - function AddDatabase( - request: AddDatabaseRequest, - options: AddDatabaseOptions = {} - ): Promise<void> { + function AddDatabase(request: AddDatabaseRequest, options: AddDatabaseOptions = {}): Promise<void> { const { setAsCurrent = true, updateDatabaseCache = false } = options; return new Promise((resolve, reject) => { - fetch(`https://${domain}/user/database`, { + fetch(`${domain}${useruri}/database`, { method: 'POST', credentials: 'same-origin', headers: new Headers({ - Authorization: - 'Bearer ' + accessToken, + Authorization: 'Bearer ' + accessToken, }), body: JSON.stringify(request), }).then((response: Response) => { - console.log(response); + console.info('Added Database', response); if (!response.ok) { reject(response.statusText); } - if (setAsCurrent) - dispatch(updateCurrentDatabase(request.name)); - if (updateDatabaseCache) - GetAllDatabases({ updateSessionCache: true }); + if (setAsCurrent) dispatch(updateCurrentDatabase(request.name)); + if (updateDatabaseCache) GetAllDatabases({ updateSessionCache: true }); resolve(); }); @@ -70,15 +69,14 @@ export const useDatabaseAPI = (domain: string) => { async function GetAllDatabases(options: GetDatabasesOptions = {}): Promise<Array<string>> { const { updateSessionCache: updateDatabaseCache = true } = options; // console.log(accessToken); - const response = await fetch(`https://${domain}/user/database`, { + const response = await fetch(`${domain}${useruri}/database`, { method: 'GET', credentials: 'same-origin', headers: new Headers({ - Authorization: - 'Bearer ' + accessToken, + Authorization: 'Bearer ' + accessToken, }), }); - console.log(response); + // console.log(response); if (!response.ok) { const text = await response.text(); @@ -87,33 +85,30 @@ export const useDatabaseAPI = (domain: string) => { // throw Error(response.statusText) } const json = await response.json(); - if (updateDatabaseCache) - dispatch(updateDatabaseList(json.databases)); + if (updateDatabaseCache) dispatch(updateDatabaseList(json.databases)); return json.databases; } function DeleteDatabase(name: string, options: DeleteDatabasesOptions = {}): Promise<void> { const { updateSessionCache: updateDatabaseCache = true } = options; return new Promise((resolve, reject) => { - fetch(`https://${domain}/user/database/` + name, { + fetch(`${domain}${useruri}/database/` + name, { method: 'DELETE', credentials: 'same-origin', headers: new Headers({ - Authorization: - 'Bearer ' + accessToken, + Authorization: 'Bearer ' + accessToken, }), }).then((response: Response) => { if (!response.ok) { reject(response.statusText); } - if (updateDatabaseCache) - GetAllDatabases({ updateSessionCache: true }); + if (updateDatabaseCache) GetAllDatabases({ updateSessionCache: true }); resolve(); }); }); } - return { DatabaseType, AddDatabase, GetAllDatabases, DeleteDatabase } + return { DatabaseType, AddDatabase, GetAllDatabases, DeleteDatabase }; }; diff --git a/libs/shared/lib/data-access/api/index.ts b/libs/shared/lib/data-access/api/index.ts index a378760c1..d2e1deca1 100644 --- a/libs/shared/lib/data-access/api/index.ts +++ b/libs/shared/lib/data-access/api/index.ts @@ -1,4 +1,4 @@ -export * from './database' -export * from './user' -export * from './schema' -export * from './query' \ No newline at end of file +export * from './database'; +export * from './user'; +export * from './schema'; +export * from './query'; diff --git a/libs/shared/lib/data-access/api/query.ts b/libs/shared/lib/data-access/api/query.ts index 461ab0d04..1aad449bf 100644 --- a/libs/shared/lib/data-access/api/query.ts +++ b/libs/shared/lib/data-access/api/query.ts @@ -1,52 +1,51 @@ // All database related API calls -import { BackendQueryFormat } from "../../querybuilder/model/BackendQueryFormat"; -import { useAuthorizationCache, useSessionCache } from "../store"; - -export const useQueryAPI = (domain: string) => { - const cache = useSessionCache(); - const { accessToken } = useAuthorizationCache(); - - async function execute(query: BackendQueryFormat) { - - const response = await fetch(`https://${domain}/query/execute/`, { - method: 'POST', - credentials: 'same-origin', - headers: new Headers({ - Authorization: - 'Bearer ' + accessToken, - }), - body: JSON.stringify(query) - }); - - if (!response?.ok) { - const ret = await response.text(); - console.error(response, ret); - } - const ret = await response.json(); - console.log('Sent Query EXECUTION', ret); +import { BackendQueryFormat } from '../../querybuilder/model/BackendQueryFormat'; +import { useAuthorizationCache, useSessionCache } from '../store'; + +export const useQueryAPI = () => { + const cache = useSessionCache(); + const { accessToken } = useAuthorizationCache(); + const domain = import.meta.env.VITE_BACKEND_URL; + const query_url = import.meta.env.VITE_BACKEND_QUERY; + + async function execute(query: BackendQueryFormat) { + const response = await fetch(`${domain}${query_url}/execute/`, { + method: 'POST', + credentials: 'same-origin', + headers: new Headers({ + Authorization: 'Bearer ' + accessToken, + }), + body: JSON.stringify(query), + }); + + if (!response?.ok) { + const ret = await response.text(); + console.error(response, ret); + return; } - - async function retrieveCachedQuery(queryID: string) { - // TODO: check if this method is needed! - - // const response = await fetch(`https://${domain}/query/retrieve-cached/`, { - // method: 'POST', - // credentials: 'same-origin', - // headers: new Headers({ - // Authorization: - // 'Bearer ' + accessToken, - // }), - // body: JSON.stringify({ queryID }) - // }); - - // if (!response?.ok) { - // const ret = await response.text(); - // console.error(response, ret); - // } - // // const ret = await response.json(); - // console.log(response); - } - - return { execute, retrieveCachedQuery }; + const ret = await response.json(); + console.debug('Sent Query EXECUTION', ret); + } + + async function retrieveCachedQuery(queryID: string) { + // TODO: check if this method is needed! + // const response = await fetch(`${domain}/query/retrieve-cached/`, { + // method: 'POST', + // credentials: 'same-origin', + // headers: new Headers({ + // Authorization: + // 'Bearer ' + accessToken, + // }), + // body: JSON.stringify({ queryID }) + // }); + // if (!response?.ok) { + // const ret = await response.text(); + // console.error(response, ret); + // } + // // const ret = await response.json(); + // console.log(response); + } + + return { execute, retrieveCachedQuery }; }; diff --git a/libs/shared/lib/data-access/api/schema.ts b/libs/shared/lib/data-access/api/schema.ts index 2a4e00b4c..417b60783 100644 --- a/libs/shared/lib/data-access/api/schema.ts +++ b/libs/shared/lib/data-access/api/schema.ts @@ -1,41 +1,40 @@ // All database related API calls -import { useAuthorizationCache, useSessionCache } from "../store"; - -export const useSchemaAPI = (domain: string) => { - const cache = useSessionCache(); - const { accessToken } = useAuthorizationCache(); - - - async function RequestSchema(databaseName?: string) { - if (!databaseName) databaseName = cache.currentDatabase; - if (!databaseName) { - throw Error('Must call with a database name'); - } - - const request = { - databaseName, - cached: true, - }; - - const response = await fetch(`https://${domain}/schema/`, { - method: 'POST', - credentials: 'same-origin', - headers: new Headers({ - Authorization: - 'Bearer ' + accessToken, - }), - body: JSON.stringify(request) - }); - - if (!response?.ok) { - const ret = await response.text(); - console.error(response, ret); - } - // const ret = await response.json(); - console.log(response); +import { useAuthorizationCache, useSessionCache } from '../store'; + +export const useSchemaAPI = () => { + const cache = useSessionCache(); + const { accessToken } = useAuthorizationCache(); + const domain = import.meta.env.VITE_BACKEND_URL; + const schema = import.meta.env.VITE_BACKEND_SCHEMA; + + async function RequestSchema(databaseName?: string) { + if (!databaseName) databaseName = cache.currentDatabase; + if (!databaseName) { + throw Error('Must call with a database name'); + } + const request = { + databaseName, + cached: true, + }; + + const response = await fetch(`${domain}${schema}/`, { + method: 'POST', + credentials: 'same-origin', + headers: new Headers({ + Authorization: 'Bearer ' + accessToken, + }), + body: JSON.stringify(request), + }); + + if (!response?.ok) { + const ret = await response.text(); + console.error(response, ret); } + // const ret = await response.json(); + // console.debug('Schema Requested', response); + } - return { RequestSchema }; + return { RequestSchema }; }; diff --git a/libs/shared/lib/data-access/api/user.ts b/libs/shared/lib/data-access/api/user.ts index 30a2a6f2a..5e4cd7e3d 100644 --- a/libs/shared/lib/data-access/api/user.ts +++ b/libs/shared/lib/data-access/api/user.ts @@ -1,6 +1,6 @@ // All user related API calls -import { useAuthorizationCache } from "../store"; +import { useAuthorizationCache } from '../store'; export type User = { Name: string; @@ -8,12 +8,13 @@ export type User = { SignInProvider: number; }; -export const useUserAPI = (domain: string) => { +export const useUserAPI = () => { const { accessToken } = useAuthorizationCache(); + const domain = import.meta.env.VITE_BACKEND_URL; function GetUserInfo(): Promise<User> { return new Promise<User>((resolve, reject) => { - fetch(`https://${domain}/user/`, { + fetch(`${domain}/user/`, { method: 'GET', credentials: 'same-origin', headers: new Headers({ @@ -37,6 +38,5 @@ export const useUserAPI = (domain: string) => { }); } - return { GetUserInfo }; }; diff --git a/libs/shared/lib/data-access/authorization/useAuth.jsx b/libs/shared/lib/data-access/authorization/useAuth.jsx index 644abac87..25e0643c6 100644 --- a/libs/shared/lib/data-access/authorization/useAuth.jsx +++ b/libs/shared/lib/data-access/authorization/useAuth.jsx @@ -6,13 +6,9 @@ import { authorized } from '../store/authSlice'; export const useAuth = () => { const keycloak = new Keycloak({ - // url: import.meta.env.VITE_KEYCLOAK_URL, - // realm: import.meta.env.VITE_KEYCLOAK_REALM, - // clientId: import.meta.env.VITE_KEYCLOAK_CLIENT, - //TODO: remove this hardcode - url: 'https://keycloak.graphpolaris.com', - realm: 'graphpolaris', - clientId: 'web', + url: import.meta.env.VITE_KEYCLOAK_URL || 'https://keycloak.graphpolaris.com', + realm: import.meta.env.VITE_KEYCLOAK_REALM || 'graphpolaris', + clientId: import.meta.env.VITE_KEYCLOAK_CLIENT || 'web', }); const isRun = useRef(false); @@ -20,8 +16,21 @@ export const useAuth = () => { const dispatch = useAppDispatch(); useEffect(() => { - if (isRun.current) return; + if (import.meta.env.VITE_SKIP_LOGIN) { + console.warn('skipping login'); + setLogin(true); + dispatch( + authorized({ + userID: 'userID', + sessionID: 'sessionID', + accessToken: 'accessToken', + authorized: true, + }) + ); + return; + } + if (isRun.current) return; isRun.current = true; keycloak .init({ @@ -32,11 +41,9 @@ export const useAuth = () => { .then(async (isAuthenticated) => { // console.log("useAuth useEffect", isAuthenticated, keycloak.idTokenParsed); setLogin(isAuthenticated); - // just for example here: // const profile = await getUserProfile(); // console.log("useAuth useEffect profile", profile); - await dispatch( authorized({ // Info from https://www.keycloak.org/docs/latest/securing_apps/ diff --git a/libs/shared/lib/data-access/index.ts b/libs/shared/lib/data-access/index.ts index 9b260fdbe..2706d3a0f 100644 --- a/libs/shared/lib/data-access/index.ts +++ b/libs/shared/lib/data-access/index.ts @@ -1,4 +1,4 @@ -export * from './api' -export * from './authorization' -export * from './store' -export * from './theme' \ No newline at end of file +export * from './api'; +export * from './authorization'; +export * from './store'; +export * from './theme'; diff --git a/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/DatabaseRepository.tsx b/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/DatabaseRepository.tsx deleted file mode 100644 index e1d3a49b2..000000000 --- a/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/DatabaseRepository.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -/** Contains interface for adding user databases and fetching the connected databases */ -export type Database = { - // The user specified database, can be anything - databaseName: string; - username: string; - password: string; - port: number; - hostname: string; - // The database for the internal database - internalDatabaseName: string; - // The database type vendor, currently only arangodb and neo4j are supported - databaseType: string; - // Password for speficied databasehost -}; - -// Interface for a database api repository -export default interface DatabaseRepository { - /** - * Adds a database for the currently logged in user. - * @param database {Database} The database to add for the user. - * @returns A promise with a standard Response. - */ - addDatabase(database: Database): Promise<Response>; - - /** - * Fetches the currently connected database of the user. - * @returns The database names. - */ - fetchUserDatabases(): Promise<string[]>; -} diff --git a/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/QueryRepository.tsx b/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/QueryRepository.tsx deleted file mode 100644 index 244a61014..000000000 --- a/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/QueryRepository.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { TranslatedJSONQuery } from './TranslatedJSONQuery'; - -/** A type for a query to send to the backend. */ -export interface Query extends TranslatedJSONQuery { - databaseName: string; -} - -export default interface QueryRepository { - /** - * Sends a query to the database to execute. - * @param query {Query} The query to execute. - * @returns A promise with the assigned queryID. - */ - sendQuery(query: Query): Promise<string>; - - /** - * Sends a message to the backend to retrieve a cached query - * @param queryID {string} The id of the query to retrieve. - */ - retrieveCachedQuery(queryID: string): Promise<Response>; -} diff --git a/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/TranslatedJSONQuery.tsx b/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/TranslatedJSONQuery.tsx deleted file mode 100644 index 7d645f745..000000000 --- a/libs/shared/lib/data-access/socket/DELETE-repository-interfaces/TranslatedJSONQuery.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -/** JSON query format used to send a query to the backend. */ -export interface TranslatedJSONQuery { - return: { - entities: number[]; - relations: number[]; - groupBys: number[]; - }; - entities: Entity[]; - relations: Relation[]; - groupBys: GroupBy[]; - machineLearning: MachineLearning[]; - limit: number; -} - -/** Interface for an entity in the JSON for the query. */ -export interface Entity { - name: string; - ID: number; - constraints: Constraint[]; -} - -/** Interface for an relation in the JSON for the query. */ -export interface Relation { - name: string; - ID: number; - fromType: string; - fromID: number; - toType: string; - toID: number; - depth: { min: number; max: number }; - constraints: Constraint[]; -} - -/** - * Constraint datatypes created from the attributes of a relation or entity. - * - * string MatchTypes: exact/contains/startswith/endswith. - * int MatchTypes: GT/LT/EQ. - * bool MatchTypes: EQ/NEQ. - */ -export interface Constraint { - attribute: string; - dataType: string; - - matchType: string; - value: string; -} - -/** Interface for a function in the JSON for the query. */ -export interface GroupBy { - ID: number; - - groupType: string; - groupID: number[]; - groupAttribute: string; - - byType: string; - byID: number[]; - byAttribute: string; - - appliedModifier: string; - relationID: number; - constraints: Constraint[]; -} - -/** Interface for Machine Learning algorithm */ -export interface MachineLearning { - ID?: number; - queuename: string; - parameters: string[]; -} -/** Interface for what the JSON needs for link predicition */ -export interface LinkPrediction { - queuename: string; - parameters: { - key: string; - value: string; - }[]; -} diff --git a/libs/shared/lib/data-access/socket/backend-message-receiver/BackendMessageReceiverMock.tsx b/libs/shared/lib/data-access/socket/backend-message-receiver/BackendMessageReceiverMock.tsx index 3bb7c4480..5d5b90108 100644 --- a/libs/shared/lib/data-access/socket/backend-message-receiver/BackendMessageReceiverMock.tsx +++ b/libs/shared/lib/data-access/socket/backend-message-receiver/BackendMessageReceiverMock.tsx @@ -11,9 +11,7 @@ import BackendMessageReceiver from './BackendMessageReceiver'; /** A mock for the backend message receiver used for testing purposes. */ -export default class BackendMessageReceiverMock - implements BackendMessageReceiver -{ +export default class BackendMessageReceiverMock implements BackendMessageReceiver { private connected = false; public connect(): void { diff --git a/libs/shared/lib/data-access/socket/backend-message-receiver/WebSocketHandler.tsx b/libs/shared/lib/data-access/socket/backend-message-receiver/WebSocketHandler.tsx index 55669800e..64b41b9df 100644 --- a/libs/shared/lib/data-access/socket/backend-message-receiver/WebSocketHandler.tsx +++ b/libs/shared/lib/data-access/socket/backend-message-receiver/WebSocketHandler.tsx @@ -16,7 +16,7 @@ export class WebSocketHandler implements BackendMessageReceiver { /** @param domain The domain to make the websocket connection with. */ public constructor(domain: string) { - this.url = 'wss://' + domain + '/socket/'; + this.url = domain; this.connected = false; } @@ -33,9 +33,7 @@ export class WebSocketHandler implements BackendMessageReceiver { // If there already is already a current websocket connection, close it first. if (this.webSocket) this.close(); - this.webSocket = new WebSocket( - this.url + (!!this.token ? `?jwt=${encodeURIComponent(this.token)}` : '') - ); + this.webSocket = new WebSocket(this.url + (!!this.token ? `?jwt=${encodeURIComponent(this.token)}` : '')); this.webSocket.onopen = () => onOpen(); this.webSocket.onmessage = this.onWebSocketMessage; this.webSocket.onerror = this.onError; @@ -69,7 +67,7 @@ export class WebSocketHandler implements BackendMessageReceiver { */ public onWebSocketMessage = (event: MessageEvent<any>) => { let data = JSON.parse(event.data); - console.log('WS message: ', data); + console.debug('WS message: ', data); Broker.instance().publish(data.value, data.type); }; @@ -79,6 +77,6 @@ export class WebSocketHandler implements BackendMessageReceiver { * @param {any} event contains the event data. */ private onError(event: any): void { - console.log(event); + console.error(event); } } diff --git a/libs/shared/lib/data-access/socket/backend-message-receiver/index.ts b/libs/shared/lib/data-access/socket/backend-message-receiver/index.ts index 1690ebf99..d0001cef6 100644 --- a/libs/shared/lib/data-access/socket/backend-message-receiver/index.ts +++ b/libs/shared/lib/data-access/socket/backend-message-receiver/index.ts @@ -1 +1 @@ -export * from './WebSocketHandler' \ No newline at end of file +export * from './WebSocketHandler'; diff --git a/libs/shared/lib/data-access/socket/backend-messenger/BackendMessenger.test.tsx b/libs/shared/lib/data-access/socket/backend-messenger/BackendMessenger.test.tsx index 3d347168e..6fe42346c 100644 --- a/libs/shared/lib/data-access/socket/backend-messenger/BackendMessenger.test.tsx +++ b/libs/shared/lib/data-access/socket/backend-messenger/BackendMessenger.test.tsx @@ -11,8 +11,6 @@ describe('BackendMessenger', () => { let backendMessenger = new BackendMessenger(placeholderDomain); it('should create the the correct url ', () => { - expect(backendMessenger['url']).toEqual( - 'https://' + placeholderDomain + '/' - ); + expect(backendMessenger['url']).toEqual('https://' + placeholderDomain + '/'); }); }); diff --git a/libs/shared/lib/data-access/socket/backend-messenger/BackendMessengerMock.tsx b/libs/shared/lib/data-access/socket/backend-messenger/BackendMessengerMock.tsx index ada2ac8a9..f06c167be 100644 --- a/libs/shared/lib/data-access/socket/backend-messenger/BackendMessengerMock.tsx +++ b/libs/shared/lib/data-access/socket/backend-messenger/BackendMessengerMock.tsx @@ -4,13 +4,13 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ +import BackendMessengerRepository from '../backend-message-receiver/BackendMessengerRepository'; + /* istanbul ignore file */ /* The comment above was added so the code coverage wouldn't count this file towards code coverage. * We do not test mock implementations. * See testing plan for more details.*/ -import BackendMessengerRepository from '../../../domain/repository-interfaces/BackendMessengerRepository'; - /** Mock repository used for testing. */ export default class BackendMessengerMock implements BackendMessengerRepository { /** A mock version of the sendMessage function sends a fake message. */ diff --git a/libs/shared/lib/data-access/socket/backend-messenger/index.tsx b/libs/shared/lib/data-access/socket/backend-messenger/index.tsx index d0633bcab..acaf5792d 100644 --- a/libs/shared/lib/data-access/socket/backend-messenger/index.tsx +++ b/libs/shared/lib/data-access/socket/backend-messenger/index.tsx @@ -22,11 +22,7 @@ export default class BackendMessenger implements BackendMessengerRepository { * @param {string} requestMethod The method of your request. Most used are: POST, GET. * @returns {Promise<void>} A promise which is resolved once a response with status 200 has been received. */ - public SendMessage( - body: string, - requestURL: string, - requestMethod: string - ): Promise<Response> { + public SendMessage(body: string, requestURL: string, requestMethod: string): Promise<Response> { // Construct the URL we will request from const req = this.url + requestURL; diff --git a/libs/shared/lib/data-access/socket/broker/index.tsx b/libs/shared/lib/data-access/socket/broker/index.tsx index b67aa0da0..098413fd9 100644 --- a/libs/shared/lib/data-access/socket/broker/index.tsx +++ b/libs/shared/lib/data-access/socket/broker/index.tsx @@ -3,7 +3,6 @@ * Utrecht University within the Software Project course. * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import BrokerListener from './BrokerListenerInterface'; /** * A broker that handles incoming messages from the backend. @@ -39,16 +38,11 @@ export default class Broker { public publish(jsonObject: unknown, routingKey: string): void { this.mostRecentMessages[routingKey] = jsonObject; - if ( - this.listeners[routingKey] && - Object.keys(this.listeners[routingKey]).length != 0 - ) - Object.values(this.listeners[routingKey]).forEach((listener) => - listener(jsonObject, routingKey) - ); + if (this.listeners[routingKey] && Object.keys(this.listeners[routingKey]).length != 0) + Object.values(this.listeners[routingKey]).forEach((listener) => listener(jsonObject, routingKey)); // If there are no listeners, log the message else - console.log( + console.debug( `no listeners for message with routing key %c${routingKey}`, 'font-weight:bold; color: blue; background-color: white;', jsonObject @@ -74,8 +68,7 @@ export default class Broker { this.listeners[routingKey][key] = newListener; // Consume the most recent message - if (consumeMostRecentMessage && routingKey in this.mostRecentMessages) - newListener(this.mostRecentMessages[routingKey], routingKey); + if (consumeMostRecentMessage && routingKey in this.mostRecentMessages) newListener(this.mostRecentMessages[routingKey], routingKey); } return key; diff --git a/libs/shared/lib/data-access/socket/index.ts b/libs/shared/lib/data-access/socket/index.ts index 34802e1e5..9c9d22ab7 100644 --- a/libs/shared/lib/data-access/socket/index.ts +++ b/libs/shared/lib/data-access/socket/index.ts @@ -1,3 +1,3 @@ export * from './backend-message-receiver'; export * from './backend-messenger'; -export * from './broker' \ No newline at end of file +export * from './broker'; diff --git a/libs/shared/lib/data-access/socket/listeners/SchemaViewModelImpl.tsx b/libs/shared/lib/data-access/socket/listeners/SchemaViewModelImpl.tsx deleted file mode 100644 index 8347ca340..000000000 --- a/libs/shared/lib/data-access/socket/listeners/SchemaViewModelImpl.tsx +++ /dev/null @@ -1,897 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -/** This class is responsible for updating and creating the graph schema. */ -export default class SchemaViewModelImpl - extends AbstractBaseViewModelImpl - implements SchemaViewModel -{ - private reactFlowInstance: any; - private relationCounter: number; - private drawOrderUseCase: DrawOrderUseCase; - private nodeUseCase: NodeUseCase; - private edgeUseCase: EdgeUseCase; - private graphUseCase: GraphUseCase; - private placeInQueryBuilder: (name: string, type: string) => void; - public elements: SchemaElements = { nodes: [], edges: [], selfEdges: [] }; - public zoom: number; - public visible: boolean; - - public nodeQualityPopup: NodeQualityPopupNode; - public attributeAnalyticsPopupMenu: AttributeAnalyticsPopupMenuNode; - - public nodeQualityData: Record< - string, - NodeQualityDataForEntities | NodeQualityDataForRelations - >; - public attributeAnalyticsData: Record<string, AttributeAnalyticsData>; - - private entityPopupOffsets = { - nodeQualityOffset: { - x: SchemaThemeHolder.entity.width, - y: SchemaThemeHolder.entity.height - 3, - }, - attributeQualityOffset: { - x: SchemaThemeHolder.entity.width, - y: -SchemaThemeHolder.entity.height + 12, - }, - }; - - private relationPopupOffsets = { - nodeQualityOffset: { - x: SchemaThemeHolder.relation.width + 50, - y: SchemaThemeHolder.relation.height + 2, - }, - attributeQualityOffset: { - x: SchemaThemeHolder.relation.width + 50, - y: -SchemaThemeHolder.relation.height + 17, - }, - }; - // React flow reference for positioning on drop. - public myRef: React.RefObject<HTMLDivElement>; - - public nodeTypes = { - entity: EntityNode, - relation: RelationNode, - nodeQualityEntityPopup: NodeQualityEntityPopupNode, - nodeQualityRelationPopup: NodeQualityRelationPopupNode, - attributeAnalyticsPopupMenu: AttributeAnalyticsPopupMenu, - }; - - public edgeTypes = { - nodeEdge: NodeEdge, - selfEdge: SelfEdge, - }; - - public constructor( - drawOrderUseCase: DrawOrderUseCase, - nodeUseCase: NodeUseCase, - edgeUseCase: EdgeUseCase, - graphUseCase: GraphUseCase, - addAttribute: (name: string, type: string) => void - ) { - super(); - - this.myRef = React.createRef(); - // TODO: These values need to not be hardcoded. - this.zoom = 1.3; - this.relationCounter = 0; - this.reactFlowInstance = 0; - this.visible = true; - this.edgeUseCase = edgeUseCase; - this.nodeUseCase = nodeUseCase; - this.drawOrderUseCase = drawOrderUseCase; - this.graphUseCase = graphUseCase; - this.placeInQueryBuilder = addAttribute; - - this.nodeQualityPopup = this.emptyNodeQualityPopupNode(); - this.attributeAnalyticsPopupMenu = - this.emptyAttributeAnalyticsPopupMenuNode(); - - this.nodeQualityData = {}; - this.attributeAnalyticsData = {}; - } - - /** - * Containts all function calls to create the graph schema. - * Notifies the view about the changes at the end. - */ - public createSchema = (elements: SchemaElements): SchemaElements => { - let drawOrder: Node[]; - - drawOrder = this.drawOrderUseCase.createDrawOrder(elements); - // Create nodes with start position. - elements.nodes = this.nodeUseCase.setEntityNodePosition( - drawOrder, - { - x: 0, - y: 0, - }, - this.toggleNodeQualityPopup, - this.toggleAttributeAnalyticsPopupMenu - ); - - // Create the relation-nodes. - elements.edges.forEach((relation) => { - this.createRelationNode( - relation.id, - relation.data.attributes, - relation.data.collection, - elements - ); - }); - - elements.selfEdges.forEach((relation) => { - this.createRelationNode( - relation.id, - relation.data.attributes, - relation.data.collection, - elements - ); - }); - - // Complement the relation-nodes with extra data that is now accessible. - elements.edges = this.edgeUseCase.positionEdges( - drawOrder, - elements, - this.setRelationNodePosition - ); - - this.visible = false; - this.notifyViewAboutChanges(); - return elements; - }; - - /** - * consumes the schema send from the backend and uses it to create the SchemaElements for the schema - * @param jsonObject - */ - public consumeMessageFromBackend(jsonObject: unknown): void { - if (isSchemaResult(jsonObject)) { - // This is always the first message to receive, so reset the global variables. - this.visible = false; - this.createSchema({ nodes: [], edges: [], selfEdges: [] }); - - this.relationCounter = 0; - - this.nodeQualityPopup.isHidden = true; - this.attributeAnalyticsPopupMenu.isHidden = true; - - /* Create the graph-schema and add the popup-menu's for as far as possible. - * Runs underlying useCase trice to fix a bug with lingering selfEdges. - TODO: clean this up.*/ - this.elements = this.createSchema({ - nodes: [], - edges: [], - selfEdges: [], - }); - let schemaElements = - this.graphUseCase.createGraphFromInputData(jsonObject); - this.elements = this.createSchema(schemaElements); - this.notifyViewAboutChanges(); - //End weird fix - - this.visible = true; - schemaElements = this.graphUseCase.createGraphFromInputData(jsonObject); - this.elements = this.createSchema(schemaElements); - this.notifyViewAboutChanges(); - - this.addAttributesToAnalyticsPopupMenus(jsonObject); - } else if (isAttributeDataEntity(jsonObject)) { - // Add all information from the received message to the popup-menu's. - this.addAttributeDataToPopupMenusAndElements( - jsonObject, - 'gsa_node_result', - this.elements - ); - } else if (isAttributeDataRelation(jsonObject)) { - // Add all information from the received message to the popup-menu's. - this.addAttributeDataToPopupMenusAndElements( - jsonObject, - 'gsa_edge_result', - this.elements - ); - } else { - // TODO: This should be an error screen eventually. - console.log('This is no valid input!'); - } - this.notifyViewAboutChanges(); - } - - /** - * Create a reference to the reactflow schema component on load - * @param reactFlowInstance reactflow instance - */ - public onLoad = (reactFlowInstance: OnLoadParams<any>): void => { - this.reactFlowInstance = reactFlowInstance; - }; - - /** - * Complements the relation-nodes with data that had default values before. - * It determines the position of the relation-node and the connected entity-nodes. - * @param centerX Used to determine the center of the edge. - * @param centerY Used to determine the center of the edge. - * @param id The id of the relation node. - * @param from The id of the entity where the edge should connect from. - * @param to The id of the entity where the edge should connect to. - * @param attributes The attributes of the relation node. - */ - public setRelationNodePosition = ( - centerX: number, - centerY: number, - id: string, - from: string, - to: string, - attributes: Attribute[] - ): void => { - let width: number; - let overlap: boolean; - let y: number; - - // Check if the relation-node is in the list of nodes. - let relation = this.elements.nodes.find((node) => node.id == id); - if (relation == undefined) - throw new Error('Relation ' + id + ' does not exist.'); - - // Height of relation/entity + external buttons. - const height = 20; - - width = - SchemaThemeHolder.relation.width + - calcWidthRelationNodeBox(attributes.length, 0); - let x = centerX - SchemaThemeHolder.relation.width / 2; - y = centerY - height / 2; - - while (this.CheckForOverlap(x, y, width, height)) { - y = y + 1; - } - - // Replace the default values for the correct values. - relation.position = { x: x, y: y }; - relation.data.from = from; - relation.data.to = to; - - this.relationCounter++; - if (this.relationCounter == this.elements.edges.length) { - this.fitToView(); - } - this.notifyViewAboutChanges(); - }; - - /** - * Creates a new relation-node with some default values. - * @param id The id of the relation node. - * @param attributes The list of attributes that this relation-node has. - * @param collection The collection this relation-node is in. - */ - public createRelationNode = ( - id: string, - attributes: Attribute[], - collection: string, - schemaElements: SchemaElements - ): void => { - // Height of relation/entity + external buttons. - const height = 20; - const width = - SchemaThemeHolder.relation.width + - calcWidthRelationNodeBox(attributes.length, 0); - - schemaElements.nodes.push({ - type: NodeType.relation, - id: id, - position: { x: 0, y: 0 }, - data: { - width: width, - height: height, - collection: collection, - attributes: attributes, - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - toggleNodeQualityPopup: this.toggleNodeQualityPopup, - toggleAttributeAnalyticsPopupMenu: - this.toggleAttributeAnalyticsPopupMenu, - }, - }); - }; - - /** - * Calculates the width and height of the graph-schema-panel. - * @returns { number; number } The width and the height of the graph-schema-panel. - */ - public getWidthHeight = (): { width: number; height: number } => { - const reactFlow = this.myRef.current as HTMLDivElement; - const reactFlowBounds = reactFlow.getBoundingClientRect(); - const width = reactFlowBounds.right - reactFlowBounds.left; - const height = reactFlowBounds.bottom - reactFlowBounds.top; - return { width, height }; - }; - - /** Placeholder function for fitting the schema into the view. */ - public fitToView = (): void => { - let minX = Infinity; - let maxX = 0; - let minY = Infinity; - let maxY = 0; - let xZoom = 0; - let yZoom = 0; - - let setY = 0; - let setX = 0; - let setZoom = 0; - - this.elements.nodes.forEach((node) => { - const attributeCount: number = node.data.attributes.length; - const nodeCount: number = node.data.nodeCount; - const nodeWidth: number = - node.type == NodeType.entity - ? SchemaThemeHolder.entity.width + - calcWidthEntityNodeBox(attributeCount, nodeCount) - : SchemaThemeHolder.relation.width + - calcWidthRelationNodeBox(attributeCount, nodeCount); - - if (node.position.x < minX) minX = node.position.x; - if (node.position.x + nodeWidth > maxX) - maxX = node.position.x + nodeWidth; - if (node.position.y < minY) minY = node.position.y; - if (node.position.y + node.data.height > maxY) - maxY = node.position.y + node.data.height; - }); - - minX -= 10; - maxX += 90; - - const { width, height } = this.getWidthHeight(); - - // Correct for X and Y position with width and height. - let nodeWidth = Math.abs(maxX - minX); - let nodeHeight = Math.abs(maxY - minY); - - setX = minX * -1; - xZoom = width / nodeWidth; - setY = minY * -1; - yZoom = height / nodeHeight; - - // TODO: Correct position and zoom for selfEdges. - - if (xZoom >= yZoom) { - setZoom = yZoom; - setX = setX + width / 2 - nodeWidth / 2; - } else { - setZoom = xZoom; - setY = setY + height / 2 - nodeHeight / 2; - } - - try { - this.reactFlowInstance.setTransform({ x: setX, y: setY, zoom: setZoom }); - } catch { - console.log('this.reactFlowInstance is undefined!'); - } - }; - - /** - * this function check for a relation node if it overlaps with any of the other nodes in the schema. - * It creates boundingbox for the node and checks with all the other nodes if the boxes overlap. - * @param x Top left x of the node. - * @param y Top left y of the node. - * @param width Width of the node. - * @param height Height of the node. - * @returns {bool} whether it overlaps.*/ - public CheckForOverlap = ( - x: number, - y: number, - width: number, - height: number - ): boolean => { - const boundingBox = makeBoundingBox(x, y, width, height); - let elements = this.elements; - - let boundingTemporary: BoundingBox; - for (let i = 0; i < elements.nodes.length; i++) { - boundingTemporary = makeBoundingBox( - elements.nodes[i].position.x, - elements.nodes[i].position.y, - elements.nodes[i].data.width, - elements.nodes[i].data.height - ); - if (doBoxesOverlap(boundingBox, boundingTemporary)) { - return true; - } - } - return false; - }; - - /** Exports the schema builder to a beautiful png file */ - public exportToPNG(): void { - const { width, height } = this.getWidthHeight(); - exportComponentAsPNG(this.myRef, { - fileName: 'schemaBuilder', - pdfOptions: { - x: 0, - y: 0, - w: width, - h: height, - unit: 'px', - } as Partial<PDFOptions>, - } as Params); - } - - /** Not implemented method for exporting the schema builder visualisation to PDF. */ - public exportToPDF(): void { - console.log('Method not implemented.'); - } - - /** Attach the listener to the broker. */ - public subscribeToSchemaResult(): void { - Broker.instance().subscribe(this, 'schema_result'); - } - - /** Detach the listener to the broker. */ - public unSubscribeFromSchemaResult(): void { - Broker.instance().unSubscribe(this, 'schema_result'); - } - - /** Attach the listeners to the broker. */ - public subscribeToAnalyticsData(): void { - Broker.instance().subscribe(this, 'gsa_node_result'); - Broker.instance().subscribe(this, 'gsa_edge_result'); - } - - /** Detach the listeners to the broker. */ - public unSubscribeFromAnalyticsData(): void { - Broker.instance().unSubscribe(this, 'gsa_node_result'); - Broker.instance().unSubscribe(this, 'gsa_edge_result'); - } - - /** - * This function is used by relation and entity nodes to hide or show the node-quality popup of that node. - * @param id of the node for which the new popup is. - */ - public toggleNodeQualityPopup = (id: string): void => { - const popup = this.nodeQualityPopup; - - // Hide the popup if the current popup is visible and if the popup belongs to the same node as the given id. - if (popup.nodeID == id && !popup.isHidden) popup.isHidden = true; - // Else make and show a new popup for the node with the given id. - else this.updateNodeQualityPopup(id, this.elements); - - this.notifyViewAboutChanges(); - }; - - /** - * This function shows and updates the node-quality popup for the node which has the given id. - * @param id of the node for which the new popup is. - */ - private updateNodeQualityPopup(id: string, schemaElements: SchemaElements) { - let node = schemaElements.nodes.find((node) => node.id == id); - if (node == undefined) { - throw new Error('Node does not exist therefore no popup can be shown.'); - } - - const popup = this.nodeQualityPopup; - popup.nodeID = id; - popup.isHidden = false; - popup.data = this.nodeQualityData[id]; - - if (node.type == 'entity') { - // Make changes to the popup, to make it a popup for entities. - this.updateToNodeQualityEntityPopup(node); - } else { - // Make changes to the popup, to make it a popup for relations. - this.updateToNodeQualityRelationPopup(node); - } - - // Hide the attributeAnalyticsPopupMenu so that only one popup is displayed. - this.attributeAnalyticsPopupMenu.isHidden = true; - - this.notifyViewAboutChanges(); - this.relationCounter++; - if (this.relationCounter == schemaElements.edges.length) { - this.fitToView(); - } - } - - /** - * This displays the new node-quality popup for the given entity. - * @param node This is the entity of which you want to display the popup. - */ - private updateToNodeQualityEntityPopup(node: Node) { - const popup = this.nodeQualityPopup; - const offset = this.entityPopupOffsets.nodeQualityOffset; - - popup.position = { - x: node.position.x + offset.x, - y: node.position.y + offset.y, - }; - - popup.type = 'nodeQualityEntityPopup'; - } - - /** - * This displays the new node-quality popup for the given relation. - * @param node This is the relation of which you want to display the popup. - */ - private updateToNodeQualityRelationPopup(node: Node) { - const popup = this.nodeQualityPopup; - const offset = this.relationPopupOffsets.nodeQualityOffset; - - popup.position = { - x: node.position.x + offset.x, - y: node.position.y + offset.y, - }; - - popup.type = 'nodeQualityRelationPopup'; - } - - /** - * This function is used by relation and entity nodes to hide or show the attribute analyics popup menu of that node. - * @param id of the node for which the popup is. - */ - public toggleAttributeAnalyticsPopupMenu = (id: string): void => { - const popupMenu = this.attributeAnalyticsPopupMenu; - - // Hide the popup menu if the current popup menu is visible and if the popup menu belongs to the same node as the given id. - if (popupMenu.nodeID == id && !popupMenu.isHidden) - popupMenu.isHidden = true; - // Else make and show a new popup menu for the node with the given id. - else this.updateAttributeAnalyticsPopupMenu(id, this.elements); - - this.notifyViewAboutChanges(); - }; - - /** - * This displays the attribute-analytics popup menu for the given node (entity or relation). - * It removes the other menus from the screen. - * @param id This is the id of the node (entity or relation) of which you want to display the menu. - */ - public updateAttributeAnalyticsPopupMenu = ( - id: string, - schemaElements: SchemaElements - ): void => { - const node = schemaElements.nodes.find((node) => node.id == id); - - if (node == undefined) - throw new Error( - 'Node ' + id + ' does not exist therefore no popup menu can be shown.' - ); - - const popupMenu = this.attributeAnalyticsPopupMenu; - // Make new popup menu for the node. - popupMenu.nodeID = id; - popupMenu.isHidden = false; - popupMenu.data = { ...this.attributeAnalyticsData[id] }; - - if (node.type == NodeType.entity) { - const offset = this.entityPopupOffsets.attributeQualityOffset; - popupMenu.position = { - x: node.position.x + offset.x, - y: node.position.y + offset.y, - }; - } else { - const offset = this.relationPopupOffsets.attributeQualityOffset; - popupMenu.position = { - x: node.position.x + offset.x, - y: node.position.y + offset.y, - }; - } - - // Hide the nodeQualityPopup so that only one popup is displayed. - this.nodeQualityPopup.isHidden = true; - - this.notifyViewAboutChanges(); - }; - - /** This removes the node quality popup from the screen. */ - public hideNodeQualityPopup = (): void => { - this.nodeQualityPopup.isHidden = true; - this.notifyViewAboutChanges(); - }; - - /** This removes the attribute-analytics popup menu from the screen. */ - public hideAttributeAnalyticsPopupMenu = (): void => { - this.attributeAnalyticsPopupMenu.isHidden = true; - this.notifyViewAboutChanges(); - }; - - /** - * This sets all the data for the attributesPopupMenu without the attribute data. - * @param schemaResult This is the schema result that you get (so no attribute data yet). - */ - addAttributesToAnalyticsPopupMenus = (schemaResult: Schema): void => { - this.nodeQualityData = {}; - this.attributeAnalyticsData = {}; - - // Firstly, loop over all entities and add the quality-data (as far as possible). - // Then add the attribute-data (as far as possible). - schemaResult.nodes.forEach((node) => { - this.nodeQualityData[node.name] = { - nodeCount: 0, - notConnectedNodeCount: 0, - attributeNullCount: 0, - isAttributeDataIn: false, - onClickCloseButton: this.hideNodeQualityPopup, - }; - let attributes: any = []; - node.attributes.forEach((attribute) => { - attributes.push({ - attribute: attribute, - category: AttributeCategory.undefined, - nullAmount: 0, - }); - }); - this.attributeAnalyticsData[node.name] = { - nodeID: node.name, - nodeType: NodeType.entity, - attributes: attributes, - isAttributeDataIn: false, - onClickCloseButton: this.hideAttributeAnalyticsPopupMenu, - onClickPlaceInQueryBuilderButton: this.placeInQueryBuilder, - searchForAttributes: this.searchForAttributes, - resetAttributeFilters: this.resetAttributeFilters, - applyAttributeFilters: this.applyAttributeFilters, - }; - }); - // Secondly, loop over all relations and add the quality-data (as far as possible). - // Then add the attribute-data (as far as possible). - schemaResult.edges.forEach((edge) => { - this.nodeQualityData[edge.collection] = { - nodeCount: 0, - fromRatio: 0, - toRatio: 0, - attributeNullCount: 0, - notConnectedNodeCount: 0, - isAttributeDataIn: false, - onClickCloseButton: this.hideNodeQualityPopup, - }; - let attributes: any = []; - edge.attributes.forEach((attribute) => { - attributes.push({ - attribute: attribute, - category: AttributeCategory.undefined, - nullAmount: 0, - }); - }); - this.attributeAnalyticsData[edge.collection] = { - nodeID: edge.collection, - nodeType: NodeType.relation, - attributes: attributes, - isAttributeDataIn: false, - onClickCloseButton: this.hideAttributeAnalyticsPopupMenu, - onClickPlaceInQueryBuilderButton: this.placeInQueryBuilder, - searchForAttributes: this.searchForAttributes, - resetAttributeFilters: this.resetAttributeFilters, - applyAttributeFilters: this.applyAttributeFilters, - }; - }); - }; - - /** Returns an empty quality popup node for react-flow. */ - public emptyAttributeAnalyticsPopupMenuNode(): AttributeAnalyticsPopupMenuNode { - return { - id: 'attributeAnalyticsPopupMenu', - nodeID: '', - position: { x: 0, y: 0 }, - data: { - nodeID: '', - nodeType: NodeType.relation, - attributes: [], - isAttributeDataIn: false, - onClickCloseButton: this.hideAttributeAnalyticsPopupMenu, - onClickPlaceInQueryBuilderButton: this.placeInQueryBuilder, - searchForAttributes: this.searchForAttributes, - resetAttributeFilters: this.resetAttributeFilters, - applyAttributeFilters: this.applyAttributeFilters, - }, - type: 'attributeAnalyticsPopupMenu', - isHidden: true, - className: 'attributeAnalyticsPopupMenu', - }; - } - /** Returns an empty quality popup node for react-flow. */ - public emptyNodeQualityPopupNode(): NodeQualityPopupNode { - return { - id: 'nodeQualityPopup', - position: { x: 0, y: 0 }, - data: { - nodeCount: 0, - notConnectedNodeCount: 0, - attributeNullCount: 0, - isAttributeDataIn: false, - onClickCloseButton: this.hideNodeQualityPopup, - }, - type: 'nodeQualityEntityPopup', - isHidden: true, - nodeID: '', - className: 'nodeQualityPopup', - }; - } - - /** - * This function adjusts the values from the new attribute data into - * the attribute-analytics data and the node-quality data. - * @param attributeData The data that comes from the backend with (some) data about the attributes. - */ - public addAttributeDataToPopupMenusAndElements = ( - attributeData: AttributeData, - attributeDataType: string, - schemaElements: SchemaElements - ): void => { - // Check if attributeData is a node/entity. - if (attributeDataType === 'gsa_node_result') { - const entity = attributeData as NodeAttributeData; - // If it is a entity then add the data for the corresponding entity. - if (entity.id in this.attributeAnalyticsData) { - const attributeDataOfEntity = this.attributeAnalyticsData[entity.id]; - attributeDataOfEntity.isAttributeDataIn = true; - - entity.attributes.forEach((attribute) => { - // Check if attribute is in the list with attributes from the correct entity. - const attributeFound = attributeDataOfEntity.attributes.find( - (attribute_) => attribute_.attribute.name == attribute.name - ); - if (attributeFound !== undefined) { - attributeFound.category = attribute.type; - attributeFound.nullAmount = attribute.nullAmount; - } - }); - } // Not throw new error, because it should not crash, a message is enough and not all data will be shown. - else - console.log( - 'entity ' + entity.id + ' is not in attributeAnalyticsData' - ); - - if (entity.id in this.nodeQualityData) { - const qualityDataOfEntity = this.nodeQualityData[ - entity.id - ] as NodeQualityDataForEntities; - qualityDataOfEntity.nodeCount = entity.length; - qualityDataOfEntity.attributeNullCount = entity.summedNullAmount; - qualityDataOfEntity.notConnectedNodeCount = Number( - (1 - entity.connectedRatio).toFixed(2) - ); - qualityDataOfEntity.isAttributeDataIn = true; - } // Not throw new error, because it should not crash, a message is enough and not all data will be shown. - else console.log('entity ' + entity.id + ' is not in nodeQualityData'); - - // Check also if the entity exists in the this.elements-list. - // If so, add the new data to it. - const elementsNode = schemaElements.nodes.find( - (node_) => node_.id == entity.id - ); - if (elementsNode !== undefined) { - elementsNode.data.nodeCount = entity.length; - elementsNode.data.summedNullAmount = entity.summedNullAmount; - elementsNode.data.connectedRatio = entity.connectedRatio; - } - } - - // Check if attributeData is an edge/relation. - else if (attributeDataType === 'gsa_edge_result') { - const relation = attributeData as EdgeAttributeData; - // If it is a relation then add the data for the corresponding relation. - if (relation.id in this.attributeAnalyticsData) { - const attributeDataOfRelation = - this.attributeAnalyticsData[relation.id]; - attributeDataOfRelation.isAttributeDataIn = true; - - relation.attributes.forEach((attribute) => { - // Check if attribute is in the list with attributes from the correct relation. - const attributeFound = attributeDataOfRelation.attributes.find( - (attribute_) => attribute_.attribute.name == attribute.name - ); - if (attributeFound !== undefined) { - attributeFound.category = attribute.type; - attributeFound.nullAmount = attribute.nullAmount; - } - }); - } // Not throw new error, because it should not crash, a message is enough and not all data will be shown. - else - console.log( - 'relation ' + relation.id + ' is not in attributeAnalyticsData' - ); - - if (relation.id in this.nodeQualityData) { - const qualityDataOfRelation = this.nodeQualityData[ - relation.id - ] as NodeQualityDataForRelations; - qualityDataOfRelation.nodeCount = relation.length; - qualityDataOfRelation.attributeNullCount = relation.summedNullAmount; - qualityDataOfRelation.fromRatio = Number(relation.fromRatio.toFixed(2)); - qualityDataOfRelation.toRatio = Number(relation.toRatio.toFixed(2)); - qualityDataOfRelation.isAttributeDataIn = true; - } // Not throw new error, because it should not crash, a message is enough and not all data will be shown. - else - console.log('relation ' + relation.id + ' is not in nodeQualityData'); - - // Check also if the entity exists in the this.elements-list. - // If so, add the new data to it. - const elementsNode = schemaElements.nodes.find( - (node_) => node_.id == relation.id - ); - if (elementsNode !== undefined) { - elementsNode.data.nodeCount = relation.length; - elementsNode.data.summedNullAmount = relation.summedNullAmount; - elementsNode.data.fromRatio = relation.fromRatio; - elementsNode.data.toRatio = relation.toRatio; - } - } else throw new Error('This data is not valid!'); - }; - - /** - * Filter out attributes that do not contain the given searchbar-value. - * @param id The id of the node the attributes are from. - * @param searchbarValue The value of the searchbar. - */ - public searchForAttributes = (id: string, searchbarValue: string): void => { - const data = this.attributeAnalyticsData[id]; - // Check if there is data available. - if (data !== undefined) { - let passedAttributes: AttributeWithData[] = []; - data.attributes.forEach((attribute) => { - if ( - attribute.attribute.name - .toLowerCase() - .includes(searchbarValue.toLowerCase()) - ) - passedAttributes.push(attribute); - }); - this.attributeAnalyticsPopupMenu.data.attributes = passedAttributes; - this.notifyViewAboutChanges(); - } - }; - - /** - * Reset the current used filters for the attribute-list. - * @param id The id of the node the attributes are from. - */ - public resetAttributeFilters = (id: string): void => { - const data = this.attributeAnalyticsData[id]; - // Check if there is data available. - if (data !== undefined) { - this.attributeAnalyticsPopupMenu.data.attributes = data.attributes; - this.notifyViewAboutChanges(); - } - }; - - /** - * Applies the chosen filters on the list of attributes of the particular node. - * @param id The id of the node the attributes are from. - * @param dataType The given type of the data you want to filter on (numerical, categorical, other). - * @param predicate The given predicate. - * @param percentage The given percentage you want to compare the null-values on. - */ - public applyAttributeFilters = ( - id: string, - dataType: AttributeCategory, - predicate: string, - percentage: number - ): void => { - const data = this.attributeAnalyticsData[id]; - // Check if there is data available. - if (data !== undefined) { - let passedAttributes: AttributeWithData[] = []; - data.attributes.forEach((attribute) => { - // If the value is undefined it means that this filter is not chosen, so that must not be taken into account for further filtering. - if ( - attribute.category == dataType || - dataType == AttributeCategory.undefined - ) - if (predicate == '' || percentage == -1) - // If the string is empty it means that this filter is not chosen, so that must not be taken into account for filtering. - passedAttributes.push(attribute); - else if ( - numberPredicates[predicate](attribute.nullAmount, percentage) - ) - passedAttributes.push(attribute); - }); - this.attributeAnalyticsPopupMenu.data.attributes = passedAttributes; - this.notifyViewAboutChanges(); - } - }; -} diff --git a/libs/shared/lib/data-access/socket/query/QueryApi.tsx b/libs/shared/lib/data-access/socket/query/QueryApi.tsx deleted file mode 100644 index 1d4f86112..000000000 --- a/libs/shared/lib/data-access/socket/query/QueryApi.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import BackendMessengerRepository from '../../../domain/repository-interfaces/BackendMessengerRepository'; -import QueryRepository, { Query } from '../../../domain/repository-interfaces/QueryRepository'; - -/** - * Implementation of the QueryRepository. - * Contains function for sending a query to the database and retrieving a cached query. - */ -export default class QueryApi implements QueryRepository { - backendMessenger: BackendMessengerRepository; - - /** @param backendMessenger {BackendMessengerRepository} A BackendMessengerRepository implementation. */ - constructor(backendMessenger: BackendMessengerRepository) { - this.backendMessenger = backendMessenger; - } - - async sendQuery(query: Query): Promise<string> { - const body = JSON.stringify(query); - return this.backendMessenger.SendMessage(body, 'query/execute/', 'POST').then((response) => { - return response.json().then((obj) => obj.queryID); - }); - } - - async retrieveCachedQuery(queryID: string): Promise<Response> { - return this.backendMessenger.SendMessage( - JSON.stringify({ queryID }), - 'query/retrieve-cached/', - 'POST', - ); - } -} diff --git a/libs/shared/lib/data-access/store/authSlice.ts b/libs/shared/lib/data-access/store/authSlice.ts index b088f1dcf..9c892fc46 100644 --- a/libs/shared/lib/data-access/store/authSlice.ts +++ b/libs/shared/lib/data-access/store/authSlice.ts @@ -1,8 +1,12 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import type { RootState } from './store'; -import { useIsAuthorizedState } from '../authorization'; - +type useIsAuthorizedState = { + authorized: boolean | undefined; + accessToken: string | undefined; + sessionID: string | undefined; + userID: string | undefined; +}; // Define the initial state using that type export const initialState: useIsAuthorizedState = { @@ -18,29 +22,31 @@ export const authSlice = createSlice({ initialState, reducers: { updateAccessToken(state, action: PayloadAction<string>) { + console.debug('Updating access token'); state.accessToken = action.payload; }, authorized(state, action: PayloadAction<useIsAuthorizedState>) { + console.debug('Authorized'); state.authorized = action.payload.authorized; state.accessToken = action.payload.accessToken; state.sessionID = action.payload.sessionID; state.userID = action.payload.userID; }, logout(state) { + console.debug('Logging out'); state.authorized = undefined; state.accessToken = undefined; state.sessionID = undefined; state.userID = undefined; }, unauthorized(state) { + console.debug('Unauthorized'); state.authorized = false; - } + }, }, }); -export const { - updateAccessToken, authorized, unauthorized, logout -} = authSlice.actions; +export const { updateAccessToken, authorized, unauthorized, logout } = authSlice.actions; // Other code such as selectors can use the imported `RootState` type export const authState = (state: RootState) => state.auth; diff --git a/libs/shared/lib/data-access/store/colorPaletteConfigSlice.ts b/libs/shared/lib/data-access/store/colorPaletteConfigSlice.ts index 397305583..57f596303 100644 --- a/libs/shared/lib/data-access/store/colorPaletteConfigSlice.ts +++ b/libs/shared/lib/data-access/store/colorPaletteConfigSlice.ts @@ -110,7 +110,7 @@ const defaultPallete: ColorPalette = { visEdge: '#999999', nodeHighlightedEdge: '#30A530', background: '#fffdfa', - visBackground: '#fffdfa', + visBackground: '#ffffff', attr: '#e2d9d3', entry: '#d49350', relation: '#1e9797', @@ -156,15 +156,7 @@ export const initialState: ColorPaletteConfig = { ...defaultPallete, custom: { ...defaultPallete.custom, - nodes: [ - '#eaeaea', - '#5358ed', - '#4eedba', - '#b665ed', - '#bd73ef', - '#c380f0', - '#c88cf1', - ], + nodes: ['#eaeaea', '#5358ed', '#4eedba', '#b665ed', '#bd73ef', '#c380f0', '#c88cf1'], nodesBase: ['#e9e9e9'], elements: { entityBase: ['#ffac57', '#ECB880', '#a36a30'], // normal, lighter, darker @@ -229,11 +221,9 @@ export const colorPaletteConfigSlice = createSlice({ }, }); -export const { changePrimary, changeDataPointColors, toggleDarkMode } = - colorPaletteConfigSlice.actions; +export const { changePrimary, changeDataPointColors, toggleDarkMode } = colorPaletteConfigSlice.actions; // Select the schema and convert it to a graphology object -export const selectColorPaletteConfig = (state: RootState) => - state.colorPaletteConfig; +export const selectColorPaletteConfig = (state: RootState) => state.colorPaletteConfig; export default colorPaletteConfigSlice.reducer; diff --git a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts index 1835b1697..a776ada94 100644 --- a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts +++ b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts @@ -1,14 +1,21 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import type { RootState } from './store'; +export interface GraphQueryResultFromBackendPayload { + payload: GraphQueryResultFromBackend; + type: string; +} + export interface GraphQueryResultFromBackend { nodes: { id: string; + label?: string; attributes: { [key: string]: unknown }; }[]; edges: { id: string; + label?: string; attributes: { [key: string]: unknown }; from: string; to: string; @@ -20,6 +27,7 @@ export interface GraphQueryResultFromBackend { export interface Node { id: string; + label: string; attributes: { [key: string]: unknown }; mldata?: any; // FIXME /* type: string[]; */ @@ -28,7 +36,8 @@ export interface Edge { attributes: { [key: string]: unknown }; from: string; to: string; - id?: string; + id: string; + label: string; /* type: string; */ } @@ -54,24 +63,39 @@ export const graphQueryResultSlice = createSlice({ // `createSlice` will infer the state type from the `initialState` argument initialState, reducers: { - assignNewGraphQueryResult: ( - state, - action: PayloadAction<GraphQueryResultFromBackend> - ) => { + assignNewGraphQueryResult: (state, action: PayloadAction<GraphQueryResultFromBackendPayload>) => { + const payload = action.payload.payload; + // console.log('!!!assignNewGraphQueryResult', action.payload.payload); + // Maybe do some data quality checking and parsing // ... // Collect all the different nodetypes in the result const nodeTypes: string[] = []; - action.payload.nodes.forEach((node) => { - // TODO FIXME!! Note: works only for arangodb - const nodeType = node.id.split('/')[0]; + payload.nodes = payload.nodes.map((node) => { + let _node = { ...node }; + let nodeType = node.id.split('/')[0]; + let innerLabels = node?.attributes?.labels as string[]; + if (innerLabels.length > 0) { + nodeType = innerLabels[0] as string; + } if (!nodeTypes.includes(nodeType)) nodeTypes.push(nodeType); + _node.label = nodeType; + return _node; + }); + + payload.edges = payload.edges.map((edge) => { + let edgeType = edge.id.split('/')[0]; + if (!edge.id.includes('/')) { + edgeType = edge.attributes.Type as string; + } + edge.label = edgeType; + return edge; }); // Assign new state - state.nodes = action.payload.nodes as Node[]; - state.edges = action.payload.edges; + state.nodes = payload.nodes as Node[]; + state.edges = payload.edges as Edge[]; state.nodeTypes = nodeTypes; }, resetGraphQueryResults: (state) => { @@ -83,15 +107,11 @@ export const graphQueryResultSlice = createSlice({ }, }); -export const { assignNewGraphQueryResult, resetGraphQueryResults } = - graphQueryResultSlice.actions; +export const { assignNewGraphQueryResult, resetGraphQueryResults } = graphQueryResultSlice.actions; // Other code such as selectors can use the imported `RootState` type -export const selectGraphQueryResult = (state: RootState) => - state.graphQueryResult; -export const selectGraphQueryResultNodes = (state: RootState) => - state.graphQueryResult.nodes; -export const selectGraphQueryResultLinks = (state: RootState) => - state.graphQueryResult.edges; +export const selectGraphQueryResult = (state: RootState) => state.graphQueryResult; +export const selectGraphQueryResultNodes = (state: RootState) => state.graphQueryResult.nodes; +export const selectGraphQueryResultLinks = (state: RootState) => state.graphQueryResult.edges; export default graphQueryResultSlice.reducer; diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts index e4095282c..7463e73c5 100644 --- a/libs/shared/lib/data-access/store/hooks.ts +++ b/libs/shared/lib/data-access/store/hooks.ts @@ -1,10 +1,6 @@ import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; import { selectGraphQueryResult } from './graphQueryResultSlice'; -import { - schemaGraph, - schemaGraphology, - selectSchemaLayout, -} from './schemaSlice'; +import { schemaGraph, schemaGraphology, selectSchemaLayout } from './schemaSlice'; import type { RootState, AppDispatch } from './store'; import { configState } from '@graphpolaris/shared/lib/data-access/store/configSlice'; import { @@ -29,12 +25,9 @@ export const useSchemaGraph = () => useAppSelector(schemaGraph); // Gives the schema form the store (as a graphology object) export const useSchemaLayout = () => useAppSelector(selectSchemaLayout); -export const useQuerybuilderGraphology = () => - useAppSelector(selectQuerybuilderGraphology); -export const useQuerybuilderGraph = () => - useAppSelector(selectQuerybuilderGraph); -export const useQuerybuilderHash = () => - useAppSelector(selectQuerybuilderHash); +export const useQuerybuilderGraphology = () => useAppSelector(selectQuerybuilderGraphology); +export const useQuerybuilderGraph = () => useAppSelector(selectQuerybuilderGraph); +export const useQuerybuilderHash = () => useAppSelector(selectQuerybuilderHash); // Overall Configuration of the app export const useConfig = () => useAppSelector(configState); diff --git a/libs/shared/lib/data-access/store/index.ts b/libs/shared/lib/data-access/store/index.ts index 43667f977..be78c8572 100644 --- a/libs/shared/lib/data-access/store/index.ts +++ b/libs/shared/lib/data-access/store/index.ts @@ -1,12 +1,7 @@ export * from './store'; export * from './hooks'; -export { - setSchema, - readInSchemaFromBackend, - schemaSlice, - selectSchemaLayout, -} from './schemaSlice'; +export { setSchema, readInSchemaFromBackend, schemaSlice, selectSchemaLayout } from './schemaSlice'; export { querybuilderSlice, setQuerybuilderNodes } from './querybuilderSlice'; export { selectGraphQueryResult, @@ -26,7 +21,4 @@ export { // Exported types export type { Node, Edge, GraphQueryResult } from './graphQueryResultSlice'; -export type { - ColorPaletteConfig, - ExtraColorsForMui5, -} from './colorPaletteConfigSlice'; +export type { ColorPaletteConfig, ExtraColorsForMui5 } from './colorPaletteConfigSlice'; diff --git a/libs/shared/lib/data-access/store/querybuilderSlice.ts b/libs/shared/lib/data-access/store/querybuilderSlice.ts index 61f6a4a9e..26993303d 100644 --- a/libs/shared/lib/data-access/store/querybuilderSlice.ts +++ b/libs/shared/lib/data-access/store/querybuilderSlice.ts @@ -1,16 +1,14 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import type { RootState } from './store'; -import { MultiGraph } from 'graphology'; +import Graph, { MultiGraph } from 'graphology'; import { Attributes, SerializedGraph } from 'graphology-types'; -import { - QueryMultiGraphology, - QueryMultiGraph, -} from '@graphpolaris/shared/lib/querybuilder/model/graphology/utils'; -import { json } from 'd3'; +import { QueryMultiGraph, QueryMultiGraphology as QueryGraphology } from '../../querybuilder/model/graphology/utils'; // Define the initial state using that type -export const initialState = { - graphologySerialized: new QueryMultiGraphology().export(), +export const initialState: { + graphologySerialized: QueryMultiGraph; +} = { + graphologySerialized: new QueryGraphology().export(), // schemaLayout: 'Graphology_noverlap', }; @@ -19,36 +17,24 @@ export const querybuilderSlice = createSlice({ // `createSlice` will infer the state type from the `initialState` argument initialState, reducers: { - setQuerybuilderNodes: ( - state, - action: PayloadAction<SerializedGraph<Attributes, Attributes, Attributes>> - ) => { - state.graphologySerialized = QueryMultiGraphology.from( - action.payload - ).export() as QueryMultiGraph; - }, - updateQBAttributeOperator: ( - state, - action: PayloadAction<{ id: string; operator: string }> - ) => { - const graph = QueryMultiGraphology.from(state.graphologySerialized); - graph.setNodeAttribute( - action.payload.id, - 'operator', - action.payload.operator - ); - state.graphologySerialized = graph.export(); - }, - updateQBAttributeValue: ( - state, - action: PayloadAction<{ id: string; value: string }> - ) => { - const graph = QueryMultiGraphology.from(state.graphologySerialized); - graph.setNodeAttribute(action.payload.id, 'value', action.payload.value); - state.graphologySerialized = graph.export(); + setQuerybuilderNodes: (state, action: PayloadAction<QueryMultiGraph>) => { + // console.log('setQuerybuilderNodes', action.payload); + // @ts-ignore + state.graphologySerialized = QueryGraphology.from(action.payload).export(); + // state.graphologySerialized = action.payload; }, + // updateQBAttributeOperator: (state, action: PayloadAction<{ id: string; operator: string }>) => { + // const graph = QueryMultiGraphology.from(state.graphologySerialized); + // graph.setNodeAttribute(action.payload.id, 'operator', action.payload.operator); + // state.graphologySerialized = graph.export(); + // }, + // updateQBAttributeValue: (state, action: PayloadAction<{ id: string; value: string }>) => { + // const graph = QueryMultiGraphology.from(state.graphologySerialized); + // graph.setNodeAttribute(action.payload.id, 'value', action.payload.value); + // state.graphologySerialized = graph.export(); + // }, clearQB: (state) => { - state.graphologySerialized = new QueryMultiGraphology().export(); + state.graphologySerialized = new QueryGraphology().export(); }, // addQuerybuilderNode: ( @@ -65,39 +51,45 @@ export const querybuilderSlice = createSlice({ }, }); -export const { - setQuerybuilderNodes, - updateQBAttributeOperator, - updateQBAttributeValue, - clearQB, -} = querybuilderSlice.actions; +export const { setQuerybuilderNodes, clearQB } = querybuilderSlice.actions; /** Select the querybuilder nodes in serialized fromat */ -export const selectQuerybuilderGraphology = ( - state: RootState -): QueryMultiGraphology => { +export const selectQuerybuilderGraphology = (state: RootState): QueryGraphology => { // This is really weird but for some reason all the attributes appeared as read-only otherwise - let ret = new QueryMultiGraphology(); - ret.import(MultiGraph.from(state.querybuilder.graphologySerialized).export()); + let ret = new QueryGraphology(); + ret.import(Graph.from(state.querybuilder.graphologySerialized).export()); return ret; }; /** Select the querybuilder nodes and convert it to a graphology object */ -export const selectQuerybuilderGraph = ( - state: RootState -): QueryMultiGraph => { +export const selectQuerybuilderGraph = (state: RootState): QueryMultiGraph => { // This is really weird but for some reason all the attributes appeared as read-only otherwise return state.querybuilder.graphologySerialized as QueryMultiGraph; }; /** Select the querybuilder nodes and convert it to a graphology object */ -export const selectQuerybuilderHash = ( - state: RootState -): any => { +export const selectQuerybuilderHash = (state: RootState): any => { + const hashedNodes = state.querybuilder.graphologySerialized.nodes.map((n) => { + let node = { ...n }; + if (n?.attributes) { + let newAttributes = { ...n?.attributes }; + newAttributes.x = 0; + newAttributes.y = 0; + newAttributes.height = 0; + newAttributes.width = 0; + node.attributes = newAttributes; + } + return node; + }); + return JSON.stringify({ - nodes: state.querybuilder.graphologySerialized.nodes.map(n => ({ key: n.key })), - edges: state.querybuilder.graphologySerialized.edges.map(n => ({ key: n.key, source: n.source, target: n.target })) + nodes: hashedNodes, + edges: state.querybuilder.graphologySerialized.edges.map((n) => ({ + key: n.key, + source: n.source, + target: n.target, + })), }); }; diff --git a/libs/shared/lib/data-access/store/schemaSlice.ts b/libs/shared/lib/data-access/store/schemaSlice.ts index beccab8e2..80f957305 100644 --- a/libs/shared/lib/data-access/store/schemaSlice.ts +++ b/libs/shared/lib/data-access/store/schemaSlice.ts @@ -12,7 +12,7 @@ export const initialState: { layoutName: AllLayoutAlgorithms; } = { graphologySerialized: new SchemaGraphology().export(), - // layoutName: 'Cytoscape_fcose', + // layoutName: 'Cytoscape_fcose', layoutName: CytoscapeLayoutAlgorithms.KLAY as AllLayoutAlgorithms, }; @@ -31,12 +31,9 @@ export const schemaSlice = createSlice({ state.layoutName = action.payload as AllLayoutAlgorithms; }, - readInSchemaFromBackend: ( - state, - action: PayloadAction<SchemaFromBackend> - ) => { + readInSchemaFromBackend: (state, action: PayloadAction<SchemaFromBackend>) => { state.graphologySerialized = SchemaUtils.schemaBackend2Graphology(action.payload).export(); - console.log('Updated schema from backend'); + // console.log('Updated schema from backend'); // The graph schema needs a node for each node AND edge. These need then be connected // nodes.forEach((node) => { diff --git a/libs/shared/lib/data-access/store/sessionSlice.ts b/libs/shared/lib/data-access/store/sessionSlice.ts index f246f7e44..bc9e98a8b 100644 --- a/libs/shared/lib/data-access/store/sessionSlice.ts +++ b/libs/shared/lib/data-access/store/sessionSlice.ts @@ -51,11 +51,10 @@ export const sessionSlice = createSlice({ state.currentDatabase = action.payload; }, updateDatabaseList(state, action: PayloadAction<string[]>) { - console.log('Updating database list', action); + console.debug('Updating database list', action); state.databases = action.payload; if (state.databases.length > 0) { - if (!state.currentDatabase || !(state.databases.includes(state.currentDatabase))) - state.currentDatabase = state.databases[0] + if (!state.currentDatabase || !state.databases.includes(state.currentDatabase)) state.currentDatabase = state.databases[0]; else state.currentDatabase = undefined; } }, @@ -67,20 +66,17 @@ export const sessionSlice = createSlice({ case 'Translation error': case 'Database error': case 'ML bad request': - state.error = action.payload + state.error = action.payload; break; } }, closeError(state): void { state.error = undefined; - } + }, }, }); -export const { - updateCurrentDatabase, updateDatabaseList, displayError, closeError - -} = sessionSlice.actions; +export const { updateCurrentDatabase, updateDatabaseList, displayError, closeError } = sessionSlice.actions; // Other code such as selectors can use the imported `RootState` type export const sessionCacheState = (state: RootState) => state.sessionCache; diff --git a/libs/shared/lib/data-access/store/store.spec.ts b/libs/shared/lib/data-access/store/store.spec.ts index 235dc00bb..3346fd37d 100644 --- a/libs/shared/lib/data-access/store/store.spec.ts +++ b/libs/shared/lib/data-access/store/store.spec.ts @@ -1,4 +1,4 @@ -import { assert, describe, expect, it } from "vitest"; +import { assert, describe, expect, it } from 'vitest'; describe('sharedDataAccessStore', () => { it('should work', () => { diff --git a/libs/shared/lib/data-access/store/store.ts b/libs/shared/lib/data-access/store/store.ts index f5dd2dab6..b7be9dcd3 100644 --- a/libs/shared/lib/data-access/store/store.ts +++ b/libs/shared/lib/data-access/store/store.ts @@ -20,10 +20,7 @@ export const store = configureStore({ middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { - ignoredPaths: [ - 'schema.graphologySerialized', - 'querybuilder.graphologySerialized', - ], + ignoredPaths: ['schema.graphologySerialized', 'querybuilder.graphologySerialized'], }, }), }); diff --git a/libs/shared/lib/data-access/theme/colours.tsx b/libs/shared/lib/data-access/theme/colours.tsx index 68d2534d8..60870f15a 100644 --- a/libs/shared/lib/data-access/theme/colours.tsx +++ b/libs/shared/lib/data-access/theme/colours.tsx @@ -58,15 +58,7 @@ export const ColourPalettes: { [key: string]: any } = { // dark dark: { - nodes: [ - 'eaeaea', - '5358ed', - '4eedba', - 'b665ed', - 'bd73ef', - 'c380f0', - 'c88cf1', - ], + nodes: ['eaeaea', '5358ed', '4eedba', 'b665ed', 'bd73ef', 'c380f0', 'c88cf1'], nodesBase: ['e9e9e9'], elements: { entityBase: ['ffac57', 'ECB880', 'a36a30'], // normal, lighter, darker diff --git a/libs/shared/lib/data-access/theme/graphPolarisThemeProvider.spec.tsx b/libs/shared/lib/data-access/theme/graphPolarisThemeProvider.spec.tsx index 88289326e..6a262bc1e 100644 --- a/libs/shared/lib/data-access/theme/graphPolarisThemeProvider.spec.tsx +++ b/libs/shared/lib/data-access/theme/graphPolarisThemeProvider.spec.tsx @@ -1,14 +1,14 @@ // import Button from '@mui/material/Button'; -import React from "react"; -import ReactDOM from "react-dom"; -import { MockTheme, MockThemeGraphPolaris } from "./mockTheme"; -import { assert, describe, expect, it } from "vitest"; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { MockTheme, MockThemeGraphPolaris } from './mockTheme'; +import { assert, describe, expect, it } from 'vitest'; -describe("<MockTheme GraphPolarisThemeProvider>", () => { +describe('<MockTheme GraphPolarisThemeProvider>', () => { // TODO: This test should be implemented and running, but I get import issues in the mono-repo with jest (MIB) // https://emotion.sh/docs/@emotion/jest - it("renders without crashing", () => undefined); + it('renders without crashing', () => undefined); // it('passes smoke test no config of mocktheme', () => { // const div = document.createElement('div'); diff --git a/libs/shared/lib/data-access/theme/graphPolarisThemeProvider.tsx b/libs/shared/lib/data-access/theme/graphPolarisThemeProvider.tsx index 191b3606f..f9ead3f62 100644 --- a/libs/shared/lib/data-access/theme/graphPolarisThemeProvider.tsx +++ b/libs/shared/lib/data-access/theme/graphPolarisThemeProvider.tsx @@ -1,10 +1,5 @@ import React, { ReactNode } from 'react'; -import { - ColorPaletteConfig, - ExtraColorsForMui5, - selectColorPaletteConfig, - useAppSelector, -} from '../../data-access/store'; +import { ColorPaletteConfig, ExtraColorsForMui5, selectColorPaletteConfig, useAppSelector } from '../../data-access/store'; import { createTheme, ThemeOptions, ThemeProvider } from '@mui/material/styles'; import { ColorPalette } from '../store/colorPaletteConfigSlice'; @@ -20,11 +15,7 @@ declare module '@mui/material/styles' { } } -export function GraphPolarisThemeProvider({ - children, -}: { - children: ReactNode; -}) { +export function GraphPolarisThemeProvider({ children }: { children: ReactNode }) { const colorPaletteConfig = useAppSelector(selectColorPaletteConfig); // Create a new theme when our custom color palette in redux changed @@ -38,15 +29,11 @@ export function GraphPolarisThemeProvider({ return <ThemeProvider theme={theme}>{children}</ThemeProvider>; } -export function MapColorsConfigToMuiTheme( - colorsConfig: ColorPaletteConfig -): ThemeOptions { +export function MapColorsConfigToMuiTheme(colorsConfig: ColorPaletteConfig): ThemeOptions { return { palette: { mode: colorsConfig.darkMode ? 'dark' : 'light', - ...(colorsConfig.darkMode - ? colorsConfig.darkPalette - : colorsConfig.lightPalette), + ...(colorsConfig.darkMode ? colorsConfig.darkPalette : colorsConfig.lightPalette), // ...(colorsConfig.darkMode // ? colorsConfig.darkPalette.custom // : colorsConfig.lightPalette.custom) as ExtraColorsForMui5, diff --git a/libs/shared/lib/graph-layout/cytoscape-layouts.ts b/libs/shared/lib/graph-layout/cytoscape-layouts.ts index b56db8e71..7b0fe6945 100644 --- a/libs/shared/lib/graph-layout/cytoscape-layouts.ts +++ b/libs/shared/lib/graph-layout/cytoscape-layouts.ts @@ -49,9 +49,7 @@ type CytoNode = { /** * This is the Cytoscape Factory */ -export class CytoscapeFactory - implements ILayoutFactory<CytoscapeLayoutAlgorithms> -{ +export class CytoscapeFactory implements ILayoutFactory<CytoscapeLayoutAlgorithms> { createLayout(LayoutAlgorithm: CytoscapeLayoutAlgorithms): Cytoscape | null { switch (LayoutAlgorithm) { case 'Cytoscape_klay': @@ -227,9 +225,7 @@ class CytoscapeKlay extends Cytoscape { super('Cytoscape_klay'); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes> - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>): void { const cytonodes: CytoNode[] = this.convertToCytoscapeModel(graph); cytoscape.use(klay); @@ -245,10 +241,10 @@ class CytoscapeKlay extends Cytoscape { // boundingBox: undefined, ready: function () { - console.info('Layout.ready'); + // console.info('Layout.ready'); }, // on layoutready stop: function () { - console.debug('Layout.stop'); + // console.debug('Layout.stop'); }, // on layoutstop } as any); layout.run(); @@ -349,10 +345,7 @@ class CytoscapeElk extends Cytoscape { super('Cytoscape_elk'); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes>, - verbose?: boolean - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>, verbose?: boolean): void { const cytonodes: CytoNode[] = this.convertToCytoscapeModel(graph); const cy = this.makeCytoscapeInstance(cytonodes); @@ -454,10 +447,7 @@ class CytoscapeDagre extends Cytoscape { cytoscape.use(dagre); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes>, - verbose?: boolean - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>, verbose?: boolean): void { const cytonodes: CytoNode[] = this.convertToCytoscapeModel(graph); const cy = this.makeCytoscapeInstance(cytonodes); @@ -486,10 +476,7 @@ class CytoscapeFCose extends Cytoscape { cytoscape.use(fcose); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes>, - verbose?: boolean - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>, verbose?: boolean): void { const cytonodes: CytoNode[] = this.convertToCytoscapeModel(graph); const cy = this.makeCytoscapeInstance(cytonodes); @@ -520,10 +507,7 @@ class CytoscapeCoseBilkent extends Cytoscape { cytoscape.use(coseBilkent); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes>, - verbose?: boolean - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>, verbose?: boolean): void { const cytonodes: CytoNode[] = this.convertToCytoscapeModel(graph); const cy = this.makeCytoscapeInstance(cytonodes); @@ -553,10 +537,7 @@ class CytoscapeCise extends Cytoscape { cytoscape.use(cise); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes>, - verbose?: boolean - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>, verbose?: boolean): void { const cytonodes: CytoNode[] = this.convertToCytoscapeModel(graph); const cy = this.makeCytoscapeInstance(cytonodes); diff --git a/libs/shared/lib/graph-layout/graphology-layouts.ts b/libs/shared/lib/graph-layout/graphology-layouts.ts index 74585977c..93c26cbb9 100644 --- a/libs/shared/lib/graph-layout/graphology-layouts.ts +++ b/libs/shared/lib/graph-layout/graphology-layouts.ts @@ -1,8 +1,6 @@ import Graph from 'graphology'; import { circular, random } from 'graphology-layout'; -import forceAtlas2, { - ForceAtlas2Settings, -} from 'graphology-layout-forceatlas2'; +import forceAtlas2, { ForceAtlas2Settings } from 'graphology-layout-forceatlas2'; import noverlap from 'graphology-layout-noverlap'; import { RandomLayoutOptions } from 'graphology-layout/random'; import { Attributes } from 'graphology-types'; @@ -21,9 +19,7 @@ export type GraphologyLayoutAlgorithms = * This is the Graphology Constructor for the main layouts available at * https://graphology.github.io/ */ -export class GraphologyFactory - implements ILayoutFactory<GraphologyLayoutAlgorithms> -{ +export class GraphologyFactory implements ILayoutFactory<GraphologyLayoutAlgorithms> { createLayout(layoutAlgorithm: GraphologyLayoutAlgorithms): Graphology | null { switch (layoutAlgorithm) { case 'Graphology_random': @@ -69,9 +65,7 @@ export class GraphologyCircular extends Graphology { super('Graphology_circular'); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes> - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>): void { // To directly assign the positions to the nodes: circular.assign(graph, { scale: 100, @@ -90,9 +84,7 @@ export class GraphologyRandom extends Graphology { super('Graphology_random'); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes> - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>): void { // const positions = random(graph); // To directly assign the positions to the nodes: @@ -120,9 +112,7 @@ export class GraphologyNoverlap extends Graphology { super('Graphology_noverlap'); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes> - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>): void { // To directly assign the positions to the nodes: noverlap.assign(graph, { maxIterations: 10000, @@ -157,9 +147,7 @@ export class GraphologyForceAtlas2 extends Graphology { super('Graphology_forceAtlas2'); } - public override layout( - graph: Graph<Attributes, Attributes, Attributes> - ): void { + public override layout(graph: Graph<Attributes, Attributes, Attributes>): void { forceAtlas2.assign(graph, { iterations: 500, settings: DEFAULT_FORCEATLAS2_SETTINGS, diff --git a/libs/shared/lib/graph-layout/graphology.spec.ts b/libs/shared/lib/graph-layout/graphology.spec.ts index 3b5ffa45c..aec7880e8 100644 --- a/libs/shared/lib/graph-layout/graphology.spec.ts +++ b/libs/shared/lib/graph-layout/graphology.spec.ts @@ -2,7 +2,7 @@ import Graph, { UndirectedGraph } from 'graphology'; import { MultiGraph } from 'graphology'; import connectedCaveman from 'graphology-generators/community/connected-caveman'; import ladder from 'graphology-generators/classic/ladder'; -import { assert, describe, expect, it } from "vitest"; +import { assert, describe, expect, it } from 'vitest'; describe('graphology connection', () => { it('should create a graphology caveman', () => { @@ -10,7 +10,6 @@ describe('graphology connection', () => { const graph = connectedCaveman(Graph, 6, 8); }); - it('should create a graphology ladder', () => { // Creating a connected caveman graph const graph = ladder(Graph, 6 + 8); diff --git a/libs/shared/lib/graph-layout/layout-creator-usecase.spec.ts b/libs/shared/lib/graph-layout/layout-creator-usecase.spec.ts index 404b05c3d..0742accdc 100644 --- a/libs/shared/lib/graph-layout/layout-creator-usecase.spec.ts +++ b/libs/shared/lib/graph-layout/layout-creator-usecase.spec.ts @@ -1,410 +1,419 @@ -import { assert, describe, expect, it, test } from "vitest"; +import { assert, describe, expect, it, test } from 'vitest'; // import 'vitest-canvas-mock'; -// import { -// movieSchemaRaw, -// northwindSchemaRaw, -// simpleSchemaRaw, -// twitterSchemaRaw, -// } from '@graphpolaris/shared/lib/mock-data'; -import Graph, { MultiGraph } from 'graphology'; - -import connectedCaveman from 'graphology-generators/community/connected-caveman'; -import ladder from 'graphology-generators/classic/ladder'; -import { LayoutFactory } from './index'; - -// import 'vitest-canvas-mock'; - -const TIMEOUT = 10; - -/** - * @jest-environment jsdom - */ -describe('LayoutFactory Graphology Libries', () => { +describe('LayoutFactory Graphology Libraries', () => { /** * @jest-environment jsdom */ - it( - 'should work with noverlap from graphology ', - () => { - const graph = new MultiGraph(); - - // Adding some nodes - // graph.addNode('John', { x: 0, y: 0, width: 200, height: 200 }); - // graph.addNode('Martha', { x: 0, y: 0 }); - graph.addNode('John'); - graph.addNode('Martha'); - - // Adding an edge - graph.addEdge('John', 'Martha'); - - const layoutFactory = new LayoutFactory(); - const layoutAlgorithm = layoutFactory.createLayout('Graphology_noverlap'); - layoutAlgorithm?.layout(graph); - - checkNodeAttributesDefined(graph); - }, - TIMEOUT - ); - - test( - 'should work with noverlap from graphology on generated graph', - () => { - // Creating a ladder graph - const graph = ladder(Graph, 10); - - graph.forEachNode((node, attr) => { - graph.setNodeAttribute(node, 'x', 0); - graph.setNodeAttribute(node, 'y', 0); - }); - - const layoutFactory = new LayoutFactory(); - - const layout = layoutFactory.createLayout('Graphology_noverlap'); - layout?.layout(graph); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - - // const positionMap = new Set<string>(); - // graph.forEachNode((node, attr) => { - // const pos = '' + attr['x'] + '' + attr['y']; - - // expect(positionMap.has(pos)).toBeFalsy(); - // positionMap.add(pos); - // }); - }, - TIMEOUT - ); - - test( - 'should work with random from graphology on generated graph', - () => { - // Creating a ladder graph - const graph = ladder(Graph, 10); - - graph.forEachNode((node, attr) => { - graph.setNodeAttribute(node, 'x', 0); - graph.setNodeAttribute(node, 'y', 0); - }); - - const layoutFactory = new LayoutFactory(); - - const layout = layoutFactory.createLayout('Graphology_random'); - layout?.setDimensions(100, 100); - layout?.layout(graph); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - - // const positionMap = new Set<string>(); - // graph.forEachNode((node, attr) => { - // const pos = '' + attr['x'] + '' + attr['y']; - - // expect(positionMap.has(pos)).toBeFalsy(); - // positionMap.add(pos); - // }); - }, - TIMEOUT - ); - - test( - 'should work with circular from graphology on generated graph', - () => { - // Creating a ladder graph - const graph = ladder(Graph, 100); - - graph.forEachNode((node) => { - graph.setNodeAttribute(node, 'x', 0); - graph.setNodeAttribute(node, 'y', 0); - }); - - const layoutFactory = new LayoutFactory(); - - const layout = layoutFactory.createLayout('Graphology_circular'); - layout?.setDimensions(100, 100); - layout?.layout(graph); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - - // const positionMap = new Set<string>(); - // graph.forEachNode((node, attr) => { - // const pos = '' + attr['x'] + '' + attr['y']; - - // expect(positionMap.has(pos)).toBeFalsy(); - // positionMap.add(pos); - // }); - }, - TIMEOUT - ); - - test( - 'should work with Graphology_forceAtlas2 from graphology on generated graph', - () => { - // console.log(Object.keys(AllLayoutAlgorithms)) - - const graph = connectedCaveman(Graph, 6, 8); - - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeUndefined(); - expect(graph.getNodeAttribute(node, 'y')).toBeUndefined(); - }); - - // console.log('before'); - const layoutFactory = new LayoutFactory(); - const layout = layoutFactory.createLayout('Graphology_forceAtlas2'); - layout?.setDimensions(100, 100); - layout?.layout(graph); - - // console.log('after'); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - - // const positionMap = new Set<string>(); - // graph.forEachNode((node, attr) => { - // const pos = '' + attr['x'] + '' + attr['y']; - // // console.log(pos); - - // expect(positionMap.has(pos)).toBeFalsy(); - // positionMap.add(pos); - // }); - }, - TIMEOUT - ); -}); - -describe('LayoutFactory Cytoscape Libraries', () => { - it('should convert between models Graphology <-> Cytoscape ', () => { - const graph = connectedCaveman(Graph, 10, 10); - - const layoutFactory = new LayoutFactory(); - const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_klay'); - - const cytonodes = layoutAlgorithm?.convertToCytoscapeModel(graph); - - let nodes = 0; - let edges = 0; - cytonodes?.forEach((element) => { - if (element.data.type == 'node') { - nodes++; - } else if (element.data.type == 'edge') { - edges++; - } - }); - - // console.log('Number of nodes', graph.order); - // console.log('Number of edges', graph.size); - try { - expect(nodes).toBe(graph.order); - } catch (error) { - console.error( - 'expect(nodes).toBe(graph.order) but was', - nodes, - graph.order - ); - } - expect(edges).toBe(graph.size); + it('should ignore ', () => { + return true; }); - - it( - 'should execute Cytoscape_klay from Cytoscape ', - () => { - /** - * l number: number of components in the graph. - * k number: number of nodes of the components. - */ - const graph = connectedCaveman(Graph, 5, 5); - graph.addNode('Martha', { occurrences: 1 }); - - // graph.forEachNode(node => { - // console.log(node); - // }); - - const layoutFactory = new LayoutFactory(); - const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_klay'); - layoutAlgorithm?.layout(graph); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - - // const positionMap = new Set<string>(); - // graph.forEachNode((node, attr) => { - // const pos = '' + attr['x'] + '' + attr['y']; - // expect(positionMap.has(pos)).toBeFalsy(); - // positionMap.add(pos); - // }); - }, - TIMEOUT - ); - - it( - 'should execute Cytoscape_elk from Cytoscape ', - () => { - /** - * l number: number of components in the graph. - * k number: number of nodes of the components. - */ - const graph = connectedCaveman(Graph, 5, 5); - graph.addNode('Martha', { occurrences: 1 }); - - // graph.forEachNode(node => { - // console.log(node); - // }); - - const layoutFactory = new LayoutFactory(); - const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_elk'); - layoutAlgorithm?.layout(graph, false); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - // const positionMap = new Set<string>(); - // graph.forEachNode((node, attr) => { - // const pos = '' + attr['x'] + '' + attr['y']; - // expect(positionMap.has(pos)).toBeFalsy(); - // positionMap.add(pos); - // }); - }, - TIMEOUT - ); - - it( - 'should execute Cytoscape_dagre from Cytoscape ', - () => { - /** - * l number: number of components in the graph. - * k number: number of nodes of the components. - */ - const graph = connectedCaveman(Graph, 5, 5); - graph.addNode('Martha', { occurrences: 1 }); - - // graph.forEachNode(node => { - // console.log(node); - // }); - - const layoutFactory = new LayoutFactory(); - const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_dagre'); - layoutAlgorithm?.layout(graph); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - }, - TIMEOUT - ); - - it( - 'should execute Cytoscape_fcose from Cytoscape ', - () => { - /** - * l number: number of components in the graph. - * k number: number of nodes of the components. - */ - const graph = connectedCaveman(Graph, 5, 5); - graph.addNode('Martha', { occurrences: 1 }); - - // graph.forEachNode(node => { - // console.log(node); - // }); - - const layoutFactory = new LayoutFactory(); - const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_fcose'); - layoutAlgorithm?.layout(graph, false); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - }, - TIMEOUT - ); - - it( - 'should execute Cytoscape_cose-bilkent from Cytoscape ', - () => { - /** - * l number: number of components in the graph. - * k number: number of nodes of the components. - */ - const graph = connectedCaveman(Graph, 5, 5); - graph.addNode('Martha', { occurrences: 1 }); - - // graph.forEachNode(node => { - // console.log(node); - // }); - - const layoutFactory = new LayoutFactory(); - const layoutAlgorithm = layoutFactory.createLayout( - 'Cytoscape_cose-bilkent' - ); - layoutAlgorithm?.layout(graph); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - }, - TIMEOUT - ); - - it( - 'should execute Cytoscape_cise from Cytoscape ', - () => { - /** - * l number: number of components in the graph. - * k number: number of nodes of the components. - */ - const graph = connectedCaveman(Graph, 5, 5); - graph.addNode('Martha', { occurrences: 1 }); - - // graph.forEachNode(node => { - // console.log(node); - // }); - - const layoutFactory = new LayoutFactory(); - const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_cise'); - layoutAlgorithm?.layout(graph); - - checkNodeAttributesDefined(graph); - checkPositionOverlap(graph); - }, - TIMEOUT - ); }); -function checkNodeAttributesDefined(graph: Graph) { - try { - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); - }); - } catch (error) { - console.error('Node attribute x is not defined'); - } - - try { - graph.forEachNode((node, attr) => { - expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); - }); - } catch (error) { - console.error('Node attribute y is not defined'); - } -} - -function checkPositionOverlap(graph: Graph) { - // try { - // const positionMap = new Set<string>(); - // graph.forEachNode((node, attr) => { - // const pos = '' + attr['x'] + '' + attr['y']; - // expect(positionMap.has(pos)).toBeFalsy(); - // positionMap.add(pos); - // }); - // } catch (error) { - // console.error('A node position overlap was found'); - // } - - try { - const positionMap = new Set<string>(); - graph.forEachNode((node, attr) => { - expect(isNaN(graph.getNodeAttribute(node, 'x'))).toBeFalsy(); - expect(isNaN(graph.getNodeAttribute(node, 'y'))).toBeFalsy(); - }); - } catch (error) { - console.error('A node position may not be NaN'); - } -} +// // import { +// // movieSchemaRaw, +// // northwindSchemaRaw, +// // simpleSchemaRaw, +// // twitterSchemaRaw, +// // } from '@graphpolaris/shared/lib/mock-data'; +// import Graph, { MultiGraph } from 'graphology'; + +// import connectedCaveman from 'graphology-generators/community/connected-caveman'; +// import ladder from 'graphology-generators/classic/ladder'; +// import { LayoutFactory } from './index'; + +// // import 'vitest-canvas-mock'; + +// const TIMEOUT = 10; + +// /** +// * @jest-environment jsdom +// */ +// describe('LayoutFactory Graphology Libries', () => { +// /** +// * @jest-environment jsdom +// */ +// it( +// 'should work with noverlap from graphology ', +// () => { +// const graph = new MultiGraph(); + +// // Adding some nodes +// // graph.addNode('John', { x: 0, y: 0, width: 200, height: 200 }); +// // graph.addNode('Martha', { x: 0, y: 0 }); +// graph.addNode('John'); +// graph.addNode('Martha'); + +// // Adding an edge +// graph.addEdge('John', 'Martha'); + +// const layoutFactory = new LayoutFactory(); +// const layoutAlgorithm = layoutFactory.createLayout('Graphology_noverlap'); +// layoutAlgorithm?.layout(graph); + +// checkNodeAttributesDefined(graph); +// }, +// TIMEOUT +// ); + +// test( +// 'should work with noverlap from graphology on generated graph', +// () => { +// // Creating a ladder graph +// const graph = ladder(Graph, 10); + +// graph.forEachNode((node, attr) => { +// graph.setNodeAttribute(node, 'x', 0); +// graph.setNodeAttribute(node, 'y', 0); +// }); + +// const layoutFactory = new LayoutFactory(); + +// const layout = layoutFactory.createLayout('Graphology_noverlap'); +// layout?.layout(graph); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); + +// // const positionMap = new Set<string>(); +// // graph.forEachNode((node, attr) => { +// // const pos = '' + attr['x'] + '' + attr['y']; + +// // expect(positionMap.has(pos)).toBeFalsy(); +// // positionMap.add(pos); +// // }); +// }, +// TIMEOUT +// ); + +// test( +// 'should work with random from graphology on generated graph', +// () => { +// // Creating a ladder graph +// const graph = ladder(Graph, 10); + +// graph.forEachNode((node, attr) => { +// graph.setNodeAttribute(node, 'x', 0); +// graph.setNodeAttribute(node, 'y', 0); +// }); + +// const layoutFactory = new LayoutFactory(); + +// const layout = layoutFactory.createLayout('Graphology_random'); +// layout?.setDimensions(100, 100); +// layout?.layout(graph); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); + +// // const positionMap = new Set<string>(); +// // graph.forEachNode((node, attr) => { +// // const pos = '' + attr['x'] + '' + attr['y']; + +// // expect(positionMap.has(pos)).toBeFalsy(); +// // positionMap.add(pos); +// // }); +// }, +// TIMEOUT +// ); + +// test( +// 'should work with circular from graphology on generated graph', +// () => { +// // Creating a ladder graph +// const graph = ladder(Graph, 100); + +// graph.forEachNode((node) => { +// graph.setNodeAttribute(node, 'x', 0); +// graph.setNodeAttribute(node, 'y', 0); +// }); + +// const layoutFactory = new LayoutFactory(); + +// const layout = layoutFactory.createLayout('Graphology_circular'); +// layout?.setDimensions(100, 100); +// layout?.layout(graph); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); + +// // const positionMap = new Set<string>(); +// // graph.forEachNode((node, attr) => { +// // const pos = '' + attr['x'] + '' + attr['y']; + +// // expect(positionMap.has(pos)).toBeFalsy(); +// // positionMap.add(pos); +// // }); +// }, +// TIMEOUT +// ); + +// test( +// 'should work with Graphology_forceAtlas2 from graphology on generated graph', +// () => { +// // console.log(Object.keys(AllLayoutAlgorithms)) + +// const graph = connectedCaveman(Graph, 6, 8); + +// graph.forEachNode((node, attr) => { +// expect(graph.getNodeAttribute(node, 'x')).toBeUndefined(); +// expect(graph.getNodeAttribute(node, 'y')).toBeUndefined(); +// }); + +// // console.log('before'); +// const layoutFactory = new LayoutFactory(); +// const layout = layoutFactory.createLayout('Graphology_forceAtlas2'); +// layout?.setDimensions(100, 100); +// layout?.layout(graph); + +// // console.log('after'); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); + +// // const positionMap = new Set<string>(); +// // graph.forEachNode((node, attr) => { +// // const pos = '' + attr['x'] + '' + attr['y']; +// // // console.log(pos); + +// // expect(positionMap.has(pos)).toBeFalsy(); +// // positionMap.add(pos); +// // }); +// }, +// TIMEOUT +// ); +// }); + +// describe('LayoutFactory Cytoscape Libraries', () => { +// it('should convert between models Graphology <-> Cytoscape ', () => { +// const graph = connectedCaveman(Graph, 10, 10); + +// const layoutFactory = new LayoutFactory(); +// const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_klay'); + +// const cytonodes = layoutAlgorithm?.convertToCytoscapeModel(graph); + +// let nodes = 0; +// let edges = 0; +// cytonodes?.forEach((element) => { +// if (element.data.type == 'node') { +// nodes++; +// } else if (element.data.type == 'edge') { +// edges++; +// } +// }); + +// // console.log('Number of nodes', graph.order); +// // console.log('Number of edges', graph.size); +// try { +// expect(nodes).toBe(graph.order); +// } catch (error) { +// console.error( +// 'expect(nodes).toBe(graph.order) but was', +// nodes, +// graph.order +// ); +// } +// expect(edges).toBe(graph.size); +// }); + +// it( +// 'should execute Cytoscape_klay from Cytoscape ', +// () => { +// /** +// * l number: number of components in the graph. +// * k number: number of nodes of the components. +// */ +// const graph = connectedCaveman(Graph, 5, 5); +// graph.addNode('Martha', { occurrences: 1 }); + +// // graph.forEachNode(node => { +// // console.log(node); +// // }); + +// const layoutFactory = new LayoutFactory(); +// const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_klay'); +// layoutAlgorithm?.layout(graph); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); + +// // const positionMap = new Set<string>(); +// // graph.forEachNode((node, attr) => { +// // const pos = '' + attr['x'] + '' + attr['y']; +// // expect(positionMap.has(pos)).toBeFalsy(); +// // positionMap.add(pos); +// // }); +// }, +// TIMEOUT +// ); + +// it( +// 'should execute Cytoscape_elk from Cytoscape ', +// () => { +// /** +// * l number: number of components in the graph. +// * k number: number of nodes of the components. +// */ +// const graph = connectedCaveman(Graph, 5, 5); +// graph.addNode('Martha', { occurrences: 1 }); + +// // graph.forEachNode(node => { +// // console.log(node); +// // }); + +// const layoutFactory = new LayoutFactory(); +// const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_elk'); +// layoutAlgorithm?.layout(graph, false); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); +// // const positionMap = new Set<string>(); +// // graph.forEachNode((node, attr) => { +// // const pos = '' + attr['x'] + '' + attr['y']; +// // expect(positionMap.has(pos)).toBeFalsy(); +// // positionMap.add(pos); +// // }); +// }, +// TIMEOUT +// ); + +// it( +// 'should execute Cytoscape_dagre from Cytoscape ', +// () => { +// /** +// * l number: number of components in the graph. +// * k number: number of nodes of the components. +// */ +// const graph = connectedCaveman(Graph, 5, 5); +// graph.addNode('Martha', { occurrences: 1 }); + +// // graph.forEachNode(node => { +// // console.log(node); +// // }); + +// const layoutFactory = new LayoutFactory(); +// const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_dagre'); +// layoutAlgorithm?.layout(graph); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); +// }, +// TIMEOUT +// ); + +// it( +// 'should execute Cytoscape_fcose from Cytoscape ', +// () => { +// /** +// * l number: number of components in the graph. +// * k number: number of nodes of the components. +// */ +// const graph = connectedCaveman(Graph, 5, 5); +// graph.addNode('Martha', { occurrences: 1 }); + +// // graph.forEachNode(node => { +// // console.log(node); +// // }); + +// const layoutFactory = new LayoutFactory(); +// const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_fcose'); +// layoutAlgorithm?.layout(graph, false); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); +// }, +// TIMEOUT +// ); + +// it( +// 'should execute Cytoscape_cose-bilkent from Cytoscape ', +// () => { +// /** +// * l number: number of components in the graph. +// * k number: number of nodes of the components. +// */ +// const graph = connectedCaveman(Graph, 5, 5); +// graph.addNode('Martha', { occurrences: 1 }); + +// // graph.forEachNode(node => { +// // console.log(node); +// // }); + +// const layoutFactory = new LayoutFactory(); +// const layoutAlgorithm = layoutFactory.createLayout( +// 'Cytoscape_cose-bilkent' +// ); +// layoutAlgorithm?.layout(graph); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); +// }, +// TIMEOUT +// ); + +// it( +// 'should execute Cytoscape_cise from Cytoscape ', +// () => { +// /** +// * l number: number of components in the graph. +// * k number: number of nodes of the components. +// */ +// const graph = connectedCaveman(Graph, 5, 5); +// graph.addNode('Martha', { occurrences: 1 }); + +// // graph.forEachNode(node => { +// // console.log(node); +// // }); + +// const layoutFactory = new LayoutFactory(); +// const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_cise'); +// layoutAlgorithm?.layout(graph); + +// checkNodeAttributesDefined(graph); +// checkPositionOverlap(graph); +// }, +// TIMEOUT +// ); +// }); + +// function checkNodeAttributesDefined(graph: Graph) { +// try { +// graph.forEachNode((node, attr) => { +// expect(graph.getNodeAttribute(node, 'x')).toBeDefined(); +// }); +// } catch (error) { +// console.error('Node attribute x is not defined'); +// } + +// try { +// graph.forEachNode((node, attr) => { +// expect(graph.getNodeAttribute(node, 'y')).toBeDefined(); +// }); +// } catch (error) { +// console.error('Node attribute y is not defined'); +// } +// } + +// function checkPositionOverlap(graph: Graph) { +// // try { +// // const positionMap = new Set<string>(); +// // graph.forEachNode((node, attr) => { +// // const pos = '' + attr['x'] + '' + attr['y']; +// // expect(positionMap.has(pos)).toBeFalsy(); +// // positionMap.add(pos); +// // }); +// // } catch (error) { +// // console.error('A node position overlap was found'); +// // } + +// try { +// const positionMap = new Set<string>(); +// graph.forEachNode((node, attr) => { +// expect(isNaN(graph.getNodeAttribute(node, 'x'))).toBeFalsy(); +// expect(isNaN(graph.getNodeAttribute(node, 'y'))).toBeFalsy(); +// }); +// } catch (error) { +// console.error('A node position may not be NaN'); +// } +// } diff --git a/libs/shared/lib/graph-layout/layout-creator-usecase.ts b/libs/shared/lib/graph-layout/layout-creator-usecase.ts index a21534777..725af76dc 100644 --- a/libs/shared/lib/graph-layout/layout-creator-usecase.ts +++ b/libs/shared/lib/graph-layout/layout-creator-usecase.ts @@ -1,36 +1,20 @@ -import { - Cytoscape, - CytoscapeFactory, - CytoscapeLayoutAlgorithms, - CytoscapeProvider, -} from './cytoscape-layouts'; -import { - Graphology, - GraphologyFactory, - GraphologyLayoutAlgorithms, - GraphologyProvider, -} from './graphology-layouts'; +import { Cytoscape, CytoscapeFactory, CytoscapeLayoutAlgorithms, CytoscapeProvider } from './cytoscape-layouts'; +import { Graphology, GraphologyFactory, GraphologyLayoutAlgorithms, GraphologyProvider } from './graphology-layouts'; import { Layout } from './layout'; export type Providers = GraphologyProvider | CytoscapeProvider; -export type LayoutAlgorithm<Provider extends Providers> = - `${Provider}_${string}`; +export type LayoutAlgorithm<Provider extends Providers> = `${Provider}_${string}`; -export type AllLayoutAlgorithms = - | GraphologyLayoutAlgorithms - | CytoscapeLayoutAlgorithms; +export type AllLayoutAlgorithms = GraphologyLayoutAlgorithms | CytoscapeLayoutAlgorithms; -export type AlgorithmToLayoutProvider<Algorithm extends AllLayoutAlgorithms> = - Algorithm extends GraphologyLayoutAlgorithms +export type AlgorithmToLayoutProvider<Algorithm extends AllLayoutAlgorithms> = Algorithm extends GraphologyLayoutAlgorithms ? Graphology : Algorithm extends CytoscapeLayoutAlgorithms ? Cytoscape : Cytoscape | Graphology; export interface ILayoutFactory<Algorithm extends AllLayoutAlgorithms> { - createLayout: ( - Algorithm: Algorithm - ) => AlgorithmToLayoutProvider<Algorithm> | null; + createLayout: (Algorithm: Algorithm) => AlgorithmToLayoutProvider<Algorithm> | null; } /** @@ -47,29 +31,13 @@ export class LayoutFactory implements ILayoutFactory<AllLayoutAlgorithms> { return LayoutAlgorithm.startsWith(startsWith); } - createLayout<Algorithm extends AllLayoutAlgorithms>( - layoutAlgorithm: Algorithm - ): AlgorithmToLayoutProvider<Algorithm> { - if ( - this.isSpecificAlgorithm<GraphologyLayoutAlgorithms>( - layoutAlgorithm, - 'Graphology' - ) - ) { - return this.graphologyFactory.createLayout( - layoutAlgorithm - ) as AlgorithmToLayoutProvider<Algorithm>; + createLayout<Algorithm extends AllLayoutAlgorithms>(layoutAlgorithm: Algorithm): AlgorithmToLayoutProvider<Algorithm> { + if (this.isSpecificAlgorithm<GraphologyLayoutAlgorithms>(layoutAlgorithm, 'Graphology')) { + return this.graphologyFactory.createLayout(layoutAlgorithm) as AlgorithmToLayoutProvider<Algorithm>; } - if ( - this.isSpecificAlgorithm<CytoscapeLayoutAlgorithms>( - layoutAlgorithm, - 'Cytoscape' - ) - ) { - return this.cytoscapeFactory.createLayout( - layoutAlgorithm - ) as AlgorithmToLayoutProvider<Algorithm>; + if (this.isSpecificAlgorithm<CytoscapeLayoutAlgorithms>(layoutAlgorithm, 'Cytoscape')) { + return this.cytoscapeFactory.createLayout(layoutAlgorithm) as AlgorithmToLayoutProvider<Algorithm>; } throw Error('Invalid layout algorithm'); diff --git a/libs/shared/lib/graph-layout/layout.ts b/libs/shared/lib/graph-layout/layout.ts index 5697c2841..e99ef69bb 100644 --- a/libs/shared/lib/graph-layout/layout.ts +++ b/libs/shared/lib/graph-layout/layout.ts @@ -7,17 +7,12 @@ import { Providers, LayoutAlgorithm } from './layout-creator-usecase'; */ export abstract class Layout<provider extends Providers> { - constructor( - public provider: provider, - public algorithm: LayoutAlgorithm<provider> - ) { - console.info( - `Created the following Layout: ${provider} - ${this.algorithm}` - ); + constructor(public provider: provider, public algorithm: LayoutAlgorithm<provider>) { + // console.info(`Created the following Layout: ${provider} - ${this.algorithm}`); } public layout(graph: Graph, verbose?: boolean) { - console.log(`${this.provider} [${this.algorithm}] layouting now`); + // console.log(`${this.provider} [${this.algorithm}] layouting now`); graph.forEachNode((node) => { const attr = graph.getNodeAttributes(node); diff --git a/libs/shared/lib/graph-layout/mockdata-layout.spec.ts b/libs/shared/lib/graph-layout/mockdata-layout.spec.ts index 6f1f4da37..dc3421cc7 100644 --- a/libs/shared/lib/graph-layout/mockdata-layout.spec.ts +++ b/libs/shared/lib/graph-layout/mockdata-layout.spec.ts @@ -1,4 +1,4 @@ -import { assert, describe, expect, it } from "vitest"; +import { assert, describe, expect, it } from 'vitest'; // import { // movieSchema, diff --git a/libs/shared/lib/mock-data/query-result/big2ndChamberQueryResult.ts b/libs/shared/lib/mock-data/query-result/big2ndChamberQueryResult.js similarity index 99% rename from libs/shared/lib/mock-data/query-result/big2ndChamberQueryResult.ts rename to libs/shared/lib/mock-data/query-result/big2ndChamberQueryResult.js index 877c9cb84..2cbb5c566 100644 --- a/libs/shared/lib/mock-data/query-result/big2ndChamberQueryResult.ts +++ b/libs/shared/lib/mock-data/query-result/big2ndChamberQueryResult.js @@ -10,7 +10,7 @@ * See testing plan for more details.*/ /** Mock elements used for testing the query results. */ -export const big2ndChamberQueryResult:GraphQueryResultFromBackend = { +export const big2ndChamberQueryResult = { edges: [ { from: 'kamerleden/148', diff --git a/libs/shared/lib/vis/geovis/mock-data.tsx b/libs/shared/lib/mock-data/query-result/geo-mock-data.js similarity index 92% rename from libs/shared/lib/vis/geovis/mock-data.tsx rename to libs/shared/lib/mock-data/query-result/geo-mock-data.js index 2f062d37e..dc0c8e4b4 100644 --- a/libs/shared/lib/vis/geovis/mock-data.tsx +++ b/libs/shared/lib/mock-data/query-result/geo-mock-data.js @@ -9,6 +9,7 @@ export const flights = { nodes: [ { id: 'AMM', + attributes: { airport: 'AMM', city: 'Amman', @@ -19480,22987 +19481,22987 @@ export const flights = { ], edges: [ { - id: 0, + id: '0', from: 'AMM', to: 'ALY', attributes: { prediction: 1540 }, }, { - id: 1, + id: '1', from: 'AMS', to: 'ABV', attributes: { prediction: 2581 }, }, { - id: 2, + id: '2', from: 'BHX', to: 'KTW', attributes: { prediction: 1209 }, }, { - id: 3, + id: '3', from: 'BIL', to: 'MSP', attributes: { prediction: 4134 }, }, { - id: 4, + id: '4', from: 'BLA', to: 'VLN', attributes: { prediction: 2635 }, }, { - id: 5, + id: '5', from: 'DME', to: 'LED', attributes: { prediction: 24341 }, }, { - id: 6, + id: '6', from: 'DMM', to: 'ALY', attributes: { prediction: 449 }, }, { - id: 7, + id: '7', from: 'DNH', to: 'XIY', attributes: { prediction: 449 }, }, { - id: 8, + id: '8', from: 'DOH', to: 'AMM', attributes: { prediction: 6127 }, }, { - id: 9, + id: '9', from: 'DOH', to: 'KRT', attributes: { prediction: 7872 }, }, { - id: 10, + id: '10', from: 'DRW', to: 'DIL', attributes: { prediction: 1144 }, }, { - id: 11, + id: '11', from: 'DSN', to: 'NAY', attributes: { prediction: 3554 }, }, { - id: 12, + id: '12', from: 'DTM', to: 'BUD', attributes: { prediction: 1174 }, }, { - id: 13, + id: '13', from: 'DTW', to: 'CUN', attributes: { prediction: 5807 }, }, { - id: 14, + id: '14', from: 'DTW', to: 'FRA', attributes: { prediction: 10022 }, }, { - id: 15, + id: '15', from: 'DTW', to: 'FWA', attributes: { prediction: 3402 }, }, { - id: 16, + id: '16', from: 'DTW', to: 'SNA', attributes: { prediction: 2225 }, }, { - id: 17, + id: '17', from: 'DUB', to: 'AGA', attributes: { prediction: 659 }, }, { - id: 18, + id: '18', from: 'FAR', to: 'SLC', attributes: { prediction: 965 }, }, { - id: 19, + id: '19', from: 'FCO', to: 'ALP', attributes: { prediction: 114 }, }, { - id: 20, + id: '20', from: 'FCO', to: 'MLE', attributes: { prediction: 2017 }, }, { - id: 21, + id: '21', from: 'HAM', to: 'EWR', attributes: { prediction: 3456 }, }, { - id: 22, + id: '22', from: 'ACE', to: 'LGW', attributes: { prediction: 11927 }, }, { - id: 23, + id: '23', from: 'AGP', to: 'CMN', attributes: { prediction: 808 }, }, { - id: 24, + id: '24', from: 'AGP', to: 'SVO', attributes: { prediction: 989 }, }, { - id: 25, + id: '25', from: 'AHU', to: 'TNG', attributes: { prediction: 100 }, }, { - id: 26, + id: '26', from: 'ALB', to: 'ART', attributes: { prediction: 355 }, }, { - id: 27, + id: '27', from: 'ALC', to: 'CRL', attributes: { prediction: 4697 }, }, { - id: 28, + id: '28', from: 'ALC', to: 'EDI', attributes: { prediction: 1396 }, }, { - id: 29, + id: '29', from: 'HGH', to: 'TYN', attributes: { prediction: 9358 }, }, { - id: 30, + id: '30', from: 'LIS', to: 'CNF', attributes: { prediction: 4060 }, }, { - id: 31, + id: '31', from: 'LLW', to: 'BLZ', attributes: { prediction: 3294 }, }, { - id: 32, + id: '32', from: 'LPA', to: 'OSL', attributes: { prediction: 5701 }, }, { - id: 33, + id: '33', from: 'LPL', to: 'PRG', attributes: { prediction: 1320 }, }, { - id: 34, + id: '34', from: 'LPL', to: 'SNN', attributes: { prediction: 3943 }, }, { - id: 35, + id: '35', from: 'LSH', to: 'HEH', attributes: { prediction: 260 }, }, { - id: 36, + id: '36', from: 'LUO', to: 'LAD', attributes: { prediction: 2307 }, }, { - id: 37, + id: '37', from: 'LWO', to: 'FCO', attributes: { prediction: 448 }, }, { - id: 38, + id: '38', from: 'LWO', to: 'VKO', attributes: { prediction: 1346 }, }, { - id: 39, + id: '39', from: 'LYS', to: 'BCN', attributes: { prediction: 5450 }, }, { - id: 40, + id: '40', from: 'LYS', to: 'BSK', attributes: { prediction: 455 }, }, { - id: 41, + id: '41', from: 'MGA', to: 'MIA', attributes: { prediction: 13456 }, }, { - id: 42, + id: '42', from: 'MHD', to: 'BND', attributes: { prediction: 2521 }, }, { - id: 43, + id: '43', from: 'PEC', to: 'JNU', attributes: { prediction: 145 }, }, { - id: 44, + id: '44', from: 'PEK', to: 'IKT', attributes: { prediction: 1568 }, }, { - id: 45, + id: '45', from: 'PEK', to: 'TGO', attributes: { prediction: 1651 }, }, { - id: 46, + id: '46', from: 'PEK', to: 'TPE', attributes: { prediction: 18633 }, }, { - id: 47, + id: '47', from: 'PER', to: 'CNS', attributes: { prediction: 2571 }, }, { - id: 48, + id: '48', from: 'PEW', to: 'ISB', attributes: { prediction: 2145 }, }, { - id: 49, + id: '49', from: 'PFO', to: 'DSA', attributes: { prediction: 735 }, }, { - id: 50, + id: '50', from: 'PFO', to: 'GLA', attributes: { prediction: 791 }, }, { - id: 51, + id: '51', from: 'PHF', to: 'MCO', attributes: { prediction: 2549 }, }, { - id: 52, + id: '52', from: 'PHL', to: 'MIA', attributes: { prediction: 19207 }, }, { - id: 53, + id: '53', from: 'PIS', to: 'LYS', attributes: { prediction: 1430 }, }, { - id: 54, + id: '54', from: 'PMI', to: 'LBC', attributes: { prediction: 2445 }, }, { - id: 55, + id: '55', from: 'PNH', to: 'TPE', attributes: { prediction: 5071 }, }, { - id: 56, + id: '56', from: 'REC', to: 'SDU', attributes: { prediction: 2782 }, }, { - id: 57, + id: '57', from: 'RIC', to: 'YYZ', attributes: { prediction: 988 }, }, { - id: 58, + id: '58', from: 'SPN', to: 'TIQ', attributes: { prediction: 152 }, }, { - id: 59, + id: '59', from: 'TAS', to: 'KSQ', attributes: { prediction: 2358 }, }, { - id: 60, + id: '60', from: 'TAS', to: 'VKO', attributes: { prediction: 2312 }, }, { - id: 61, + id: '61', from: 'TBU', to: 'SUV', attributes: { prediction: 315 }, }, { - id: 62, + id: '62', from: 'TBZ', to: 'PGU', attributes: { prediction: 274 }, }, { - id: 63, + id: '63', from: 'TFS', to: 'DSA', attributes: { prediction: 1563 }, }, { - id: 64, + id: '64', from: 'TFS', to: 'HAJ', attributes: { prediction: 4084 }, }, { - id: 65, + id: '65', from: 'TFS', to: 'LNZ', attributes: { prediction: 804 }, }, { - id: 66, + id: '66', from: 'THR', to: 'KHY', attributes: { prediction: 433 }, }, { - id: 67, + id: '67', from: 'THR', to: 'PFQ', attributes: { prediction: 268 }, }, { - id: 68, + id: '68', from: 'TIA', to: 'STN', attributes: { prediction: 1158 }, }, { - id: 69, + id: '69', from: 'TIJ', to: 'UPN', attributes: { prediction: 2423 }, }, { - id: 70, + id: '70', from: 'TIV', to: 'BEG', attributes: { prediction: 5331 }, }, { - id: 71, + id: '71', from: 'TLL', to: 'LPP', attributes: { prediction: 1140 }, }, { - id: 72, + id: '72', from: 'TLV', to: 'GYD', attributes: { prediction: 448 }, }, { - id: 73, + id: '73', from: 'BAQ', to: 'MAR', attributes: { prediction: 84 }, }, { - id: 74, + id: '74', from: 'BBO', to: 'ADE', attributes: { prediction: 495 }, }, { - id: 75, + id: '75', from: 'BCN', to: 'STR', attributes: { prediction: 4386 }, }, { - id: 76, + id: '76', from: 'BDA', to: 'EWR', attributes: { prediction: 3831 }, }, { - id: 77, + id: '77', from: 'BEG', to: 'LHR', attributes: { prediction: 5149 }, }, { - id: 78, + id: '78', from: 'AMS', to: 'LUX', attributes: { prediction: 4075 }, }, { - id: 79, + id: '79', from: 'AMS', to: 'PEK', attributes: { prediction: 12316 }, }, { - id: 80, + id: '80', from: 'ARD', to: 'KOE', attributes: { prediction: 1194 }, }, { - id: 81, + id: '81', from: 'ARN', to: 'TLL', attributes: { prediction: 5113 }, }, { - id: 82, + id: '82', from: 'ASB', to: 'TAS', attributes: { prediction: 307 }, }, { - id: 83, + id: '83', from: 'ASO', to: 'JIM', attributes: { prediction: 617 }, }, { - id: 84, + id: '84', from: 'ATH', to: 'MXP', attributes: { prediction: 18303 }, }, { - id: 85, + id: '85', from: 'ATL', to: 'MTY', attributes: { prediction: 1704 }, }, { - id: 86, + id: '86', from: 'ATL', to: 'SAP', attributes: { prediction: 1605 }, }, { - id: 87, + id: '87', from: 'ATL', to: 'SMF', attributes: { prediction: 6641 }, }, { - id: 88, + id: '88', from: 'ATL', to: 'STX', attributes: { prediction: 1089 }, }, { - id: 89, + id: '89', from: 'ATL', to: 'TYS', attributes: { prediction: 8891 }, }, { - id: 90, + id: '90', from: 'AUC', to: 'BOG', attributes: { prediction: 2368 }, }, { - id: 91, + id: '91', from: 'AUH', to: 'BEY', attributes: { prediction: 12811 }, }, { - id: 92, + id: '92', from: 'AUS', to: 'LNK', attributes: { prediction: 40 }, }, { - id: 93, + id: '93', from: 'AUS', to: 'MSP', attributes: { prediction: 3321 }, }, { - id: 94, + id: '94', from: 'AUS', to: 'TPA', attributes: { prediction: 2954 }, }, { - id: 95, + id: '95', from: 'AYP', to: 'LIM', attributes: { prediction: 1700 }, }, { - id: 96, + id: '96', from: 'BEY', to: 'KBP', attributes: { prediction: 356 }, }, { - id: 97, + id: '97', from: 'BGY', to: 'BTS', attributes: { prediction: 4613 }, }, { - id: 98, + id: '98', from: 'BGY', to: 'CIA', attributes: { prediction: 15052 }, }, { - id: 99, + id: '99', from: 'BGY', to: 'GRO', attributes: { prediction: 13017 }, }, { - id: 100, + id: '100', from: 'BGY', to: 'NTE', attributes: { prediction: 2508 }, }, { - id: 101, + id: '101', from: 'TOY', to: 'ICN', attributes: { prediction: 1330 }, }, { - id: 102, + id: '102', from: 'TOY', to: 'PVG', attributes: { prediction: 660 }, }, { - id: 103, + id: '103', from: 'TPA', to: 'EWR', attributes: { prediction: 25177 }, }, { - id: 104, + id: '104', from: 'TPA', to: 'ISP', attributes: { prediction: 5100 }, }, { - id: 105, + id: '105', from: 'TPE', to: 'BKK', attributes: { prediction: 72122 }, }, { - id: 106, + id: '106', from: 'TPE', to: 'NGO', attributes: { prediction: 16707 }, }, { - id: 107, + id: '107', from: 'TRV', to: 'BAH', attributes: { prediction: 3757 }, }, { - id: 108, + id: '108', from: 'CID', to: 'LAN', attributes: { prediction: 74 }, }, { - id: 109, + id: '109', from: 'CIJ', to: 'TDD', attributes: { prediction: 1594 }, }, { - id: 110, + id: '110', from: 'CJU', to: 'CJJ', attributes: { prediction: 14731 }, }, { - id: 111, + id: '111', from: 'CKG', to: 'XMN', attributes: { prediction: 6952 }, }, { - id: 112, + id: '112', from: 'CLE', to: 'CUN', attributes: { prediction: 3089 }, }, { - id: 113, + id: '113', from: 'CLJ', to: 'BCN', attributes: { prediction: 2239 }, }, { - id: 114, + id: '114', from: 'BNE', to: 'GLT', attributes: { prediction: 4747 }, }, { - id: 115, + id: '115', from: 'BOM', to: 'IKA', attributes: { prediction: 1983 }, }, { - id: 116, + id: '116', from: 'BOM', to: 'SHJ', attributes: { prediction: 7381 }, }, { - id: 117, + id: '117', from: 'BOM', to: 'SIN', attributes: { prediction: 30126 }, }, { - id: 118, + id: '118', from: 'BOS', to: 'SLC', attributes: { prediction: 5875 }, }, { - id: 119, + id: '119', from: 'BPX', to: 'CTU', attributes: { prediction: 2991 }, }, { - id: 120, + id: '120', from: 'BRU', to: 'KGL', attributes: { prediction: 2595 }, }, { - id: 121, + id: '121', from: 'BTS', to: 'NYO', attributes: { prediction: 1719 }, }, { - id: 122, + id: '122', from: 'BUD', to: 'FCO', attributes: { prediction: 10501 }, }, { - id: 123, + id: '123', from: 'CLT', to: 'MGM', attributes: { prediction: 2016 }, }, { - id: 124, + id: '124', from: 'BWI', to: 'SDF', attributes: { prediction: 6945 }, }, { - id: 125, + id: '125', from: 'CCS', to: 'EWR', attributes: { prediction: 1018 }, }, { - id: 126, + id: '126', from: 'CDG', to: 'TRS', attributes: { prediction: 1725 }, }, { - id: 127, + id: '127', from: 'CEB', to: 'OZC', attributes: { prediction: 2072 }, }, { - id: 128, + id: '128', from: 'CGK', to: 'PKY', attributes: { prediction: 3264 }, }, { - id: 129, + id: '129', from: 'CMN', to: 'ORY', attributes: { prediction: 26831 }, }, { - id: 130, + id: '130', from: 'COQ', to: 'ULN', attributes: { prediction: 201 }, }, { - id: 131, + id: '131', from: 'CPH', to: 'BGO', attributes: { prediction: 12329 }, }, { - id: 132, + id: '132', from: 'CPT', to: 'DXB', attributes: { prediction: 9583 }, }, { - id: 133, + id: '133', from: 'CRW', to: 'IAD', attributes: { prediction: 2409 }, }, { - id: 134, + id: '134', from: 'VCE', to: 'NAP', attributes: { prediction: 14043 }, }, { - id: 135, + id: '135', from: 'VDM', to: 'BHI', attributes: { prediction: 283 }, }, { - id: 136, + id: '136', from: 'VGO', to: 'CDG', attributes: { prediction: 3353 }, }, { - id: 137, + id: '137', from: 'VKO', to: 'HMA', attributes: { prediction: 2523 }, }, { - id: 138, + id: '138', from: 'ZIH', to: 'PHX', attributes: { prediction: 1451 }, }, { - id: 139, + id: '139', from: 'ZIH', to: 'YYC', attributes: { prediction: 1432 }, }, { - id: 140, + id: '140', from: 'DIK', to: 'ISN', attributes: { prediction: 286 }, }, { - id: 141, + id: '141', from: 'DIR', to: 'ABK', attributes: { prediction: 182 }, }, { - id: 142, + id: '142', from: 'CSX', to: 'YIW', attributes: { prediction: 830 }, }, { - id: 143, + id: '143', from: 'CTS', to: 'TPE', attributes: { prediction: 10977 }, }, { - id: 144, + id: '144', from: 'CUF', to: 'BBU', attributes: { prediction: 934 }, }, { - id: 145, + id: '145', from: 'CUR', to: 'YYZ', attributes: { prediction: 1038 }, }, { - id: 146, + id: '146', from: 'CWB', to: 'CAC', attributes: { prediction: 1004 }, }, { - id: 147, + id: '147', from: 'CZL', to: 'LYS', attributes: { prediction: 2316 }, }, { - id: 148, + id: '148', from: 'DAC', to: 'BKK', attributes: { prediction: 10593 }, }, { - id: 149, + id: '149', from: 'DAC', to: 'JED', attributes: { prediction: 5880 }, }, { - id: 150, + id: '150', from: 'DAM', to: 'CDG', attributes: { prediction: 1958 }, }, { - id: 151, + id: '151', from: 'DAM', to: 'LCA', attributes: { prediction: 2466 }, }, { - id: 152, + id: '152', from: 'DCA', to: 'BUF', attributes: { prediction: 4445 }, }, { - id: 153, + id: '153', from: 'DEN', to: 'DAY', attributes: { prediction: 4061 }, }, { - id: 154, + id: '154', from: 'DEN', to: 'SLC', attributes: { prediction: 55602 }, }, { - id: 155, + id: '155', from: 'DLC', to: 'WUH', attributes: { prediction: 6212 }, }, { - id: 156, + id: '156', from: 'DLH', to: 'LAS', attributes: { prediction: 784 }, }, { - id: 157, + id: '157', from: 'TUP', to: 'ATL', attributes: { prediction: 601 }, }, { - id: 158, + id: '158', from: 'COK', to: 'AUH', attributes: { prediction: 6538 }, }, { - id: 159, + id: '159', from: 'KBP', to: 'AUH', attributes: { prediction: 3072 }, }, { - id: 160, + id: '160', from: 'EWR', to: 'AUS', attributes: { prediction: 5781 }, }, { - id: 161, + id: '161', from: 'ALG', to: 'ETZ', attributes: { prediction: 839 }, }, { - id: 162, + id: '162', from: 'DXB', to: 'EVN', attributes: { prediction: 1481 }, }, { - id: 163, + id: '163', from: 'LED', to: 'EVN', attributes: { prediction: 799 }, }, { - id: 164, + id: '164', from: 'BWI', to: 'EWR', attributes: { prediction: 3213 }, }, { - id: 165, + id: '165', from: 'CLE', to: 'EWR', attributes: { prediction: 13730 }, }, { - id: 166, + id: '166', from: 'DXB', to: 'MUC', attributes: { prediction: 22894 }, }, { - id: 167, + id: '167', from: 'EBL', to: 'BEY', attributes: { prediction: 286 }, }, { - id: 168, + id: '168', from: 'EDI', to: 'CMF', attributes: { prediction: 651 }, }, { - id: 169, + id: '169', from: 'ETZ', to: 'NCE', attributes: { prediction: 549 }, }, { - id: 170, + id: '170', from: 'EVN', to: 'HRK', attributes: { prediction: 258 }, }, { - id: 171, + id: '171', from: 'EVN', to: 'KBP', attributes: { prediction: 1091 }, }, { - id: 172, + id: '172', from: 'EWR', to: 'LGA', attributes: { prediction: 2 }, }, { - id: 173, + id: '173', from: 'EWR', to: 'TUL', attributes: { prediction: 888 }, }, { - id: 174, + id: '174', from: 'GND', to: 'JFK', attributes: { prediction: 1331 }, }, { - id: 175, + id: '175', from: 'GRU', to: 'SCL', attributes: { prediction: 25868 }, }, { - id: 176, + id: '176', from: 'GSO', to: 'FLL', attributes: { prediction: 36 }, }, { - id: 177, + id: '177', from: 'FKB', to: 'ALC', attributes: { prediction: 2688 }, }, { - id: 178, + id: '178', from: 'FKI', to: 'GOM', attributes: { prediction: 1327 }, }, { - id: 179, + id: '179', from: 'FRA', to: 'TSE', attributes: { prediction: 4767 }, }, { - id: 180, + id: '180', from: 'FRA', to: 'XRY', attributes: { prediction: 735 }, }, { - id: 181, + id: '181', from: 'FRO', to: 'OSL', attributes: { prediction: 2774 }, }, { - id: 182, + id: '182', from: 'FUE', to: 'ZRH', attributes: { prediction: 407 }, }, { - id: 183, + id: '183', from: 'GAJ', to: 'NKM', attributes: { prediction: 830 }, }, { - id: 184, + id: '184', from: 'GCM', to: 'MBJ', attributes: { prediction: 302 }, }, { - id: 185, + id: '185', from: 'GDL', to: 'SMF', attributes: { prediction: 2823 }, }, { - id: 186, + id: '186', from: 'GIZ', to: 'DMM', attributes: { prediction: 1737 }, }, { - id: 187, + id: '187', from: 'GLK', to: 'JIB', attributes: { prediction: 381 }, }, { - id: 188, + id: '188', from: 'GZO', to: 'BAS', attributes: { prediction: 72 }, }, { - id: 189, + id: '189', from: 'JOG', to: 'CGK', attributes: { prediction: 82842 }, }, { - id: 190, + id: '190', from: 'KHH', to: 'KMG', attributes: { prediction: 144 }, }, { - id: 191, + id: '191', from: 'KHN', to: 'HKG', attributes: { prediction: 1055 }, }, { - id: 192, + id: '192', from: 'KHZ', to: 'MKP', attributes: { prediction: 48 }, }, { - id: 193, + id: '193', from: 'HGH', to: 'WNZ', attributes: { prediction: 1208 }, }, { - id: 194, + id: '194', from: 'HKG', to: 'NAN', attributes: { prediction: 1729 }, }, { - id: 195, + id: '195', from: 'HOB', to: 'CNM', attributes: { prediction: 387 }, }, { - id: 196, + id: '196', from: 'HPN', to: 'PHL', attributes: { prediction: 2453 }, }, { - id: 197, + id: '197', from: 'HSV', to: 'ORD', attributes: { prediction: 3797 }, }, { - id: 198, + id: '198', from: 'HTY', to: 'IST', attributes: { prediction: 9676 }, }, { - id: 199, + id: '199', from: 'IAD', to: 'BUF', attributes: { prediction: 4058 }, }, { - id: 200, + id: '200', from: 'IAH', to: 'SMF', attributes: { prediction: 12616 }, }, { - id: 201, + id: '201', from: 'IAH', to: 'VER', attributes: { prediction: 1111 }, }, { - id: 202, + id: '202', from: 'ICN', to: 'OKA', attributes: { prediction: 2677 }, }, { - id: 203, + id: '203', from: 'KIX', to: 'DOH', attributes: { prediction: 5789 }, }, { - id: 204, + id: '204', from: 'KKR', to: 'RGI', attributes: { prediction: 27 }, }, { - id: 205, + id: '205', from: 'KLU', to: 'TXL', attributes: { prediction: 2212 }, }, { - id: 206, + id: '206', from: 'IKA', to: 'SAW', attributes: { prediction: 851 }, }, { - id: 207, + id: '207', from: 'IND', to: 'FWA', attributes: { prediction: 23 }, }, { - id: 208, + id: '208', from: 'IND', to: 'TPA', attributes: { prediction: 8287 }, }, { - id: 209, + id: '209', from: 'IQQ', to: 'CPO', attributes: { prediction: 1700 }, }, { - id: 210, + id: '210', from: 'ISG', to: 'MMY', attributes: { prediction: 2772 }, }, { - id: 211, + id: '211', from: 'IXS', to: 'CCU', attributes: { prediction: 2583 }, }, { - id: 212, + id: '212', from: 'JFK', to: 'PVR', attributes: { prediction: 130 }, }, { - id: 213, + id: '213', from: 'JFK', to: 'YUL', attributes: { prediction: 4841 }, }, { - id: 214, + id: '214', from: 'JIB', to: 'ADE', attributes: { prediction: 332 }, }, { - id: 215, + id: '215', from: 'JJU', to: 'UAK', attributes: { prediction: 356 }, }, { - id: 216, + id: '216', from: 'KMG', to: 'HFE', attributes: { prediction: 6540 }, }, { - id: 217, + id: '217', from: 'KMI', to: 'FUK', attributes: { prediction: 11491 }, }, { - id: 218, + id: '218', from: 'KOT', to: 'BET', attributes: { prediction: 90 }, }, { - id: 219, + id: '219', from: 'KRK', to: 'BGO', attributes: { prediction: 1045 }, }, { - id: 220, + id: '220', from: 'KRR', to: 'UFA', attributes: { prediction: 615 }, }, { - id: 221, + id: '221', from: 'TLL', to: 'GOT', attributes: { prediction: 399 }, }, { - id: 222, + id: '222', from: 'JNB', to: 'GRJ', attributes: { prediction: 12980 }, }, { - id: 223, + id: '223', from: 'CAG', to: 'GRO', attributes: { prediction: 4152 }, }, { - id: 224, + id: '224', from: 'TLV', to: 'GRZ', attributes: { prediction: 243 }, }, { - id: 225, + id: '225', from: 'MCO', to: 'GSP', attributes: { prediction: 1248 }, }, { - id: 226, + id: '226', from: 'LHR', to: 'DTW', attributes: { prediction: 4770 }, }, { - id: 227, + id: '227', from: 'LHR', to: 'EDI', attributes: { prediction: 43251 }, }, { - id: 228, + id: '228', from: 'LHR', to: 'SVG', attributes: { prediction: 4734 }, }, { - id: 229, + id: '229', from: 'LHW', to: 'HAK', attributes: { prediction: 1372 }, }, { - id: 230, + id: '230', from: 'LCA', to: 'AUH', attributes: { prediction: 1608 }, }, { - id: 231, + id: '231', from: 'LED', to: 'GOJ', attributes: { prediction: 127 }, }, { - id: 232, + id: '232', from: 'LEX', to: 'ORD', attributes: { prediction: 2780 }, }, { - id: 233, + id: '233', from: 'LGW', to: 'LIN', attributes: { prediction: 3361 }, }, { - id: 234, + id: '234', from: 'LIM', to: 'PTY', attributes: { prediction: 12750 }, }, { - id: 235, + id: '235', from: 'GRU', to: 'GYN', attributes: { prediction: 11993 }, }, { - id: 236, + id: '236', from: 'RUN', to: 'HAH', attributes: { prediction: 773 }, }, { - id: 237, + id: '237', from: 'CTU', to: 'HAK', attributes: { prediction: 12961 }, }, { - id: 238, + id: '238', from: 'SPC', to: 'HAM', attributes: { prediction: 1195 }, }, { - id: 239, + id: '239', from: 'LPA', to: 'HEL', attributes: { prediction: 1590 }, }, { - id: 240, + id: '240', from: 'MXP', to: 'OTP', attributes: { prediction: 5053 }, }, { - id: 241, + id: '241', from: 'MAA', to: 'GOI', attributes: { prediction: 2706 }, }, { - id: 242, + id: '242', from: 'MAD', to: 'HAM', attributes: { prediction: 1453 }, }, { - id: 243, + id: '243', from: 'MAD', to: 'ZAZ', attributes: { prediction: 1114 }, }, { - id: 244, + id: '244', from: 'MAN', to: 'BGI', attributes: { prediction: 1937 }, }, { - id: 245, + id: '245', from: 'MAR', to: 'LSP', attributes: { prediction: 1273 }, }, { - id: 246, + id: '246', from: 'MCM', to: 'NCE', attributes: { prediction: 566 }, }, { - id: 247, + id: '247', from: 'MCO', to: 'BOS', attributes: { prediction: 28652 }, }, { - id: 248, + id: '248', from: 'MCO', to: 'YYZ', attributes: { prediction: 26354 }, }, { - id: 249, + id: '249', from: 'MCT', to: 'BLR', attributes: { prediction: 2195 }, }, { - id: 250, + id: '250', from: 'MCT', to: 'DAC', attributes: { prediction: 1092 }, }, { - id: 251, + id: '251', from: 'MDE', to: 'BAQ', attributes: { prediction: 1857 }, }, { - id: 252, + id: '252', from: 'MEL', to: 'BME', attributes: { prediction: 403 }, }, { - id: 253, + id: '253', from: 'MEM', to: 'AEX', attributes: { prediction: 1976 }, }, { - id: 254, + id: '254', from: 'MEM', to: 'PHL', attributes: { prediction: 3466 }, }, { - id: 255, + id: '255', from: 'MEX', to: 'HUX', attributes: { prediction: 9436 }, }, { - id: 256, + id: '256', from: 'MZV', to: 'MYY', attributes: { prediction: 2253 }, }, { - id: 257, + id: '257', from: 'NAN', to: 'KDV', attributes: { prediction: 292 }, }, { - id: 258, + id: '258', from: 'NAY', to: 'UYN', attributes: { prediction: 1789 }, }, { - id: 259, + id: '259', from: 'MHK', to: 'MCK', attributes: { prediction: 342 }, }, { - id: 260, + id: '260', from: 'MIA', to: 'UVF', attributes: { prediction: 5299 }, }, { - id: 261, + id: '261', from: 'MJF', to: 'SSJ', attributes: { prediction: 168 }, }, { - id: 262, + id: '262', from: 'MKE', to: 'BWI', attributes: { prediction: 12409 }, }, { - id: 263, + id: '263', from: 'MLE', to: 'FCO', attributes: { prediction: 2441 }, }, { - id: 264, + id: '264', from: 'MOB', to: 'LAX', attributes: { prediction: 117 }, }, { - id: 265, + id: '265', from: 'MSN', to: 'ORD', attributes: { prediction: 10135 }, }, { - id: 266, + id: '266', from: 'MSP', to: 'CWA', attributes: { prediction: 2462 }, }, { - id: 267, + id: '267', from: 'MUC', to: 'HKT', attributes: { prediction: 3804 }, }, { - id: 268, + id: '268', from: 'NBO', to: 'LKG', attributes: { prediction: 1046 }, }, { - id: 269, + id: '269', from: 'NOU', to: 'NRT', attributes: { prediction: 3692 }, }, { - id: 270, + id: '270', from: 'OTZ', to: 'IAN', attributes: { prediction: 123 }, }, { - id: 271, + id: '271', from: 'FIH', to: 'LOS', attributes: { prediction: 1045 }, }, { - id: 272, + id: '272', from: 'GRO', to: 'LPA', attributes: { prediction: 2065 }, }, { - id: 273, + id: '273', from: 'SDR', to: 'LPA', attributes: { prediction: 174 }, }, { - id: 274, + id: '274', from: 'MFU', to: 'LUN', attributes: { prediction: 410 }, }, { - id: 275, + id: '275', from: 'YYZ', to: 'MHT', attributes: { prediction: 658 }, }, { - id: 276, + id: '276', from: 'BUR', to: 'MIA', attributes: { prediction: 10 }, }, { - id: 277, + id: '277', from: 'PAD', to: 'FUE', attributes: { prediction: 1875 }, }, { - id: 278, + id: '278', from: 'PBI', to: 'CVG', attributes: { prediction: 1561 }, }, { - id: 279, + id: '279', from: 'PBM', to: 'CAY', attributes: { prediction: 413 }, }, { - id: 280, + id: '280', from: 'PDA', to: 'VVC', attributes: { prediction: 636 }, }, { - id: 281, + id: '281', from: 'NUX', to: 'UFA', attributes: { prediction: 477 }, }, { - id: 282, + id: '282', from: 'NYU', to: 'MDL', attributes: { prediction: 3681 }, }, { - id: 283, + id: '283', from: 'OAK', to: 'LGB', attributes: { prediction: 9271 }, }, { - id: 284, + id: '284', from: 'OKC', to: 'LNK', attributes: { prediction: 38 }, }, { - id: 285, + id: '285', from: 'OLH', to: 'ADQ', attributes: { prediction: 218 }, }, { - id: 286, + id: '286', from: 'ONT', to: 'DFW', attributes: { prediction: 13072 }, }, { - id: 287, + id: '287', from: 'OOL', to: 'NRT', attributes: { prediction: 6358 }, }, { - id: 288, + id: '288', from: 'ORD', to: 'NRT', attributes: { prediction: 32584 }, }, { - id: 289, + id: '289', from: 'ORY', to: 'PGX', attributes: { prediction: 407 }, }, { - id: 290, + id: '290', from: 'OSL', to: 'BCN', attributes: { prediction: 2645 }, }, { - id: 291, + id: '291', from: 'OSL', to: 'MAN', attributes: { prediction: 1879 }, }, { - id: 292, + id: '292', from: 'OSL', to: 'TRD', attributes: { prediction: 49883 }, }, { - id: 293, + id: '293', from: 'OSR', to: 'MUC', attributes: { prediction: 253 }, }, { - id: 294, + id: '294', from: 'OVB', to: 'PEK', attributes: { prediction: 2291 }, }, { - id: 295, + id: '295', from: 'SEA', to: 'MIA', attributes: { prediction: 3685 }, }, { - id: 296, + id: '296', from: 'FLL', to: 'MKE', attributes: { prediction: 4123 }, }, { - id: 297, + id: '297', from: 'STL', to: 'MKE', attributes: { prediction: 4155 }, }, { - id: 298, + id: '298', from: 'CTA', to: 'MLA', attributes: { prediction: 3127 }, }, { - id: 299, + id: '299', from: 'ESB', to: 'MLX', attributes: { prediction: 1967 }, }, { - id: 300, + id: '300', from: 'PEK', to: 'MNL', attributes: { prediction: 3139 }, }, { - id: 301, + id: '301', from: 'MXM', to: 'MOQ', attributes: { prediction: 19 }, }, { - id: 302, + id: '302', from: 'RVK', to: 'MQN', attributes: { prediction: 109 }, }, { - id: 303, + id: '303', from: 'BCN', to: 'MRS', attributes: { prediction: 1056 }, }, { - id: 304, + id: '304', from: 'LIS', to: 'MRS', attributes: { prediction: 3067 }, }, { - id: 305, + id: '305', from: 'DUR', to: 'MRU', attributes: { prediction: 521 }, }, { - id: 306, + id: '306', from: 'BJI', to: 'MSP', attributes: { prediction: 1056 }, }, { - id: 307, + id: '307', from: 'MZT', to: 'MSP', attributes: { prediction: 2887 }, }, { - id: 308, + id: '308', from: 'CMN', to: 'NKC', attributes: { prediction: 3555 }, }, { - id: 309, + id: '309', from: 'PSC', to: 'SEA', attributes: { prediction: 8034 }, }, { - id: 310, + id: '310', from: 'STL', to: 'SEA', attributes: { prediction: 2671 }, }, { - id: 311, + id: '311', from: 'SHA', to: 'JIU', attributes: { prediction: 1111 }, }, { - id: 312, + id: '312', from: 'SHA', to: 'LYI', attributes: { prediction: 2837 }, }, { - id: 313, + id: '313', from: 'SHA', to: 'WEH', attributes: { prediction: 2091 }, }, { - id: 314, + id: '314', from: 'SHE', to: 'HGH', attributes: { prediction: 15577 }, }, { - id: 315, + id: '315', from: 'POP', to: 'MIA', attributes: { prediction: 3387 }, }, { - id: 316, + id: '316', from: 'POS', to: 'CUR', attributes: { prediction: 2047 }, }, { - id: 317, + id: '317', from: 'POW', to: 'BEG', attributes: { prediction: 115 }, }, { - id: 318, + id: '318', from: 'PPT', to: 'KXU', attributes: { prediction: 48 }, }, { - id: 319, + id: '319', from: 'PRG', to: 'MAD', attributes: { prediction: 9526 }, }, { - id: 320, + id: '320', from: 'PRG', to: 'STN', attributes: { prediction: 4510 }, }, { - id: 321, + id: '321', from: 'PSA', to: 'EDI', attributes: { prediction: 1300 }, }, { - id: 322, + id: '322', from: 'PSA', to: 'EIN', attributes: { prediction: 4266 }, }, { - id: 323, + id: '323', from: 'PSA', to: 'HHN', attributes: { prediction: 4325 }, }, { - id: 324, + id: '324', from: 'PSA', to: 'MAD', attributes: { prediction: 902 }, }, { - id: 325, + id: '325', from: 'PTP', to: 'PAP', attributes: { prediction: 4025 }, }, { - id: 326, + id: '326', from: 'PTY', to: 'IAH', attributes: { prediction: 10810 }, }, { - id: 327, + id: '327', from: 'PYY', to: 'CNX', attributes: { prediction: 339 }, }, { - id: 328, + id: '328', from: 'QSF', to: 'LYS', attributes: { prediction: 3622 }, }, { - id: 329, + id: '329', from: 'RAI', to: 'BOS', attributes: { prediction: 1412 }, }, { - id: 330, + id: '330', from: 'RAK', to: 'EMA', attributes: { prediction: 1233 }, }, { - id: 331, + id: '331', from: 'RAP', to: 'DEN', attributes: { prediction: 8645 }, }, { - id: 332, + id: '332', from: 'RBQ', to: 'LPB', attributes: { prediction: 1541 }, }, { - id: 333, + id: '333', from: 'RDV', to: 'SLQ', attributes: { prediction: 5 }, }, { - id: 334, + id: '334', from: 'SHJ', to: 'GOI', attributes: { prediction: 1542 }, }, { - id: 335, + id: '335', from: 'RSW', to: 'ORD', attributes: { prediction: 16617 }, }, { - id: 336, + id: '336', from: 'RUT', to: 'BOS', attributes: { prediction: 322 }, }, { - id: 337, + id: '337', from: 'RVK', to: 'MQN', attributes: { prediction: 107 }, }, { - id: 338, + id: '338', from: 'SAL', to: 'IAH', attributes: { prediction: 12641 }, }, { - id: 339, + id: '339', from: 'SAN', to: 'MCO', attributes: { prediction: 107 }, }, { - id: 340, + id: '340', from: 'SCU', to: 'FCO', attributes: { prediction: 1380 }, }, { - id: 341, + id: '341', from: 'SDR', to: 'ALC', attributes: { prediction: 3169 }, }, { - id: 342, + id: '342', from: 'SEA', to: 'FAI', attributes: { prediction: 6606 }, }, { - id: 343, + id: '343', from: 'SEA', to: 'HLN', attributes: { prediction: 1308 }, }, { - id: 344, + id: '344', from: 'SJU', to: 'SLU', attributes: { prediction: 2119 }, }, { - id: 345, + id: '345', from: 'SJW', to: 'CAN', attributes: { prediction: 2703 }, }, { - id: 346, + id: '346', from: 'SKK', to: 'UNK', attributes: { prediction: 62 }, }, { - id: 347, + id: '347', from: 'SLU', to: 'BGI', attributes: { prediction: 3838 }, }, { - id: 348, + id: '348', from: 'YXL', to: 'SUR', attributes: { prediction: 113 }, }, { - id: 349, + id: '349', from: 'KDV', to: 'SUV', attributes: { prediction: 415 }, }, { - id: 350, + id: '350', from: 'FCO', to: 'SVQ', attributes: { prediction: 3739 }, }, { - id: 351, + id: '351', from: 'PEK', to: 'SWA', attributes: { prediction: 6730 }, }, { - id: 352, + id: '352', from: 'ABX', to: 'SYD', attributes: { prediction: 7627 }, }, { - id: 353, + id: '353', from: 'POM', to: 'SYD', attributes: { prediction: 1284 }, }, { - id: 354, + id: '354', from: 'LRR', to: 'SYZ', attributes: { prediction: 177 }, }, { - id: 355, + id: '355', from: 'GUM', to: 'TPE', attributes: { prediction: 4268 }, }, { - id: 356, + id: '356', from: 'STN', to: 'KIR', attributes: { prediction: 2557 }, }, { - id: 357, + id: '357', from: 'STN', to: 'TFS', attributes: { prediction: 5514 }, }, { - id: 358, + id: '358', from: 'STR', to: 'SPC', attributes: { prediction: 694 }, }, { - id: 359, + id: '359', from: 'SVO', to: 'LEJ', attributes: { prediction: 928 }, }, { - id: 360, + id: '360', from: 'SWJ', to: 'NUS', attributes: { prediction: 39 }, }, { - id: 361, + id: '361', from: 'SYD', to: 'ARM', attributes: { prediction: 1681 }, }, { - id: 362, + id: '362', from: 'SYX', to: 'SHE', attributes: { prediction: 1562 }, }, { - id: 363, + id: '363', from: 'SZX', to: 'FOC', attributes: { prediction: 15783 }, }, { - id: 364, + id: '364', from: 'DUB', to: 'TRF', attributes: { prediction: 2163 }, }, { - id: 365, + id: '365', from: 'TSA', to: 'TSN', attributes: { prediction: 967 }, }, { - id: 366, + id: '366', from: 'FLR', to: 'TSR', attributes: { prediction: 843 }, }, { - id: 367, + id: '367', from: 'GVA', to: 'TUN', attributes: { prediction: 3547 }, }, { - id: 368, + id: '368', from: 'ELQ', to: 'TUU', attributes: { prediction: 571 }, }, { - id: 369, + id: '369', from: 'FRL', to: 'TXL', attributes: { prediction: 973 }, }, { - id: 370, + id: '370', from: 'SOF', to: 'TXL', attributes: { prediction: 2077 }, }, { - id: 371, + id: '371', from: 'UDI', to: 'UBA', attributes: { prediction: 1992 }, }, { - id: 372, + id: '372', from: 'DME', to: 'UGC', attributes: { prediction: 791 }, }, { - id: 373, + id: '373', from: 'XMS', to: 'UIO', attributes: { prediction: 262 }, }, { - id: 374, + id: '374', from: 'KGF', to: 'UKK', attributes: { prediction: 189 }, }, { - id: 375, + id: '375', from: 'WNR', to: 'ULP', attributes: { prediction: 170 }, }, { - id: 376, + id: '376', from: 'LBD', to: 'URC', attributes: { prediction: 158 }, }, { - id: 377, + id: '377', from: 'DME', to: 'UUA', attributes: { prediction: 441 }, }, { - id: 378, + id: '378', from: 'BKK', to: 'XMN', attributes: { prediction: 2189 }, }, { - id: 379, + id: '379', from: 'YNJ', to: 'ICN', attributes: { prediction: 8334 }, }, { - id: 380, + id: '380', from: 'YNT', to: 'CGQ', attributes: { prediction: 116 }, }, { - id: 381, + id: '381', from: 'YOW', to: 'DCA', attributes: { prediction: 1401 }, }, { - id: 382, + id: '382', from: 'TSN', to: 'KUL', attributes: { prediction: 6717 }, }, { - id: 383, + id: '383', from: 'TSN', to: 'SZX', attributes: { prediction: 17715 }, }, { - id: 384, + id: '384', from: 'TUP', to: 'ATL', attributes: { prediction: 573 }, }, { - id: 385, + id: '385', from: 'YQB', to: 'YOW', attributes: { prediction: 920 }, }, { - id: 386, + id: '386', from: 'YTE', to: 'YFB', attributes: { prediction: 588 }, }, { - id: 387, + id: '387', from: 'YVR', to: 'IAH', attributes: { prediction: 4260 }, }, { - id: 388, + id: '388', from: 'YVR', to: 'MSP', attributes: { prediction: 1718 }, }, { - id: 389, + id: '389', from: 'VOZ', to: 'LED', attributes: { prediction: 448 }, }, { - id: 390, + id: '390', from: 'VSG', to: 'KBP', attributes: { prediction: 1026 }, }, { - id: 391, + id: '391', from: 'WAW', to: 'RIX', attributes: { prediction: 2677 }, }, { - id: 392, + id: '392', from: 'WNZ', to: 'HAK', attributes: { prediction: 3608 }, }, { - id: 393, + id: '393', from: 'WRO', to: 'DTM', attributes: { prediction: 1652 }, }, { - id: 394, + id: '394', from: 'WRO', to: 'STN', attributes: { prediction: 4712 }, }, { - id: 395, + id: '395', from: 'WUH', to: 'XFN', attributes: { prediction: 240 }, }, { - id: 396, + id: '396', from: 'XKS', to: 'WNN', attributes: { prediction: 44 }, }, { - id: 397, + id: '397', from: 'YEV', to: 'YHI', attributes: { prediction: 86 }, }, { - id: 398, + id: '398', from: 'YHD', to: 'YQT', attributes: { prediction: 169 }, }, { - id: 399, + id: '399', from: 'YHZ', to: 'IAD', attributes: { prediction: 190 }, }, { - id: 400, + id: '400', from: 'YXS', to: 'YXJ', attributes: { prediction: 213 }, }, { - id: 401, + id: '401', from: 'YXY', to: 'YFS', attributes: { prediction: 139 }, }, { - id: 402, + id: '402', from: 'YYC', to: 'YVR', attributes: { prediction: 60230 }, }, { - id: 403, + id: '403', from: 'YYZ', to: 'ANU', attributes: { prediction: 1355 }, }, { - id: 404, + id: '404', from: 'YYZ', to: 'PVG', attributes: { prediction: 3161 }, }, { - id: 405, + id: '405', from: 'FOC', to: 'XUZ', attributes: { prediction: 1354 }, }, { - id: 406, + id: '406', from: 'YBX', to: 'YAY', attributes: { prediction: 630 }, }, { - id: 407, + id: '407', from: 'GLA', to: 'AMS', attributes: { prediction: 9297 }, }, { - id: 408, + id: '408', from: 'LGW', to: 'AMS', attributes: { prediction: 19761 }, }, { - id: 409, + id: '409', from: 'STN', to: 'AMS', attributes: { prediction: 8014 }, }, { - id: 410, + id: '410', from: 'LFW', to: 'ABJ', attributes: { prediction: 1940 }, }, { - id: 411, + id: '411', from: 'HYA', to: 'ACK', attributes: { prediction: 2036 }, }, { - id: 412, + id: '412', from: 'AFA', to: 'AEP', attributes: { prediction: 781 }, }, { - id: 413, + id: '413', from: 'CDG', to: 'AGP', attributes: { prediction: 12126 }, }, { - id: 414, + id: '414', from: 'MDW', to: 'ALB', attributes: { prediction: 4248 }, }, { - id: 415, + id: '415', from: 'ACE', to: 'ALC', attributes: { prediction: 177 }, }, { - id: 416, + id: '416', from: 'TRD', to: 'ALC', attributes: { prediction: 337 }, }, { - id: 417, + id: '417', from: 'AGP', to: 'AMS', attributes: { prediction: 5834 }, }, { - id: 418, + id: '418', from: 'BGO', to: 'AMS', attributes: { prediction: 9580 }, }, { - id: 419, + id: '419', from: 'PHX', to: 'ANC', attributes: { prediction: 2599 }, }, { - id: 420, + id: '420', from: 'SXM', to: 'ANU', attributes: { prediction: 4105 }, }, { - id: 421, + id: '421', from: 'ITM', to: 'AOJ', attributes: { prediction: 4041 }, }, { - id: 422, + id: '422', from: 'KRN', to: 'ARN', attributes: { prediction: 5791 }, }, { - id: 423, + id: '423', from: 'CAI', to: 'ASW', attributes: { prediction: 17584 }, }, { - id: 424, + id: '424', from: 'CAI', to: 'BGW', attributes: { prediction: 1197 }, }, { - id: 425, + id: '425', from: 'MAD', to: 'BVA', attributes: { prediction: 5200 }, }, { - id: 426, + id: '426', from: 'MEM', to: 'BWI', attributes: { prediction: 4300 }, }, { - id: 427, + id: '427', from: 'ZHA', to: 'CAN', attributes: { prediction: 7045 }, }, { - id: 428, + id: '428', from: 'MDE', to: 'CCS', attributes: { prediction: 1100 }, }, { - id: 429, + id: '429', from: 'TYN', to: 'BAV', attributes: { prediction: 2610 }, }, { - id: 430, + id: '430', from: 'ISB', to: 'BCN', attributes: { prediction: 897 }, }, { - id: 431, + id: '431', from: 'VIE', to: 'BCN', attributes: { prediction: 8619 }, }, { - id: 432, + id: '432', from: 'MCO', to: 'BDL', attributes: { prediction: 13958 }, }, { - id: 433, + id: '433', from: 'LGW', to: 'BGI', attributes: { prediction: 19270 }, }, { - id: 434, + id: '434', from: 'BLL', to: 'BGY', attributes: { prediction: 1724 }, }, { - id: 435, + id: '435', from: 'MSY', to: 'BHM', attributes: { prediction: 4582 }, }, { - id: 436, + id: '436', from: 'FNC', to: 'BHX', attributes: { prediction: 822 }, }, { - id: 437, + id: '437', from: 'MCO', to: 'BKG', attributes: { prediction: 298 }, }, { - id: 438, + id: '438', from: 'BBU', to: 'BLQ', attributes: { prediction: 1465 }, }, { - id: 439, + id: '439', from: 'COK', to: 'BLR', attributes: { prediction: 8217 }, }, { - id: 440, + id: '440', from: 'PHX', to: 'BNA', attributes: { prediction: 7493 }, }, { - id: 441, + id: '441', from: 'ADZ', to: 'BOG', attributes: { prediction: 19886 }, }, { - id: 442, + id: '442', from: 'VVC', to: 'BOG', attributes: { prediction: 1875 }, }, { - id: 443, + id: '443', from: 'JAX', to: 'BOS', attributes: { prediction: 2052 }, }, { - id: 444, + id: '444', from: 'TIA', to: 'BRI', attributes: { prediction: 3706 }, }, { - id: 445, + id: '445', from: 'GRO', to: 'BRS', attributes: { prediction: 1690 }, }, { - id: 446, + id: '446', from: 'SID', to: 'BRU', attributes: { prediction: 1268 }, }, { - id: 447, + id: '447', from: 'LYS', to: 'BSK', attributes: { prediction: 484 }, }, { - id: 448, + id: '448', from: 'NCE', to: 'BSL', attributes: { prediction: 1807 }, }, { - id: 449, + id: '449', from: 'SXM', to: 'CDG', attributes: { prediction: 7099 }, }, { - id: 450, + id: '450', from: 'ARN', to: 'CGN', attributes: { prediction: 1709 }, }, { - id: 451, + id: '451', from: 'GGW', to: 'BIL', attributes: { prediction: 327 }, }, { - id: 452, + id: '452', from: 'SEA', to: 'BIL', attributes: { prediction: 3329 }, }, { - id: 453, + id: '453', from: 'MRS', to: 'BJA', attributes: { prediction: 779 }, }, { - id: 454, + id: '454', from: 'VDS', to: 'BJF', attributes: { prediction: 487 }, }, { - id: 455, + id: '455', from: 'KIX', to: 'BKK', attributes: { prediction: 21154 }, }, { - id: 456, + id: '456', from: 'CLJ', to: 'CUF', attributes: { prediction: 765 }, }, { - id: 457, + id: '457', from: 'CVG', to: 'CUN', attributes: { prediction: 2625 }, }, { - id: 458, + id: '458', from: 'DFW', to: 'CUN', attributes: { prediction: 18996 }, }, { - id: 459, + id: '459', from: 'YYJ', to: 'CUN', attributes: { prediction: 445 }, }, { - id: 460, + id: '460', from: 'ISB', to: 'CJL', attributes: { prediction: 707 }, }, { - id: 461, + id: '461', from: 'BSB', to: 'CNF', attributes: { prediction: 35848 }, }, { - id: 462, + id: '462', from: 'CNF', to: 'CPQ', attributes: { prediction: 6375 }, }, { - id: 463, + id: '463', from: 'DEN', to: 'DAL', attributes: { prediction: 102 }, }, { - id: 464, + id: '464', from: 'IND', to: 'DAL', attributes: { prediction: 98 }, }, { - id: 465, + id: '465', from: 'CAN', to: 'DAX', attributes: { prediction: 2891 }, }, { - id: 466, + id: '466', from: 'TPA', to: 'DAY', attributes: { prediction: 1914 }, }, { - id: 467, + id: '467', from: 'JLR', to: 'DEL', attributes: { prediction: 722 }, }, { - id: 468, + id: '468', from: 'BNA', to: 'DEN', attributes: { prediction: 18422 }, }, { - id: 469, + id: '469', from: 'PIA', to: 'DEN', attributes: { prediction: 962 }, }, { - id: 470, + id: '470', from: 'SSH', to: 'BRU', attributes: { prediction: 977 }, }, { - id: 471, + id: '471', from: 'CDG', to: 'BSL', attributes: { prediction: 6922 }, }, { - id: 472, + id: '472', from: 'BEY', to: 'DXB', attributes: { prediction: 34030 }, }, { - id: 473, + id: '473', from: 'JAI', to: 'DXB', attributes: { prediction: 1628 }, }, { - id: 474, + id: '474', from: 'ISU', to: 'EBL', attributes: { prediction: 2227 }, }, { - id: 475, + id: '475', from: 'SAV', to: 'DFW', attributes: { prediction: 2176 }, }, { - id: 476, + id: '476', from: 'SFO', to: 'DFW', attributes: { prediction: 34973 }, }, { - id: 477, + id: '477', from: 'AUH', to: 'DME', attributes: { prediction: 3413 }, }, { - id: 478, + id: '478', from: 'KGF', to: 'DME', attributes: { prediction: 503 }, }, { - id: 479, + id: '479', from: 'VOG', to: 'DME', attributes: { prediction: 5751 }, }, { - id: 480, + id: '480', from: 'BEY', to: 'DOH', attributes: { prediction: 10169 }, }, { - id: 481, + id: '481', from: 'SJO', to: 'DRK', attributes: { prediction: 115 }, }, { - id: 482, + id: '482', from: 'LGA', to: 'DTW', attributes: { prediction: 23366 }, }, { - id: 483, + id: '483', from: 'RSW', to: 'DTW', attributes: { prediction: 27675 }, }, { - id: 484, + id: '484', from: 'EIN', to: 'DUB', attributes: { prediction: 2818 }, }, { - id: 485, + id: '485', from: 'FCO', to: 'DUB', attributes: { prediction: 4672 }, }, { - id: 486, + id: '486', from: 'RIX', to: 'DUB', attributes: { prediction: 4864 }, }, { - id: 487, + id: '487', from: 'AUS', to: 'ELP', attributes: { prediction: 8923 }, }, { - id: 488, + id: '488', from: 'STL', to: 'DSM', attributes: { prediction: 2751 }, }, { - id: 489, + id: '489', from: 'RTM', to: 'FCO', attributes: { prediction: 4189 }, }, { - id: 490, + id: '490', from: 'TAS', to: 'FCO', attributes: { prediction: 760 }, }, { - id: 491, + id: '491', from: 'BNA', to: 'FLL', attributes: { prediction: 7449 }, }, { - id: 492, + id: '492', from: 'MHT', to: 'FLL', attributes: { prediction: 236 }, }, { - id: 493, + id: '493', from: 'PEK', to: 'FOC', attributes: { prediction: 36688 }, }, { - id: 494, + id: '494', from: 'GIG', to: 'FOR', attributes: { prediction: 14641 }, }, { - id: 495, + id: '495', from: 'DFW', to: 'FRA', attributes: { prediction: 12173 }, }, { - id: 496, + id: '496', from: 'ZRH', to: 'FCO', attributes: { prediction: 10996 }, }, { - id: 497, + id: '497', from: 'PVG', to: 'FKS', attributes: { prediction: 457 }, }, { - id: 498, + id: '498', from: 'DKR', to: 'FNA', attributes: { prediction: 1788 }, }, { - id: 499, + id: '499', from: 'DUB', to: 'FNC', attributes: { prediction: 549 }, }, { - id: 500, + id: '500', from: 'STR', to: 'FNC', attributes: { prediction: 914 }, }, { - id: 501, + id: '501', from: 'BCN', to: 'FRA', attributes: { prediction: 21399 }, }, { - id: 502, + id: '502', from: 'FCO', to: 'FRA', attributes: { prediction: 26744 }, }, { - id: 503, + id: '503', from: 'MEX', to: 'FRA', attributes: { prediction: 6862 }, }, { - id: 504, + id: '504', from: 'ROV', to: 'FRA', attributes: { prediction: 1601 }, }, { - id: 505, + id: '505', from: 'PUS', to: 'FUK', attributes: { prediction: 8054 }, }, { - id: 506, + id: '506', from: 'ACI', to: 'GCI', attributes: { prediction: 687 }, }, { - id: 507, + id: '507', from: 'CEN', to: 'GDL', attributes: { prediction: 2012 }, }, { - id: 508, + id: '508', from: 'SNA', to: 'LAX', attributes: { prediction: 17 }, }, { - id: 509, + id: '509', from: 'PUW', to: 'LWS', attributes: { prediction: 1306 }, }, { - id: 510, + id: '510', from: 'KIX', to: 'LXR', attributes: { prediction: 1646 }, }, { - id: 511, + id: '511', from: 'SXB', to: 'LYS', attributes: { prediction: 7887 }, }, { - id: 512, + id: '512', from: 'PEN', to: 'JHB', attributes: { prediction: 5257 }, }, { - id: 513, + id: '513', from: 'MTS', to: 'JNB', attributes: { prediction: 2204 }, }, { - id: 514, + id: '514', from: 'REC', to: 'JPA', attributes: { prediction: 3788 }, }, { - id: 515, + id: '515', from: 'DTM', to: 'KBP', attributes: { prediction: 2009 }, }, { - id: 516, + id: '516', from: 'MUC', to: 'KBP', attributes: { prediction: 5587 }, }, { - id: 517, + id: '517', from: 'PRG', to: 'KBP', attributes: { prediction: 5904 }, }, { - id: 518, + id: '518', from: 'RNL', to: 'HIR', attributes: { prediction: 72 }, }, { - id: 519, + id: '519', from: 'BKK', to: 'HKG', attributes: { prediction: 95668 }, }, { - id: 520, + id: '520', from: 'CEB', to: 'HKG', attributes: { prediction: 10545 }, }, { - id: 521, + id: '521', from: 'PVG', to: 'HKG', attributes: { prediction: 105231 }, }, { - id: 522, + id: '522', from: 'TNA', to: 'HKG', attributes: { prediction: 2203 }, }, { - id: 523, + id: '523', from: 'WUH', to: 'HKG', attributes: { prediction: 4806 }, }, { - id: 524, + id: '524', from: 'AOJ', to: 'HND', attributes: { prediction: 22770 }, }, { - id: 525, + id: '525', from: 'ASJ', to: 'HND', attributes: { prediction: 2974 }, }, { - id: 526, + id: '526', from: 'NTQ', to: 'HND', attributes: { prediction: 3978 }, }, { - id: 527, + id: '527', from: 'TPA', to: 'HPN', attributes: { prediction: 2785 }, }, { - id: 528, + id: '528', from: 'DSN', to: 'HRB', attributes: { prediction: 667 }, }, { - id: 529, + id: '529', from: 'HEK', to: 'HRB', attributes: { prediction: 1611 }, }, { - id: 530, + id: '530', from: 'LAD', to: 'HRE', attributes: { prediction: 333 }, }, { - id: 531, + id: '531', from: 'VFA', to: 'HRE', attributes: { prediction: 481 }, }, { - id: 532, + id: '532', from: 'CLT', to: 'HTS', attributes: { prediction: 2322 }, }, { - id: 533, + id: '533', from: 'ZAH', to: 'KER', attributes: { prediction: 241 }, }, { - id: 534, + id: '534', from: 'PER', to: 'KGI', attributes: { prediction: 6908 }, }, { - id: 535, + id: '535', from: 'FCO', to: 'ICN', attributes: { prediction: 2661 }, }, { - id: 536, + id: '536', from: 'NAG', to: 'IDR', attributes: { prediction: 591 }, }, { - id: 537, + id: '537', from: 'RUH', to: 'ISB', attributes: { prediction: 7546 }, }, { - id: 538, + id: '538', from: 'YYZ', to: 'ISB', attributes: { prediction: 954 }, }, { - id: 539, + id: '539', from: 'MUC', to: 'IST', attributes: { prediction: 18909 }, }, { - id: 540, + id: '540', from: 'IXC', to: 'IXJ', attributes: { prediction: 575 }, }, { - id: 541, + id: '541', from: 'DEN', to: 'JAC', attributes: { prediction: 4336 }, }, { - id: 542, + id: '542', from: 'JCH', to: 'JAV', attributes: { prediction: 62 }, }, { - id: 543, + id: '543', from: 'CMB', to: 'JED', attributes: { prediction: 1841 }, }, { - id: 544, + id: '544', from: 'DUB', to: 'JFK', attributes: { prediction: 12974 }, }, { - id: 545, + id: '545', from: 'RDU', to: 'JFK', attributes: { prediction: 11242 }, }, { - id: 546, + id: '546', from: 'IST', to: 'KIV', attributes: { prediction: 6181 }, }, { - id: 547, + id: '547', from: 'HYN', to: 'KMG', attributes: { prediction: 1462 }, }, { - id: 548, + id: '548', from: 'BET', to: 'KPN', attributes: { prediction: 412 }, }, { - id: 549, + id: '549', from: 'BJZ', to: 'MAD', attributes: { prediction: 1598 }, }, { - id: 550, + id: '550', from: 'FCO', to: 'MAD', attributes: { prediction: 50764 }, }, { - id: 551, + id: '551', from: 'NTE', to: 'MAD', attributes: { prediction: 2663 }, }, { - id: 552, + id: '552', from: 'SXB', to: 'MAD', attributes: { prediction: 1026 }, }, { - id: 553, + id: '553', from: 'FCO', to: 'LGW', attributes: { prediction: 11681 }, }, { - id: 554, + id: '554', from: 'LPA', to: 'LGW', attributes: { prediction: 8719 }, }, { - id: 555, + id: '555', from: 'UVF', to: 'LGW', attributes: { prediction: 8471 }, }, { - id: 556, + id: '556', from: 'KBP', to: 'KTW', attributes: { prediction: 1519 }, }, { - id: 557, + id: '557', from: 'BOM', to: 'KWI', attributes: { prediction: 10030 }, }, { - id: 558, + id: '558', from: 'JFK', to: 'KWI', attributes: { prediction: 2414 }, }, { - id: 559, + id: '559', from: 'MTY', to: 'LAP', attributes: { prediction: 787 }, }, { - id: 560, + id: '560', from: 'IDA', to: 'LAS', attributes: { prediction: 915 }, }, { - id: 561, + id: '561', from: 'RNO', to: 'LAS', attributes: { prediction: 31201 }, }, { - id: 562, + id: '562', from: 'TUL', to: 'LAS', attributes: { prediction: 3079 }, }, { - id: 563, + id: '563', from: 'YQR', to: 'LAS', attributes: { prediction: 858 }, }, { - id: 564, + id: '564', from: 'TUL', to: 'LAX', attributes: { prediction: 1088 }, }, { - id: 565, + id: '565', from: 'STN', to: 'LBC', attributes: { prediction: 5664 }, }, { - id: 566, + id: '566', from: 'FRA', to: 'LEJ', attributes: { prediction: 8978 }, }, { - id: 567, + id: '567', from: 'BNA', to: 'LGA', attributes: { prediction: 6074 }, }, { - id: 568, + id: '568', from: 'VCE', to: 'LHR', attributes: { prediction: 2557 }, }, { - id: 569, + id: '569', from: 'WNZ', to: 'LHW', attributes: { prediction: 1072 }, }, { - id: 570, + id: '570', from: 'PIU', to: 'LIM', attributes: { prediction: 8532 }, }, { - id: 571, + id: '571', from: 'FRA', to: 'LIS', attributes: { prediction: 20468 }, }, { - id: 572, + id: '572', from: 'MCT', to: 'LKO', attributes: { prediction: 2802 }, }, { - id: 573, + id: '573', from: 'JRO', to: 'MBA', attributes: { prediction: 3883 }, }, { - id: 574, + id: '574', from: 'DUS', to: 'MLE', attributes: { prediction: 2413 }, }, { - id: 575, + id: '575', from: 'ROR', to: 'MNL', attributes: { prediction: 1116 }, }, { - id: 576, + id: '576', from: 'DPS', to: 'MOF', attributes: { prediction: 853 }, }, { - id: 577, + id: '577', from: 'CDG', to: 'MPL', attributes: { prediction: 13023 }, }, { - id: 578, + id: '578', from: 'VPY', to: 'MPM', attributes: { prediction: 895 }, }, { - id: 579, + id: '579', from: 'BOO', to: 'MQN', attributes: { prediction: 1817 }, }, { - id: 580, + id: '580', from: 'SXB', to: 'MRS', attributes: { prediction: 3391 }, }, { - id: 581, + id: '581', from: 'GVA', to: 'MRU', attributes: { prediction: 949 }, }, { - id: 582, + id: '582', from: 'EWR', to: 'MSN', attributes: { prediction: 1005 }, }, { - id: 583, + id: '583', from: 'MSY', to: 'MEM', attributes: { prediction: 7437 }, }, { - id: 584, + id: '584', from: 'MTT', to: 'MEX', attributes: { prediction: 4561 }, }, { - id: 585, + id: '585', from: 'MKK', to: 'LUP', attributes: { prediction: 112 }, }, { - id: 586, + id: '586', from: 'FRA', to: 'LUX', attributes: { prediction: 3343 }, }, { - id: 587, + id: '587', from: 'CDG', to: 'LYS', attributes: { prediction: 18150 }, }, { - id: 588, + id: '588', from: 'NTE', to: 'LYS', attributes: { prediction: 15488 }, }, { - id: 589, + id: '589', from: 'STR', to: 'MAD', attributes: { prediction: 1242 }, }, { - id: 590, + id: '590', from: 'BRU', to: 'MAN', attributes: { prediction: 7215 }, }, { - id: 591, + id: '591', from: 'BDL', to: 'MCO', attributes: { prediction: 14861 }, }, { - id: 592, + id: '592', from: 'PWM', to: 'MCO', attributes: { prediction: 2517 }, }, { - id: 593, + id: '593', from: 'DXB', to: 'MEL', attributes: { prediction: 5702 }, }, { - id: 594, + id: '594', from: 'YVR', to: 'MSP', attributes: { prediction: 3523 }, }, { - id: 595, + id: '595', from: 'CLE', to: 'MSY', attributes: { prediction: 1095 }, }, { - id: 596, + id: '596', from: 'MOB', to: 'MSY', attributes: { prediction: 54 }, }, { - id: 597, + id: '597', from: 'SLC', to: 'MSY', attributes: { prediction: 1839 }, }, { - id: 598, + id: '598', from: 'VSA', to: 'MTY', attributes: { prediction: 2907 }, }, { - id: 599, + id: '599', from: 'BDU', to: 'OSL', attributes: { prediction: 5442 }, }, { - id: 600, + id: '600', from: 'DXB', to: 'OSL', attributes: { prediction: 1776 }, }, { - id: 601, + id: '601', from: 'AMS', to: 'PBM', attributes: { prediction: 9059 }, }, { - id: 602, + id: '602', from: 'CKG', to: 'PEK', attributes: { prediction: 60119 }, }, { - id: 603, + id: '603', from: 'HFE', to: 'PEK', attributes: { prediction: 26735 }, }, { - id: 604, + id: '604', from: 'DJE', to: 'MUC', attributes: { prediction: 486 }, }, { - id: 605, + id: '605', from: 'LNZ', to: 'MUC', attributes: { prediction: 2235 }, }, { - id: 606, + id: '606', from: 'MRU', to: 'MUC', attributes: { prediction: 1067 }, }, { - id: 607, + id: '607', from: 'FRA', to: 'MXP', attributes: { prediction: 13986 }, }, { - id: 608, + id: '608', from: 'SVO', to: 'MXP', attributes: { prediction: 9785 }, }, { - id: 609, + id: '609', from: 'MIM', to: 'MYA', attributes: { prediction: 717 }, }, { - id: 610, + id: '610', from: 'BBN', to: 'MYY', attributes: { prediction: 418 }, }, { - id: 611, + id: '611', from: 'LED', to: 'NCE', attributes: { prediction: 485 }, }, { - id: 612, + id: '612', from: 'KUL', to: 'NNG', attributes: { prediction: 808 }, }, { - id: 613, + id: '613', from: 'LGW', to: 'NOC', attributes: { prediction: 3209 }, }, { - id: 614, + id: '614', from: 'BNE', to: 'NRT', attributes: { prediction: 5259 }, }, { - id: 615, + id: '615', from: 'IST', to: 'NRT', attributes: { prediction: 3523 }, }, { - id: 616, + id: '616', from: 'PDX', to: 'NRT', attributes: { prediction: 4287 }, }, { - id: 617, + id: '617', from: 'NBO', to: 'NSI', attributes: { prediction: 1105 }, }, { - id: 618, + id: '618', from: 'EGO', to: 'NUX', attributes: { prediction: 709 }, }, { - id: 619, + id: '619', from: 'BTS', to: 'NYO', attributes: { prediction: 1574 }, }, { - id: 620, + id: '620', from: 'MCI', to: 'OKC', attributes: { prediction: 4722 }, }, { - id: 621, + id: '621', from: 'MCO', to: 'OMA', attributes: { prediction: 1340 }, }, { - id: 622, + id: '622', from: 'HHN', to: 'OPO', attributes: { prediction: 3794 }, }, { - id: 623, + id: '623', from: 'AUH', to: 'ORD', attributes: { prediction: 6346 }, }, { - id: 624, + id: '624', from: 'PIT', to: 'ORD', attributes: { prediction: 11560 }, }, { - id: 625, + id: '625', from: 'XNA', to: 'ORD', attributes: { prediction: 9632 }, }, { - id: 626, + id: '626', from: 'ZIH', to: 'ORD', attributes: { prediction: 139 }, }, { - id: 627, + id: '627', from: 'EDI', to: 'ORK', attributes: { prediction: 1298 }, }, { - id: 628, + id: '628', from: 'CVG', to: 'PHL', attributes: { prediction: 6628 }, }, { - id: 629, + id: '629', from: 'RSW', to: 'PHL', attributes: { prediction: 11075 }, }, { - id: 630, + id: '630', from: 'SAN', to: 'PHL', attributes: { prediction: 6503 }, }, { - id: 631, + id: '631', from: 'PIT', to: 'PHX', attributes: { prediction: 9717 }, }, { - id: 632, + id: '632', from: 'CUN', to: 'PIT', attributes: { prediction: 1164 }, }, { - id: 633, + id: '633', from: 'GLF', to: 'PJM', attributes: { prediction: 90 }, }, { - id: 634, + id: '634', from: 'STL', to: 'MWA', attributes: { prediction: 751 }, }, { - id: 635, + id: '635', from: 'MWQ', to: 'RGN', attributes: { prediction: 339 }, }, { - id: 636, + id: '636', from: 'VIE', to: 'RIX', attributes: { prediction: 1932 }, }, { - id: 637, + id: '637', from: 'MKY', to: 'ROK', attributes: { prediction: 1745 }, }, { - id: 638, + id: '638', from: 'BOM', to: 'RPR', attributes: { prediction: 3897 }, }, { - id: 639, + id: '639', from: 'LAS', to: 'RST', attributes: { prediction: 811 }, }, { - id: 640, + id: '640', from: 'ORD', to: 'RSW', attributes: { prediction: 15722 }, }, { - id: 641, + id: '641', from: 'LIM', to: 'POA', attributes: { prediction: 957 }, }, { - id: 642, + id: '642', from: 'HNL', to: 'PPT', attributes: { prediction: 783 }, }, { - id: 643, + id: '643', from: 'RAR', to: 'PPT', attributes: { prediction: 216 }, }, { - id: 644, + id: '644', from: 'GVA', to: 'PRG', attributes: { prediction: 2053 }, }, { - id: 645, + id: '645', from: 'HAJ', to: 'PRG', attributes: { prediction: 1501 }, }, { - id: 646, + id: '646', from: 'MUC', to: 'PSA', attributes: { prediction: 2235 }, }, { - id: 647, + id: '647', from: 'FNC', to: 'PXO', attributes: { prediction: 2713 }, }, { - id: 648, + id: '648', from: 'SJP', to: 'RAO', attributes: { prediction: 624 }, }, { - id: 649, + id: '649', from: 'SHJ', to: 'SAW', attributes: { prediction: 1410 }, }, { - id: 650, + id: '650', from: 'ANC', to: 'SCC', attributes: { prediction: 1392 }, }, { - id: 651, + id: '651', from: 'KMQ', to: 'SDJ', attributes: { prediction: 952 }, }, { - id: 652, + id: '652', from: 'SXM', to: 'SDQ', attributes: { prediction: 2486 }, }, { - id: 653, + id: '653', from: 'FAO', to: 'NYO', attributes: { prediction: 165 }, }, { - id: 654, + id: '654', from: 'SEA', to: 'OAK', attributes: { prediction: 35647 }, }, { - id: 655, + id: '655', from: 'CLE', to: 'SFO', attributes: { prediction: 3190 }, }, { - id: 656, + id: '656', from: 'ORD', to: 'SFO', attributes: { prediction: 61103 }, }, { - id: 657, + id: '657', from: 'YVR', to: 'SFO', attributes: { prediction: 19887 }, }, { - id: 658, + id: '658', from: 'ATL', to: 'SFO', attributes: { prediction: 28467 }, }, { - id: 659, + id: '659', from: 'FAT', to: 'SFO', attributes: { prediction: 3857 }, }, { - id: 660, + id: '660', from: 'DLI', to: 'SGN', attributes: { prediction: 6163 }, }, { - id: 661, + id: '661', from: 'HET', to: 'SHE', attributes: { prediction: 1174 }, }, { - id: 662, + id: '662', from: 'ICN', to: 'SIN', attributes: { prediction: 30167 }, }, { - id: 663, + id: '663', from: 'PLU', to: 'SJK', attributes: { prediction: 111 }, }, { - id: 664, + id: '664', from: 'TNO', to: 'SJO', attributes: { prediction: 620 }, }, { - id: 665, + id: '665', from: 'SJU', to: 'SKB', attributes: { prediction: 1320 }, }, { - id: 666, + id: '666', from: 'BOS', to: 'SLK', attributes: { prediction: 338 }, }, { - id: 667, + id: '667', from: 'IMP', to: 'SLZ', attributes: { prediction: 6045 }, }, { - id: 668, + id: '668', from: 'HER', to: 'SMI', attributes: { prediction: 128 }, }, { - id: 669, + id: '669', from: 'RDU', to: 'STL', attributes: { prediction: 2657 }, }, { - id: 670, + id: '670', from: 'GNB', to: 'STN', attributes: { prediction: 3256 }, }, { - id: 671, + id: '671', from: 'CPH', to: 'STR', attributes: { prediction: 2725 }, }, { - id: 672, + id: '672', from: 'ATZ', to: 'SHJ', attributes: { prediction: 2174 }, }, { - id: 673, + id: '673', from: 'DAM', to: 'SVO', attributes: { prediction: 834 }, }, { - id: 674, + id: '674', from: 'ZAG', to: 'SXF', attributes: { prediction: 2018 }, }, { - id: 675, + id: '675', from: 'MNL', to: 'SYD', attributes: { prediction: 4670 }, }, { - id: 676, + id: '676', from: 'GLF', to: 'SYQ', attributes: { prediction: 454 }, }, { - id: 677, + id: '677', from: 'PHL', to: 'SYR', attributes: { prediction: 7859 }, }, { - id: 678, + id: '678', from: 'HGH', to: 'SYX', attributes: { prediction: 24720 }, }, { - id: 679, + id: '679', from: 'BRS', to: 'SZG', attributes: { prediction: 176 }, }, { - id: 680, + id: '680', from: 'EXT', to: 'SZG', attributes: { prediction: 262 }, }, { - id: 681, + id: '681', from: 'SRZ', to: 'TDD', attributes: { prediction: 3336 }, }, { - id: 682, + id: '682', from: 'FLW', to: 'TER', attributes: { prediction: 191 }, }, { - id: 683, + id: '683', from: 'MAN', to: 'TFS', attributes: { prediction: 18176 }, }, { - id: 684, + id: '684', from: 'LCE', to: 'TGU', attributes: { prediction: 4447 }, }, { - id: 685, + id: '685', from: 'CBH', to: 'TIN', attributes: { prediction: 336 }, }, { - id: 686, + id: '686', from: 'BRU', to: 'TIP', attributes: { prediction: 1403 }, }, { - id: 687, + id: '687', from: 'CUU', to: 'TLC', attributes: { prediction: 4868 }, }, { - id: 688, + id: '688', from: 'MSQ', to: 'TLL', attributes: { prediction: 272 }, }, { - id: 689, + id: '689', from: 'SNR', to: 'TLS', attributes: { prediction: 1070 }, }, { - id: 690, + id: '690', from: 'TAS', to: 'TMJ', attributes: { prediction: 2997 }, }, { - id: 691, + id: '691', from: 'GEA', to: 'TOU', attributes: { prediction: 274 }, }, { - id: 692, + id: '692', from: 'BNA', to: 'TPA', attributes: { prediction: 11321 }, }, { - id: 693, + id: '693', from: 'MTY', to: 'TAM', attributes: { prediction: 1676 }, }, { - id: 694, + id: '694', from: 'MUC', to: 'TAS', attributes: { prediction: 92 }, }, { - id: 695, + id: '695', from: 'DME', to: 'TSE', attributes: { prediction: 3842 }, }, { - id: 696, + id: '696', from: 'HFE', to: 'TSN', attributes: { prediction: 3556 }, }, { - id: 697, + id: '697', from: 'VLL', to: 'BCN', attributes: { prediction: 3959 }, }, { - id: 698, + id: '698', from: 'STN', to: 'BDS', attributes: { prediction: 1218 }, }, { - id: 699, + id: '699', from: 'VPY', to: 'BEW', attributes: { prediction: 224 }, }, { - id: 700, + id: '700', from: 'MAN', to: 'BGI', attributes: { prediction: 1300 }, }, { - id: 701, + id: '701', from: 'MAN', to: 'ZRH', attributes: { prediction: 6742 }, }, { - id: 702, + id: '702', from: 'RIX', to: 'VNO', attributes: { prediction: 6503 }, }, { - id: 703, + id: '703', from: 'BOG', to: 'VUP', attributes: { prediction: 4738 }, }, { - id: 704, + id: '704', from: 'SLA', to: 'VVI', attributes: { prediction: 771 }, }, { - id: 705, + id: '705', from: 'KIJ', to: 'VVO', attributes: { prediction: 754 }, }, { - id: 706, + id: '706', from: 'JJN', to: 'WUH', attributes: { prediction: 5520 }, }, { - id: 707, + id: '707', from: 'DME', to: 'BHK', attributes: { prediction: 2349 }, }, { - id: 708, + id: '708', from: 'YRL', to: 'YHP', attributes: { prediction: 72 }, }, { - id: 709, + id: '709', from: 'PYJ', to: 'YKS', attributes: { prediction: 35 }, }, { - id: 710, + id: '710', from: 'CUN', to: 'YQR', attributes: { prediction: 466 }, }, { - id: 711, + id: '711', from: 'YHZ', to: 'YQY', attributes: { prediction: 2110 }, }, { - id: 712, + id: '712', from: 'PSP', to: 'YVR', attributes: { prediction: 3194 }, }, { - id: 713, + id: '713', from: 'YPW', to: 'YVR', attributes: { prediction: 1109 }, }, { - id: 714, + id: '714', from: 'PHX', to: 'YYZ', attributes: { prediction: 6222 }, }, { - id: 715, + id: '715', from: 'TLV', to: 'YYZ', attributes: { prediction: 3714 }, }, { - id: 716, + id: '716', from: 'EXT', to: 'AMS', attributes: { prediction: 1501 }, }, { - id: 717, + id: '717', from: 'SKB', to: 'ANU', attributes: { prediction: 4584 }, }, { - id: 718, + id: '718', from: 'KGX', to: 'ANV', attributes: { prediction: 58 }, }, { - id: 719, + id: '719', from: 'EWR', to: 'ARN', attributes: { prediction: 8445 }, }, { - id: 720, + id: '720', from: 'RYG', to: 'ATH', attributes: { prediction: 121 }, }, { - id: 721, + id: '721', from: 'BMI', to: 'ATL', attributes: { prediction: 13667 }, }, { - id: 722, + id: '722', from: 'GRK', to: 'ATL', attributes: { prediction: 2362 }, }, { - id: 723, + id: '723', from: 'MAO', to: 'ATL', attributes: { prediction: 869 }, }, { - id: 724, + id: '724', from: 'MEI', to: 'ATL', attributes: { prediction: 1851 }, }, { - id: 725, + id: '725', from: 'SFO', to: 'ABQ', attributes: { prediction: 1239 }, }, { - id: 726, + id: '726', from: 'LHR', to: 'ABZ', attributes: { prediction: 25897 }, }, { - id: 727, + id: '727', from: 'HAJ', to: 'ACE', attributes: { prediction: 1481 }, }, { - id: 728, + id: '728', from: 'SVI', to: 'ACR', attributes: { prediction: 128 }, }, { - id: 729, + id: '729', from: 'PTY', to: 'ADZ', attributes: { prediction: 2138 }, }, { - id: 730, + id: '730', from: 'CNQ', to: 'AEP', attributes: { prediction: 2030 }, }, { - id: 731, + id: '731', from: 'VKO', to: 'AER', attributes: { prediction: 17801 }, }, { - id: 732, + id: '732', from: 'HIR', to: 'AFT', attributes: { prediction: 36 }, }, { - id: 733, + id: '733', from: 'BWN', to: 'AKL', attributes: { prediction: 2141 }, }, { - id: 734, + id: '734', from: 'EWR', to: 'ALB', attributes: { prediction: 4841 }, }, { - id: 735, + id: '735', from: 'TRD', to: 'ALC', attributes: { prediction: 433 }, }, { - id: 736, + id: '736', from: 'TEE', to: 'ALG', attributes: { prediction: 641 }, }, { - id: 737, + id: '737', from: 'IAM', to: 'AZR', attributes: { prediction: 235 }, }, { - id: 738, + id: '738', from: 'ZCO', to: 'BBA', attributes: { prediction: 193 }, }, { - id: 739, + id: '739', from: 'NCE', to: 'BCN', attributes: { prediction: 3752 }, }, { - id: 740, + id: '740', from: 'EIN', to: 'BDS', attributes: { prediction: 1148 }, }, { - id: 741, + id: '741', from: 'GVA', to: 'BIQ', attributes: { prediction: 934 }, }, { - id: 742, + id: '742', from: 'AZA', to: 'BIS', attributes: { prediction: 1015 }, }, { - id: 743, + id: '743', from: 'OAK', to: 'BJX', attributes: { prediction: 352 }, }, { - id: 744, + id: '744', from: 'CAI', to: 'BKK', attributes: { prediction: 6033 }, }, { - id: 745, + id: '745', from: 'BRU', to: 'BLL', attributes: { prediction: 1235 }, }, { - id: 746, + id: '746', from: 'GRO', to: 'BTS', attributes: { prediction: 1826 }, }, { - id: 747, + id: '747', from: 'MYY', to: 'BTU', attributes: { prediction: 1339 }, }, { - id: 748, + id: '748', from: 'FUE', to: 'DUS', attributes: { prediction: 5544 }, }, { - id: 749, + id: '749', from: 'NUE', to: 'DUS', attributes: { prediction: 16238 }, }, { - id: 750, + id: '750', from: 'ORD', to: 'DUS', attributes: { prediction: 3596 }, }, { - id: 751, + id: '751', from: 'BKI', to: 'CGK', attributes: { prediction: 1285 }, }, { - id: 752, + id: '752', from: 'MDC', to: 'CGK', attributes: { prediction: 12774 }, }, { - id: 753, + id: '753', from: 'BBU', to: 'CGN', attributes: { prediction: 1545 }, }, { - id: 754, + id: '754', from: 'MAN', to: 'CGN', attributes: { prediction: 2496 }, }, { - id: 755, + id: '755', from: 'SVQ', to: 'CIA', attributes: { prediction: 2436 }, }, { - id: 756, + id: '756', from: 'NIM', to: 'BKO', attributes: { prediction: 669 }, }, { - id: 757, + id: '757', from: 'OAK', to: 'BLI', attributes: { prediction: 920 }, }, { - id: 758, + id: '758', from: 'GRU', to: 'BOG', attributes: { prediction: 9475 }, }, { - id: 759, + id: '759', from: 'TME', to: 'BOG', attributes: { prediction: 146 }, }, { - id: 760, + id: '760', from: 'EDI', to: 'BOH', attributes: { prediction: 4893 }, }, { - id: 761, + id: '761', from: 'SJU', to: 'BOS', attributes: { prediction: 16422 }, }, { - id: 762, + id: '762', from: 'CPH', to: 'BRE', attributes: { prediction: 611 }, }, { - id: 763, + id: '763', from: 'BTS', to: 'BRS', attributes: { prediction: 1171 }, }, { - id: 764, + id: '764', from: 'GLA', to: 'BRS', attributes: { prediction: 10441 }, }, { - id: 765, + id: '765', from: 'PVG', to: 'CKG', attributes: { prediction: 55962 }, }, { - id: 766, + id: '766', from: 'DEN', to: 'CLE', attributes: { prediction: 6563 }, }, { - id: 767, + id: '767', from: 'SBW', to: 'BTU', attributes: { prediction: 1361 }, }, { - id: 768, + id: '768', from: 'LTN', to: 'BUD', attributes: { prediction: 12415 }, }, { - id: 769, + id: '769', from: 'MMX', to: 'BUD', attributes: { prediction: 2394 }, }, { - id: 770, + id: '770', from: 'TGD', to: 'BUD', attributes: { prediction: 465 }, }, { - id: 771, + id: '771', from: 'PHX', to: 'BUR', attributes: { prediction: 30550 }, }, { - id: 772, + id: '772', from: 'DOK', to: 'BUS', attributes: { prediction: 176 }, }, { - id: 773, + id: '773', from: 'KCH', to: 'BWN', attributes: { prediction: 718 }, }, { - id: 774, + id: '774', from: 'NRN', to: 'CAG', attributes: { prediction: 1960 }, }, { - id: 775, + id: '775', from: 'BGW', to: 'CAI', attributes: { prediction: 773 }, }, { - id: 776, + id: '776', from: 'DAM', to: 'CAI', attributes: { prediction: 6322 }, }, { - id: 777, + id: '777', from: 'RSW', to: 'CAK', attributes: { prediction: 2854 }, }, { - id: 778, + id: '778', from: 'DEL', to: 'CAN', attributes: { prediction: 2403 }, }, { - id: 779, + id: '779', from: 'DOH', to: 'CAN', attributes: { prediction: 5155 }, }, { - id: 780, + id: '780', from: 'YOW', to: 'CCC', attributes: { prediction: 577 }, }, { - id: 781, + id: '781', from: 'LYS', to: 'CDG', attributes: { prediction: 21175 }, }, { - id: 782, + id: '782', from: 'PRG', to: 'CDG', attributes: { prediction: 29124 }, }, { - id: 783, + id: '783', from: 'VKO', to: 'CEK', attributes: { prediction: 3254 }, }, { - id: 784, + id: '784', from: 'CWB', to: 'CGH', attributes: { prediction: 45034 }, }, { - id: 785, + id: '785', from: 'EUN', to: 'CMN', attributes: { prediction: 3271 }, }, { - id: 786, + id: '786', from: 'LIL', to: 'CMN', attributes: { prediction: 768 }, }, { - id: 787, + id: '787', from: 'INN', to: 'CPH', attributes: { prediction: 2493 }, }, { - id: 788, + id: '788', from: 'KBP', to: 'CPH', attributes: { prediction: 1472 }, }, { - id: 789, + id: '789', from: 'LED', to: 'CPH', attributes: { prediction: 1749 }, }, { - id: 790, + id: '790', from: 'BEY', to: 'DXB', attributes: { prediction: 30885 }, }, { - id: 791, + id: '791', from: 'DMM', to: 'DXB', attributes: { prediction: 6901 }, }, { - id: 792, + id: '792', from: 'LYP', to: 'DXB', attributes: { prediction: 569 }, }, { - id: 793, + id: '793', from: 'TBZ', to: 'DXB', attributes: { prediction: 838 }, }, { - id: 794, + id: '794', from: 'ZRH', to: 'DXB', attributes: { prediction: 18802 }, }, { - id: 795, + id: '795', from: 'REN', to: 'DYU', attributes: { prediction: 1256 }, }, { - id: 796, + id: '796', from: 'KRT', to: 'EBD', attributes: { prediction: 368 }, }, { - id: 797, + id: '797', from: 'OKC', to: 'DEN', attributes: { prediction: 27820 }, }, { - id: 798, + id: '798', from: 'YOW', to: 'DEN', attributes: { prediction: 1433 }, }, { - id: 799, + id: '799', from: 'MSN', to: 'DFW', attributes: { prediction: 2500 }, }, { - id: 800, + id: '800', from: 'BSB', to: 'CPQ', attributes: { prediction: 8791 }, }, { - id: 801, + id: '801', from: 'NGB', to: 'CSX', attributes: { prediction: 10730 }, }, { - id: 802, + id: '802', from: 'HAK', to: 'CTU', attributes: { prediction: 14379 }, }, { - id: 803, + id: '803', from: 'HKG', to: 'CTU', attributes: { prediction: 9627 }, }, { - id: 804, + id: '804', from: 'PZI', to: 'CTU', attributes: { prediction: 7051 }, }, { - id: 805, + id: '805', from: 'HMO', to: 'CUL', attributes: { prediction: 2040 }, }, { - id: 806, + id: '806', from: 'LAP', to: 'CUL', attributes: { prediction: 2725 }, }, { - id: 807, + id: '807', from: 'IAH', to: 'CUU', attributes: { prediction: 1188 }, }, { - id: 808, + id: '808', from: 'CFC', to: 'CWB', attributes: { prediction: 366 }, }, { - id: 809, + id: '809', from: 'KPN', to: 'CYF', attributes: { prediction: 149 }, }, { - id: 810, + id: '810', from: 'SGN', to: 'DAD', attributes: { prediction: 41185 }, }, { - id: 811, + id: '811', from: 'MAF', to: 'DAL', attributes: { prediction: 12965 }, }, { - id: 812, + id: '812', from: 'AUH', to: 'DAM', attributes: { prediction: 7993 }, }, { - id: 813, + id: '813', from: 'MIA', to: 'DCA', attributes: { prediction: 36318 }, }, { - id: 814, + id: '814', from: 'ICN', to: 'DEL', attributes: { prediction: 3092 }, }, { - id: 815, + id: '815', from: 'JFK', to: 'DEL', attributes: { prediction: 6018 }, }, { - id: 816, + id: '816', from: 'SIN', to: 'DEL', attributes: { prediction: 17631 }, }, { - id: 817, + id: '817', from: 'HRG', to: 'DME', attributes: { prediction: 18584 }, }, { - id: 818, + id: '818', from: 'LHR', to: 'DME', attributes: { prediction: 21916 }, }, { - id: 819, + id: '819', from: 'BHH', to: 'DMM', attributes: { prediction: 164 }, }, { - id: 820, + id: '820', from: 'MEL', to: 'DOH', attributes: { prediction: 6382 }, }, { - id: 821, + id: '821', from: 'MXL', to: 'GDL', attributes: { prediction: 4959 }, }, { - id: 822, + id: '822', from: 'TKU', to: 'GDN', attributes: { prediction: 1903 }, }, { - id: 823, + id: '823', from: 'GVA', to: 'GLA', attributes: { prediction: 659 }, }, { - id: 824, + id: '824', from: 'JFK', to: 'EZE', attributes: { prediction: 5777 }, }, { - id: 825, + id: '825', from: 'BTT', to: 'FAI', attributes: { prediction: 373 }, }, { - id: 826, + id: '826', from: 'LTN', to: 'DUB', attributes: { prediction: 13021 }, }, { - id: 827, + id: '827', from: 'LBA', to: 'DUS', attributes: { prediction: 2847 }, }, { - id: 828, + id: '828', from: 'SPC', to: 'DUS', attributes: { prediction: 2569 }, }, { - id: 829, + id: '829', from: 'GYD', to: 'DXB', attributes: { prediction: 8543 }, }, { - id: 830, + id: '830', from: 'PNQ', to: 'DXB', attributes: { prediction: 1706 }, }, { - id: 831, + id: '831', from: 'PEK', to: 'DYG', attributes: { prediction: 4862 }, }, { - id: 832, + id: '832', from: 'FRU', to: 'DYU', attributes: { prediction: 1146 }, }, { - id: 833, + id: '833', from: 'BUD', to: 'EIN', attributes: { prediction: 4426 }, }, { - id: 834, + id: '834', from: 'IST', to: 'ERZ', attributes: { prediction: 3871 }, }, { - id: 835, + id: '835', from: 'BAL', to: 'ESB', attributes: { prediction: 2730 }, }, { - id: 836, + id: '836', from: 'LAS', to: 'EUG', attributes: { prediction: 1395 }, }, { - id: 837, + id: '837', from: 'HDN', to: 'EWR', attributes: { prediction: 391 }, }, { - id: 838, + id: '838', from: 'NCL', to: 'EXT', attributes: { prediction: 2058 }, }, { - id: 839, + id: '839', from: 'DEN', to: 'FAR', attributes: { prediction: 6340 }, }, { - id: 840, + id: '840', from: 'ALG', to: 'FCO', attributes: { prediction: 5006 }, }, { - id: 841, + id: '841', from: 'RTM', to: 'GNB', attributes: { prediction: 500 }, }, { - id: 842, + id: '842', from: 'ATH', to: 'GOT', attributes: { prediction: 79 }, }, { - id: 843, + id: '843', from: 'IAH', to: 'GRK', attributes: { prediction: 3885 }, }, { - id: 844, + id: '844', from: 'RAB', to: 'HKN', attributes: { prediction: 64 }, }, { - id: 845, + id: '845', from: 'CPH', to: 'IAD', attributes: { prediction: 4553 }, }, { - id: 846, + id: '846', from: 'LGA', to: 'IAD', attributes: { prediction: 6901 }, }, { - id: 847, + id: '847', from: 'BON', to: 'IAH', attributes: { prediction: 1382 }, }, { - id: 848, + id: '848', from: 'FSZ', to: 'ICN', attributes: { prediction: 7052 }, }, { - id: 849, + id: '849', from: 'MEM', to: 'ICT', attributes: { prediction: 2468 }, }, { - id: 850, + id: '850', from: 'NAY', to: 'FUO', attributes: { prediction: 2402 }, }, { - id: 851, + id: '851', from: 'HSV', to: 'FWA', attributes: { prediction: 34 }, }, { - id: 852, + id: '852', from: 'PDX', to: 'GEG', attributes: { prediction: 15298 }, }, { - id: 853, + id: '853', from: 'NQY', to: 'GLA', attributes: { prediction: 1082 }, }, { - id: 854, + id: '854', from: 'PPT', to: 'GMR', attributes: { prediction: 99 }, }, { - id: 855, + id: '855', from: 'BLL', to: 'GRO', attributes: { prediction: 1418 }, }, { - id: 856, + id: '856', from: 'PSR', to: 'GRO', attributes: { prediction: 1305 }, }, { - id: 857, + id: '857', from: 'DTW', to: 'GSO', attributes: { prediction: 3500 }, }, { - id: 858, + id: '858', from: 'EWR', to: 'GSP', attributes: { prediction: 3033 }, }, { - id: 859, + id: '859', from: 'HOU', to: 'GVA', attributes: { prediction: 10 }, }, { - id: 860, + id: '860', from: 'LIS', to: 'GVA', attributes: { prediction: 15353 }, }, { - id: 861, + id: '861', from: 'MIA', to: 'IND', attributes: { prediction: 1358 }, }, { - id: 862, + id: '862', from: 'SBN', to: 'IND', attributes: { prediction: 29 }, }, { - id: 863, + id: '863', from: 'SIN', to: 'HAK', attributes: { prediction: 6099 }, }, { - id: 864, + id: '864', from: 'NRT', to: 'HAN', attributes: { prediction: 6463 }, }, { - id: 865, + id: '865', from: 'FRA', to: 'HAV', attributes: { prediction: 1207 }, }, { - id: 866, + id: '866', from: 'SFB', to: 'HGR', attributes: { prediction: 1179 }, }, { - id: 867, + id: '867', from: 'KTM', to: 'HKG', attributes: { prediction: 1656 }, }, { - id: 868, + id: '868', from: 'RUH', to: 'HKG', attributes: { prediction: 6747 }, }, { - id: 869, + id: '869', from: 'LGW', to: 'HME', attributes: { prediction: 1765 }, }, { - id: 870, + id: '870', from: 'PEK', to: 'HND', attributes: { prediction: 18832 }, }, { - id: 871, + id: '871', from: 'DNZ', to: 'IST', attributes: { prediction: 6712 }, }, { - id: 872, + id: '872', from: 'DUB', to: 'IST', attributes: { prediction: 3986 }, }, { - id: 873, + id: '873', from: 'MCT', to: 'IST', attributes: { prediction: 2501 }, }, { - id: 874, + id: '874', from: 'TIP', to: 'IST', attributes: { prediction: 8830 }, }, { - id: 875, + id: '875', from: 'BWI', to: 'LIT', attributes: { prediction: 2792 }, }, { - id: 876, + id: '876', from: 'DGO', to: 'MEX', attributes: { prediction: 4455 }, }, { - id: 877, + id: '877', from: 'LED', to: 'KJA', attributes: { prediction: 2240 }, }, { - id: 878, + id: '878', from: 'GSE', to: 'KLU', attributes: { prediction: 512 }, }, { - id: 879, + id: '879', from: 'FUK', to: 'KMI', attributes: { prediction: 14009 }, }, { - id: 880, + id: '880', from: 'HND', to: 'KUH', attributes: { prediction: 11642 }, }, { - id: 881, + id: '881', from: 'LAX', to: 'IYK', attributes: { prediction: 1383 }, }, { - id: 882, + id: '882', from: 'EWR', to: 'JAX', attributes: { prediction: 7384 }, }, { - id: 883, + id: '883', from: 'MAN', to: 'JER', attributes: { prediction: 4158 }, }, { - id: 884, + id: '884', from: 'MSY', to: 'JFK', attributes: { prediction: 10638 }, }, { - id: 885, + id: '885', from: 'BKI', to: 'JHB', attributes: { prediction: 6420 }, }, { - id: 886, + id: '886', from: 'SFJ', to: 'JSU', attributes: { prediction: 474 }, }, { - id: 887, + id: '887', from: 'DUS', to: 'KGF', attributes: { prediction: 501 }, }, { - id: 888, + id: '888', from: 'TNA', to: 'KHN', attributes: { prediction: 2433 }, }, { - id: 889, + id: '889', from: 'CGK', to: 'KUL', attributes: { prediction: 56287 }, }, { - id: 890, + id: '890', from: 'LTN', to: 'KUN', attributes: { prediction: 2469 }, }, { - id: 891, + id: '891', from: 'KKH', to: 'KWK', attributes: { prediction: 77 }, }, { - id: 892, + id: '892', from: 'SVO', to: 'LAD', attributes: { prediction: 418 }, }, { - id: 893, + id: '893', from: 'MRY', to: 'LAS', attributes: { prediction: 897 }, }, { - id: 894, + id: '894', from: 'YYZ', to: 'MHT', attributes: { prediction: 915 }, }, { - id: 895, + id: '895', from: 'CLO', to: 'MIA', attributes: { prediction: 6472 }, }, { - id: 896, + id: '896', from: 'IND', to: 'MIA', attributes: { prediction: 1254 }, }, { - id: 897, + id: '897', from: 'NCE', to: 'MIR', attributes: { prediction: 1555 }, }, { - id: 898, + id: '898', from: 'MEM', to: 'MKE', attributes: { prediction: 3666 }, }, { - id: 899, + id: '899', from: 'MIA', to: 'MKE', attributes: { prediction: 161 }, }, { - id: 900, + id: '900', from: 'STN', to: 'LNZ', attributes: { prediction: 2555 }, }, { - id: 901, + id: '901', from: 'MAD', to: 'LPA', attributes: { prediction: 81530 }, }, { - id: 902, + id: '902', from: 'KZN', to: 'LBD', attributes: { prediction: 392 }, }, { - id: 903, + id: '903', from: 'EDI', to: 'LCJ', attributes: { prediction: 1341 }, }, { - id: 904, + id: '904', from: 'YUL', to: 'LGA', attributes: { prediction: 12235 }, }, { - id: 905, + id: '905', from: 'JER', to: 'LGW', attributes: { prediction: 29212 }, }, { - id: 906, + id: '906', from: 'KRK', to: 'LGW', attributes: { prediction: 3596 }, }, { - id: 907, + id: '907', from: 'RTM', to: 'LGW', attributes: { prediction: 6443 }, }, { - id: 908, + id: '908', from: 'VNO', to: 'LGW', attributes: { prediction: 4063 }, }, { - id: 909, + id: '909', from: 'BEY', to: 'LHR', attributes: { prediction: 9191 }, }, { - id: 910, + id: '910', from: 'PHX', to: 'LIH', attributes: { prediction: 4173 }, }, { - id: 911, + id: '911', from: 'BSL', to: 'LIS', attributes: { prediction: 1568 }, }, { - id: 912, + id: '912', from: 'ORK', to: 'LPL', attributes: { prediction: 5623 }, }, { - id: 913, + id: '913', from: 'MHD', to: 'LRR', attributes: { prediction: 939 }, }, { - id: 914, + id: '914', from: 'TIP', to: 'LTD', attributes: { prediction: 298 }, }, { - id: 915, + id: '915', from: 'BRU', to: 'MLA', attributes: { prediction: 3873 }, }, { - id: 916, + id: '916', from: 'MRS', to: 'MLA', attributes: { prediction: 933 }, }, { - id: 917, + id: '917', from: 'YYC', to: 'NAS', attributes: { prediction: 330 }, }, { - id: 918, + id: '918', from: 'ADD', to: 'NBO', attributes: { prediction: 9210 }, }, { - id: 919, + id: '919', from: 'FBM', to: 'NBO', attributes: { prediction: 786 }, }, { - id: 920, + id: '920', from: 'MPM', to: 'NBO', attributes: { prediction: 829 }, }, { - id: 921, + id: '921', from: 'DUS', to: 'NCE', attributes: { prediction: 5021 }, }, { - id: 922, + id: '922', from: 'CDG', to: 'NCL', attributes: { prediction: 6904 }, }, { - id: 923, + id: '923', from: 'SBH', to: 'NEV', attributes: { prediction: 33 }, }, { - id: 924, + id: '924', from: 'CSX', to: 'NKG', attributes: { prediction: 13645 }, }, { - id: 925, + id: '925', from: 'PEK', to: 'NKG', attributes: { prediction: 69058 }, }, { - id: 926, + id: '926', from: 'FUK', to: 'NKM', attributes: { prediction: 6483 }, }, { - id: 927, + id: '927', from: 'CDB', to: 'NLG', attributes: { prediction: 22 }, }, { - id: 928, + id: '928', from: 'TMU', to: 'NOB', attributes: { prediction: 550 }, }, { - id: 929, + id: '929', from: 'LEQ', to: 'NQY', attributes: { prediction: 221 }, }, { - id: 930, + id: '930', from: 'CUN', to: 'MCI', attributes: { prediction: 1460 }, }, { - id: 931, + id: '931', from: 'SJU', to: 'MCO', attributes: { prediction: 37987 }, }, { - id: 932, + id: '932', from: 'SYD', to: 'MCY', attributes: { prediction: 10076 }, }, { - id: 933, + id: '933', from: 'COU', to: 'MEM', attributes: { prediction: 2537 }, }, { - id: 934, + id: '934', from: 'PNS', to: 'MEM', attributes: { prediction: 3680 }, }, { - id: 935, + id: '935', from: 'TPA', to: 'MEM', attributes: { prediction: 8807 }, }, { - id: 936, + id: '936', from: 'HUX', to: 'MEX', attributes: { prediction: 10256 }, }, { - id: 937, + id: '937', from: 'DFW', to: 'MFE', attributes: { prediction: 12303 }, }, { - id: 938, + id: '938', from: 'CGK', to: 'MFM', attributes: { prediction: 2898 }, }, { - id: 939, + id: '939', from: 'FLL', to: 'MGA', attributes: { prediction: 907 }, }, { - id: 940, + id: '940', from: 'DEN', to: 'MIA', attributes: { prediction: 11811 }, }, { - id: 941, + id: '941', from: 'UIO', to: 'MIA', attributes: { prediction: 14052 }, }, { - id: 942, + id: '942', from: 'LEA', to: 'MJK', attributes: { prediction: 143 }, }, { - id: 943, + id: '943', from: 'PIK', to: 'MJV', attributes: { prediction: 1498 }, }, { - id: 944, + id: '944', from: 'HOU', to: 'OAK', attributes: { prediction: 6715 }, }, { - id: 945, + id: '945', from: 'LGW', to: 'SVG', attributes: { prediction: 2763 }, }, { - id: 946, + id: '946', from: 'MFM', to: 'PEK', attributes: { prediction: 6910 }, }, { - id: 947, + id: '947', from: 'DTW', to: 'PHL', attributes: { prediction: 24598 }, }, { - id: 948, + id: '948', from: 'ISP', to: 'PHL', attributes: { prediction: 3184 }, }, { - id: 949, + id: '949', from: 'MBJ', to: 'PHL', attributes: { prediction: 9137 }, }, { - id: 950, + id: '950', from: 'TRN', to: 'NAP', attributes: { prediction: 20775 }, }, { - id: 951, + id: '951', from: 'MBJ', to: 'NAS', attributes: { prediction: 526 }, }, { - id: 952, + id: '952', from: 'HAM', to: 'NCE', attributes: { prediction: 613 }, }, { - id: 953, + id: '953', from: 'CMF', to: 'NCL', attributes: { prediction: 448 }, }, { - id: 954, + id: '954', from: 'TPE', to: 'NGB', attributes: { prediction: 6886 }, }, { - id: 955, + id: '955', from: 'AXT', to: 'NGO', attributes: { prediction: 764 }, }, { - id: 956, + id: '956', from: 'SPN', to: 'NGO', attributes: { prediction: 4401 }, }, { - id: 957, + id: '957', from: 'SHE', to: 'NKG', attributes: { prediction: 14965 }, }, { - id: 958, + id: '958', from: 'BRI', to: 'NRN', attributes: { prediction: 1974 }, }, { - id: 959, + id: '959', from: 'FAO', to: 'NRN', attributes: { prediction: 1699 }, }, { - id: 960, + id: '960', from: 'HGH', to: 'NRT', attributes: { prediction: 1515 }, }, { - id: 961, + id: '961', from: 'KUL', to: 'NRT', attributes: { prediction: 14605 }, }, { - id: 962, + id: '962', from: 'PHX', to: 'PIT', attributes: { prediction: 12104 }, }, { - id: 963, + id: '963', from: 'GUM', to: 'OKJ', attributes: { prediction: 1072 }, }, { - id: 964, + id: '964', from: 'WMO', to: 'OME', attributes: { prediction: 107 }, }, { - id: 965, + id: '965', from: 'KUL', to: 'OOL', attributes: { prediction: 7344 }, }, { - id: 966, + id: '966', from: 'HPN', to: 'ORD', attributes: { prediction: 10820 }, }, { - id: 967, + id: '967', from: 'PEK', to: 'ORD', attributes: { prediction: 5941 }, }, { - id: 968, + id: '968', from: 'PUJ', to: 'ORD', attributes: { prediction: 2677 }, }, { - id: 969, + id: '969', from: 'CFE', to: 'ORY', attributes: { prediction: 10255 }, }, { - id: 970, + id: '970', from: 'LDE', to: 'ORY', attributes: { prediction: 4450 }, }, { - id: 971, + id: '971', from: 'LYS', to: 'OTP', attributes: { prediction: 1783 }, }, { - id: 972, + id: '972', from: 'AZN', to: 'OVB', attributes: { prediction: 605 }, }, { - id: 973, + id: '973', from: 'HNL', to: 'PDX', attributes: { prediction: 11270 }, }, { - id: 974, + id: '974', from: 'ELS', to: 'PLZ', attributes: { prediction: 810 }, }, { - id: 975, + id: '975', from: 'REP', to: 'PNH', attributes: { prediction: 22917 }, }, { - id: 976, + id: '976', from: 'REC', to: 'PNZ', attributes: { prediction: 5820 }, }, { - id: 977, + id: '977', from: 'TIH', to: 'PPT', attributes: { prediction: 868 }, }, { - id: 978, + id: '978', from: 'SPU', to: 'SXF', attributes: { prediction: 1516 }, }, { - id: 979, + id: '979', from: 'MIA', to: 'RSW', attributes: { prediction: 3984 }, }, { - id: 980, + id: '980', from: 'BGA', to: 'RVE', attributes: { prediction: 242 }, }, { - id: 981, + id: '981', from: 'TAI', to: 'SAH', attributes: { prediction: 2254 }, }, { - id: 982, + id: '982', from: 'BZE', to: 'SAL', attributes: { prediction: 2041 }, }, { - id: 983, + id: '983', from: 'SAN', to: 'SAT', attributes: { prediction: 6075 }, }, { - id: 984, + id: '984', from: 'OTP', to: 'PRG', attributes: { prediction: 5589 }, }, { - id: 985, + id: '985', from: 'TRD', to: 'PRG', attributes: { prediction: 1147 }, }, { - id: 986, + id: '986', from: 'ZRH', to: 'PRN', attributes: { prediction: 4576 }, }, { - id: 987, + id: '987', from: 'LGW', to: 'PSA', attributes: { prediction: 4276 }, }, { - id: 988, + id: '988', from: 'MCO', to: 'PSE', attributes: { prediction: 2979 }, }, { - id: 989, + id: '989', from: 'MWX', to: 'PVG', attributes: { prediction: 811 }, }, { - id: 990, + id: '990', from: 'YXE', to: 'PVR', attributes: { prediction: 1834 }, }, { - id: 991, + id: '991', from: 'MAN', to: 'RHO', attributes: { prediction: 170 }, }, { - id: 992, + id: '992', from: 'MJT', to: 'RHO', attributes: { prediction: 58 }, }, { - id: 993, + id: '993', from: 'BRU', to: 'ROB', attributes: { prediction: 2091 }, }, { - id: 994, + id: '994', from: 'SJU', to: 'SBH', attributes: { prediction: 49 }, }, { - id: 995, + id: '995', from: 'NOV', to: 'SDD', attributes: { prediction: 322 }, }, { - id: 996, + id: '996', from: 'CDG', to: 'SDQ', attributes: { prediction: 1133 }, }, { - id: 997, + id: '997', from: 'SFO', to: 'SEA', attributes: { prediction: 60048 }, }, { - id: 998, + id: '998', from: 'ELM', to: 'SFB', attributes: { prediction: 1040 }, }, { - id: 999, + id: '999', from: 'BEN', to: 'TUN', attributes: { prediction: 4593 }, }, { - id: 1000, + id: '1000', from: 'DOH', to: 'TUN', attributes: { prediction: 4639 }, }, { - id: 1001, + id: '1001', from: 'IST', to: 'TUN', attributes: { prediction: 7844 }, }, { - id: 1002, + id: '1002', from: 'SLC', to: 'TWF', attributes: { prediction: 1970 }, }, { - id: 1003, + id: '1003', from: 'XRY', to: 'TXL', attributes: { prediction: 645 }, }, { - id: 1004, + id: '1004', from: 'CAN', to: 'TXN', attributes: { prediction: 1724 }, }, { - id: 1005, + id: '1005', from: 'SBZ', to: 'STR', attributes: { prediction: 1728 }, }, { - id: 1006, + id: '1006', from: 'AMS', to: 'SIN', attributes: { prediction: 18327 }, }, { - id: 1007, + id: '1007', from: 'MES', to: 'SIN', attributes: { prediction: 7533 }, }, { - id: 1008, + id: '1008', from: 'SHJ', to: 'SKT', attributes: { prediction: 924 }, }, { - id: 1009, + id: '1009', from: 'SXF', to: 'SPU', attributes: { prediction: 130 }, }, { - id: 1010, + id: '1010', from: 'BPS', to: 'SSA', attributes: { prediction: 8485 }, }, { - id: 1011, + id: '1011', from: 'EMA', to: 'SSH', attributes: { prediction: 2424 }, }, { - id: 1012, + id: '1012', from: 'PAP', to: 'STI', attributes: { prediction: 92 }, }, { - id: 1013, + id: '1013', from: 'MSP', to: 'STL', attributes: { prediction: 29356 }, }, { - id: 1014, + id: '1014', from: 'BFS', to: 'STN', attributes: { prediction: 15810 }, }, { - id: 1015, + id: '1015', from: 'FLL', to: 'STT', attributes: { prediction: 3329 }, }, { - id: 1016, + id: '1016', from: 'KKN', to: 'VDS', attributes: { prediction: 1039 }, }, { - id: 1017, + id: '1017', from: 'CPH', to: 'VIE', attributes: { prediction: 16354 }, }, { - id: 1018, + id: '1018', from: 'KRR', to: 'VIE', attributes: { prediction: 2239 }, }, { - id: 1019, + id: '1019', from: 'PEN', to: 'BKK', attributes: { prediction: 7026 }, }, { - id: 1020, + id: '1020', from: 'LYS', to: 'BLJ', attributes: { prediction: 391 }, }, { - id: 1021, + id: '1021', from: 'EVN', to: 'VOZ', attributes: { prediction: 347 }, }, { - id: 1022, + id: '1022', from: 'CBT', to: 'VPE', attributes: { prediction: 840 }, }, { - id: 1023, + id: '1023', from: 'AGA', to: 'VRN', attributes: { prediction: 484 }, }, { - id: 1024, + id: '1024', from: 'CPH', to: 'VRN', attributes: { prediction: 306 }, }, { - id: 1025, + id: '1025', from: 'BOG', to: 'VVC', attributes: { prediction: 2006 }, }, { - id: 1026, + id: '1026', from: 'MEL', to: 'WGA', attributes: { prediction: 1216 }, }, { - id: 1027, + id: '1027', from: 'MYD', to: 'WIL', attributes: { prediction: 1313 }, }, { - id: 1028, + id: '1028', from: 'HAK', to: 'WUX', attributes: { prediction: 1382 }, }, { - id: 1029, + id: '1029', from: 'NUE', to: 'TFS', attributes: { prediction: 5759 }, }, { - id: 1030, + id: '1030', from: 'UPN', to: 'TIJ', attributes: { prediction: 2403 }, }, { - id: 1031, + id: '1031', from: 'HMA', to: 'TJM', attributes: { prediction: 1546 }, }, { - id: 1032, + id: '1032', from: 'TBO', to: 'TKQ', attributes: { prediction: 821 }, }, { - id: 1033, + id: '1033', from: 'HEL', to: 'TKU', attributes: { prediction: 3288 }, }, { - id: 1034, + id: '1034', from: 'HKG', to: 'TNA', attributes: { prediction: 2683 }, }, { - id: 1035, + id: '1035', from: 'ORD', to: 'TOL', attributes: { prediction: 2711 }, }, { - id: 1036, + id: '1036', from: 'DTW', to: 'TPA', attributes: { prediction: 35140 }, }, { - id: 1037, + id: '1037', from: 'HGH', to: 'TPE', attributes: { prediction: 9304 }, }, { - id: 1038, + id: '1038', from: 'KWI', to: 'TRV', attributes: { prediction: 2779 }, }, { - id: 1039, + id: '1039', from: 'XNN', to: 'XIY', attributes: { prediction: 18846 }, }, { - id: 1040, + id: '1040', from: 'PPT', to: 'TUB', attributes: { prediction: 502 }, }, { - id: 1041, + id: '1041', from: 'DFW', to: 'TXK', attributes: { prediction: 3467 }, }, { - id: 1042, + id: '1042', from: 'OSL', to: 'TXL', attributes: { prediction: 2963 }, }, { - id: 1043, + id: '1043', from: 'TRN', to: 'TXL', attributes: { prediction: 967 }, }, { - id: 1044, + id: '1044', from: 'CGH', to: 'UDI', attributes: { prediction: 11695 }, }, { - id: 1045, + id: '1045', from: 'NSK', to: 'UFA', attributes: { prediction: 248 }, }, { - id: 1046, + id: '1046', from: 'KUL', to: 'UPG', attributes: { prediction: 1724 }, }, { - id: 1047, + id: '1047', from: 'PHL', to: 'UVF', attributes: { prediction: 375 }, }, { - id: 1048, + id: '1048', from: 'ARN', to: 'VAA', attributes: { prediction: 2191 }, }, { - id: 1049, + id: '1049', from: 'GOT', to: 'VBY', attributes: { prediction: 161 }, }, { - id: 1050, + id: '1050', from: 'ZRH', to: 'VIE', attributes: { prediction: 33396 }, }, { - id: 1051, + id: '1051', from: 'NKG', to: 'XMN', attributes: { prediction: 20177 }, }, { - id: 1052, + id: '1052', from: 'LUX', to: 'XRY', attributes: { prediction: 112 }, }, { - id: 1053, + id: '1053', from: 'YCB', to: 'YHK', attributes: { prediction: 81 }, }, { - id: 1054, + id: '1054', from: 'YUL', to: 'YHZ', attributes: { prediction: 11774 }, }, { - id: 1055, + id: '1055', from: 'YVR', to: 'YKA', attributes: { prediction: 8018 }, }, { - id: 1056, + id: '1056', from: 'YQB', to: 'YKL', attributes: { prediction: 341 }, }, { - id: 1057, + id: '1057', from: 'BLA', to: 'CCS', attributes: { prediction: 19054 }, }, { - id: 1058, + id: '1058', from: 'CTA', to: 'CDG', attributes: { prediction: 8373 }, }, { - id: 1059, + id: '1059', from: 'DLA', to: 'CDG', attributes: { prediction: 5010 }, }, { - id: 1060, + id: '1060', from: 'FCO', to: 'CDG', attributes: { prediction: 57913 }, }, { - id: 1061, + id: '1061', from: 'TPE', to: 'CDG', attributes: { prediction: 3491 }, }, { - id: 1062, + id: '1062', from: 'WAW', to: 'CDG', attributes: { prediction: 19796 }, }, { - id: 1063, + id: '1063', from: 'YYY', to: 'YQB', attributes: { prediction: 256 }, }, { - id: 1064, + id: '1064', from: 'YWG', to: 'YTH', attributes: { prediction: 2907 }, }, { - id: 1065, + id: '1065', from: 'YYC', to: 'YXX', attributes: { prediction: 11064 }, }, { - id: 1066, + id: '1066', from: 'DXB', to: 'YYZ', attributes: { prediction: 6192 }, }, { - id: 1067, + id: '1067', from: 'YVR', to: 'YZF', attributes: { prediction: 1048 }, }, { - id: 1068, + id: '1068', from: 'PUJ', to: 'AMS', attributes: { prediction: 888 }, }, { - id: 1069, + id: '1069', from: 'BRW', to: 'ANC', attributes: { prediction: 2545 }, }, { - id: 1070, + id: '1070', from: 'CDV', to: 'ANC', attributes: { prediction: 2347 }, }, { - id: 1071, + id: '1071', from: 'JNU', to: 'ANC', attributes: { prediction: 7692 }, }, { - id: 1072, + id: '1072', from: 'LIS', to: 'ARN', attributes: { prediction: 3552 }, }, { - id: 1073, + id: '1073', from: 'WAW', to: 'ARN', attributes: { prediction: 3489 }, }, { - id: 1074, + id: '1074', from: 'AGS', to: 'ATL', attributes: { prediction: 15089 }, }, { - id: 1075, + id: '1075', from: 'FPO', to: 'ATL', attributes: { prediction: 909 }, }, { - id: 1076, + id: '1076', from: 'NAS', to: 'ATL', attributes: { prediction: 15005 }, }, { - id: 1077, + id: '1077', from: 'RTB', to: 'ATL', attributes: { prediction: 743 }, }, { - id: 1078, + id: '1078', from: 'BET', to: 'ATT', attributes: { prediction: 91 }, }, { - id: 1079, + id: '1079', from: 'AMS', to: 'AUH', attributes: { prediction: 3921 }, }, { - id: 1080, + id: '1080', from: 'SFO', to: 'AUS', attributes: { prediction: 5725 }, }, { - id: 1081, + id: '1081', from: 'IKT', to: 'BAX', attributes: { prediction: 1372 }, }, { - id: 1082, + id: '1082', from: 'BKO', to: 'ABJ', attributes: { prediction: 651 }, }, { - id: 1083, + id: '1083', from: 'ROB', to: 'ABJ', attributes: { prediction: 1227 }, }, { - id: 1084, + id: '1084', from: 'NUE', to: 'ACE', attributes: { prediction: 2519 }, }, { - id: 1085, + id: '1085', from: 'DLA', to: 'ADD', attributes: { prediction: 1790 }, }, { - id: 1086, + id: '1086', from: 'SVO', to: 'AGP', attributes: { prediction: 1424 }, }, { - id: 1087, + id: '1087', from: 'IST', to: 'ALA', attributes: { prediction: 5091 }, }, { - id: 1088, + id: '1088', from: 'JED', to: 'ALG', attributes: { prediction: 2544 }, }, { - id: 1089, + id: '1089', from: 'DXB', to: 'ALP', attributes: { prediction: 2100 }, }, { - id: 1090, + id: '1090', from: 'DAR', to: 'AMS', attributes: { prediction: 5093 }, }, { - id: 1091, + id: '1091', from: 'FRL', to: 'BBU', attributes: { prediction: 1079 }, }, { - id: 1092, + id: '1092', from: 'PRG', to: 'BCN', attributes: { prediction: 11501 }, }, { - id: 1093, + id: '1093', from: 'RUH', to: 'CGK', attributes: { prediction: 2250 }, }, { - id: 1094, + id: '1094', from: 'FCO', to: 'CGN', attributes: { prediction: 3903 }, }, { - id: 1095, + id: '1095', from: 'CTU', to: 'CSX', attributes: { prediction: 16493 }, }, { - id: 1096, + id: '1096', from: 'BKK', to: 'CTU', attributes: { prediction: 3245 }, }, { - id: 1097, + id: '1097', from: 'VER', to: 'CUN', attributes: { prediction: 3594 }, }, { - id: 1098, + id: '1098', from: 'DTW', to: 'CVG', attributes: { prediction: 7417 }, }, { - id: 1099, + id: '1099', from: 'BCN', to: 'CWL', attributes: { prediction: 229 }, }, { - id: 1100, + id: '1100', from: 'VLY', to: 'CWL', attributes: { prediction: 89 }, }, { - id: 1101, + id: '1101', from: 'BGW', to: 'DAM', attributes: { prediction: 2872 }, }, { - id: 1102, + id: '1102', from: 'BJL', to: 'CKY', attributes: { prediction: 188 }, }, { - id: 1103, + id: '1103', from: 'AMI', to: 'BMU', attributes: { prediction: 1401 }, }, { - id: 1104, + id: '1104', from: 'DEN', to: 'BNA', attributes: { prediction: 22772 }, }, { - id: 1105, + id: '1105', from: 'SOF', to: 'BOJ', attributes: { prediction: 1435 }, }, { - id: 1106, + id: '1106', from: 'BHU', to: 'BOM', attributes: { prediction: 1606 }, }, { - id: 1107, + id: '1107', from: 'ICN', to: 'BOM', attributes: { prediction: 2180 }, }, { - id: 1108, + id: '1108', from: 'PHX', to: 'BOS', attributes: { prediction: 11203 }, }, { - id: 1109, + id: '1109', from: 'ALC', to: 'BRE', attributes: { prediction: 1268 }, }, { - id: 1110, + id: '1110', from: 'PMI', to: 'BRE', attributes: { prediction: 8106 }, }, { - id: 1111, + id: '1111', from: 'NSI', to: 'BRU', attributes: { prediction: 2167 }, }, { - id: 1112, + id: '1112', from: 'AUX', to: 'BSB', attributes: { prediction: 761 }, }, { - id: 1113, + id: '1113', from: 'MSP', to: 'CMH', attributes: { prediction: 7148 }, }, { - id: 1114, + id: '1114', from: 'ALB', to: 'BWI', attributes: { prediction: 16563 }, }, { - id: 1115, + id: '1115', from: 'BDL', to: 'BWI', attributes: { prediction: 21996 }, }, { - id: 1116, + id: '1116', from: 'YIW', to: 'CAN', attributes: { prediction: 9198 }, }, { - id: 1117, + id: '1117', from: 'BRM', to: 'CCS', attributes: { prediction: 10344 }, }, { - id: 1118, + id: '1118', from: 'CLT', to: 'CDG', attributes: { prediction: 4402 }, }, { - id: 1119, + id: '1119', from: 'PNR', to: 'CDG', attributes: { prediction: 2001 }, }, { - id: 1120, + id: '1120', from: 'CXJ', to: 'CGH', attributes: { prediction: 3575 }, }, { - id: 1121, + id: '1121', from: 'BWN', to: 'CGK', attributes: { prediction: 3174 }, }, { - id: 1122, + id: '1122', from: 'LIM', to: 'CHM', attributes: { prediction: 220 }, }, { - id: 1123, + id: '1123', from: 'SLC', to: 'COS', attributes: { prediction: 2152 }, }, { - id: 1124, + id: '1124', from: 'SIN', to: 'CPH', attributes: { prediction: 3762 }, }, { - id: 1125, + id: '1125', from: 'SJJ', to: 'CPH', attributes: { prediction: 824 }, }, { - id: 1126, + id: '1126', from: 'TLL', to: 'CPH', attributes: { prediction: 6778 }, }, { - id: 1127, + id: '1127', from: 'MTR', to: 'CTG', attributes: { prediction: 664 }, }, { - id: 1128, + id: '1128', from: 'FLL', to: 'DCA', attributes: { prediction: 25347 }, }, { - id: 1129, + id: '1129', from: 'MIA', to: 'DCA', attributes: { prediction: 35214 }, }, { - id: 1130, + id: '1130', from: 'ORF', to: 'DCA', attributes: { prediction: 3054 }, }, { - id: 1131, + id: '1131', from: 'BOM', to: 'DEL', attributes: { prediction: 247120 }, }, { - id: 1132, + id: '1132', from: 'GOI', to: 'DEL', attributes: { prediction: 21494 }, }, { - id: 1133, + id: '1133', from: 'MAA', to: 'DEL', attributes: { prediction: 63796 }, }, { - id: 1134, + id: '1134', from: 'FRA', to: 'DLM', attributes: { prediction: 263 }, }, { - id: 1135, + id: '1135', from: 'TIF', to: 'DMM', attributes: { prediction: 1027 }, }, { - id: 1136, + id: '1136', from: 'DAC', to: 'DOH', attributes: { prediction: 9757 }, }, { - id: 1137, + id: '1137', from: 'CBR', to: 'DRW', attributes: { prediction: 1447 }, }, { - id: 1138, + id: '1138', from: 'SYR', to: 'CVG', attributes: { prediction: 1154 }, }, { - id: 1139, + id: '1139', from: 'ABQ', to: 'CVN', attributes: { prediction: 436 }, }, { - id: 1140, + id: '1140', from: 'EWR', to: 'CZM', attributes: { prediction: 430 }, }, { - id: 1141, + id: '1141', from: 'IAD', to: 'DAY', attributes: { prediction: 4617 }, }, { - id: 1142, + id: '1142', from: 'HPN', to: 'DCA', attributes: { prediction: 3668 }, }, { - id: 1143, + id: '1143', from: 'JFK', to: 'DCA', attributes: { prediction: 13150 }, }, { - id: 1144, + id: '1144', from: 'LAS', to: 'DCA', attributes: { prediction: 3379 }, }, { - id: 1145, + id: '1145', from: 'MSY', to: 'DCA', attributes: { prediction: 8436 }, }, { - id: 1146, + id: '1146', from: 'IDR', to: 'DEL', attributes: { prediction: 4275 }, }, { - id: 1147, + id: '1147', from: 'BUR', to: 'DEN', attributes: { prediction: 2433 }, }, { - id: 1148, + id: '1148', from: 'RDM', to: 'DEN', attributes: { prediction: 1083 }, }, { - id: 1149, + id: '1149', from: 'FWA', to: 'DFW', attributes: { prediction: 2260 }, }, { - id: 1150, + id: '1150', from: 'CDG', to: 'DUB', attributes: { prediction: 29719 }, }, { - id: 1151, + id: '1151', from: 'DBV', to: 'DUB', attributes: { prediction: 1795 }, }, { - id: 1152, + id: '1152', from: 'COD', to: 'DEN', attributes: { prediction: 946 }, }, { - id: 1153, + id: '1153', from: 'LNK', to: 'DEN', attributes: { prediction: 3095 }, }, { - id: 1154, + id: '1154', from: 'BNA', to: 'DFW', attributes: { prediction: 27050 }, }, { - id: 1155, + id: '1155', from: 'LGA', to: 'DFW', attributes: { prediction: 50462 }, }, { - id: 1156, + id: '1156', from: 'HRG', to: 'DUS', attributes: { prediction: 2045 }, }, { - id: 1157, + id: '1157', from: 'SOU', to: 'DUS', attributes: { prediction: 1355 }, }, { - id: 1158, + id: '1158', from: 'SUF', to: 'DUS', attributes: { prediction: 1760 }, }, { - id: 1159, + id: '1159', from: 'CGY', to: 'DVO', attributes: { prediction: 532 }, }, { - id: 1160, + id: '1160', from: 'TPA', to: 'IAD', attributes: { prediction: 14856 }, }, { - id: 1161, + id: '1161', from: 'CLE', to: 'IAH', attributes: { prediction: 21592 }, }, { - id: 1162, + id: '1162', from: 'DUB', to: 'FMM', attributes: { prediction: 3902 }, }, { - id: 1163, + id: '1163', from: 'BHX', to: 'FNC', attributes: { prediction: 677 }, }, { - id: 1164, + id: '1164', from: 'EMA', to: 'EDI', attributes: { prediction: 6463 }, }, { - id: 1165, + id: '1165', from: 'LGW', to: 'EDI', attributes: { prediction: 27776 }, }, { - id: 1166, + id: '1166', from: 'NAS', to: 'ELH', attributes: { prediction: 1827 }, }, { - id: 1167, + id: '1167', from: 'IKA', to: 'ESB', attributes: { prediction: 1659 }, }, { - id: 1168, + id: '1168', from: 'STR', to: 'ESB', attributes: { prediction: 594 }, }, { - id: 1169, + id: '1169', from: 'SXF', to: 'ESB', attributes: { prediction: 369 }, }, { - id: 1170, + id: '1170', from: 'LIR', to: 'EWR', attributes: { prediction: 970 }, }, { - id: 1171, + id: '1171', from: 'FRA', to: 'FAO', attributes: { prediction: 2310 }, }, { - id: 1172, + id: '1172', from: 'PTY', to: 'FLL', attributes: { prediction: 1431 }, }, { - id: 1173, + id: '1173', from: 'AGA', to: 'FRA', attributes: { prediction: 489 }, }, { - id: 1174, + id: '1174', from: 'JED', to: 'FRA', attributes: { prediction: 6559 }, }, { - id: 1175, + id: '1175', from: 'MZT', to: 'IAH', attributes: { prediction: 1800 }, }, { - id: 1176, + id: '1176', from: 'NRT', to: 'IAH', attributes: { prediction: 6745 }, }, { - id: 1177, + id: '1177', from: 'XNA', to: 'IAH', attributes: { prediction: 3892 }, }, { - id: 1178, + id: '1178', from: 'DPS', to: 'ICN', attributes: { prediction: 11446 }, }, { - id: 1179, + id: '1179', from: 'LGW', to: 'KEF', attributes: { prediction: 3767 }, }, { - id: 1180, + id: '1180', from: 'MZH', to: 'IST', attributes: { prediction: 3261 }, }, { - id: 1181, + id: '1181', from: 'SZF', to: 'IST', attributes: { prediction: 12305 }, }, { - id: 1182, + id: '1182', from: 'HEL', to: 'IVL', attributes: { prediction: 4360 }, }, { - id: 1183, + id: '1183', from: 'IAD', to: 'JED', attributes: { prediction: 838 }, }, { - id: 1184, + id: '1184', from: 'GIG', to: 'JFK', attributes: { prediction: 2922 }, }, { - id: 1185, + id: '1185', from: 'GVA', to: 'JFK', attributes: { prediction: 5028 }, }, { - id: 1186, + id: '1186', from: 'SMF', to: 'JFK', attributes: { prediction: 4008 }, }, { - id: 1187, + id: '1187', from: 'GYN', to: 'GRU', attributes: { prediction: 15379 }, }, { - id: 1188, + id: '1188', from: 'GRO', to: 'GSE', attributes: { prediction: 1210 }, }, { - id: 1189, + id: '1189', from: 'SFO', to: 'GUM', attributes: { prediction: 2424 }, }, { - id: 1190, + id: '1190', from: 'MIR', to: 'GVA', attributes: { prediction: 543 }, }, { - id: 1191, + id: '1191', from: 'STN', to: 'HAJ', attributes: { prediction: 5619 }, }, { - id: 1192, + id: '1192', from: 'SPU', to: 'HAM', attributes: { prediction: 453 }, }, { - id: 1193, + id: '1193', from: 'PTP', to: 'HAV', attributes: { prediction: 298 }, }, { - id: 1194, + id: '1194', from: 'KUO', to: 'HEL', attributes: { prediction: 11820 }, }, { - id: 1195, + id: '1195', from: 'CPH', to: 'HER', attributes: { prediction: 408 }, }, { - id: 1196, + id: '1196', from: 'CIF', to: 'HET', attributes: { prediction: 2131 }, }, { - id: 1197, + id: '1197', from: 'SFB', to: 'HGR', attributes: { prediction: 1149 }, }, { - id: 1198, + id: '1198', from: 'SAH', to: 'JIB', attributes: { prediction: 1366 }, }, { - id: 1199, + id: '1199', from: 'KTM', to: 'JKR', attributes: { prediction: 275 }, }, { - id: 1200, + id: '1200', from: 'AKJ', to: 'HND', attributes: { prediction: 41911 }, }, { - id: 1201, + id: '1201', from: 'KWI', to: 'HRG', attributes: { prediction: 1920 }, }, { - id: 1202, + id: '1202', from: 'BQS', to: 'HTA', attributes: { prediction: 502 }, }, { - id: 1203, + id: '1203', from: 'RJA', to: 'HYD', attributes: { prediction: 1661 }, }, { - id: 1204, + id: '1204', from: 'TPA', to: 'IAH', attributes: { prediction: 20735 }, }, { - id: 1205, + id: '1205', from: 'CDG', to: 'IBZ', attributes: { prediction: 1621 }, }, { - id: 1206, + id: '1206', from: 'STL', to: 'ICT', attributes: { prediction: 260 }, }, { - id: 1207, + id: '1207', from: 'LDB', to: 'IGU', attributes: { prediction: 606 }, }, { - id: 1208, + id: '1208', from: 'ANC', to: 'ILI', attributes: { prediction: 288 }, }, { - id: 1209, + id: '1209', from: 'DCA', to: 'ILM', attributes: { prediction: 105 }, }, { - id: 1210, + id: '1210', from: 'MPM', to: 'INH', attributes: { prediction: 192 }, }, { - id: 1211, + id: '1211', from: 'GVA', to: 'IOM', attributes: { prediction: 112 }, }, { - id: 1212, + id: '1212', from: 'JJU', to: 'JNS', attributes: { prediction: 42 }, }, { - id: 1213, + id: '1213', from: 'ATH', to: 'JSH', attributes: { prediction: 595 }, }, { - id: 1214, + id: '1214', from: 'ALA', to: 'KBL', attributes: { prediction: 299 }, }, { - id: 1215, + id: '1215', from: 'GYD', to: 'KBP', attributes: { prediction: 1909 }, }, { - id: 1216, + id: '1216', from: 'DLG', to: 'KGK', attributes: { prediction: 98 }, }, { - id: 1217, + id: '1217', from: 'PTY', to: 'KIN', attributes: { prediction: 937 }, }, { - id: 1218, + id: '1218', from: 'HND', to: 'KIX', attributes: { prediction: 68270 }, }, { - id: 1219, + id: '1219', from: 'AZN', to: 'KJA', attributes: { prediction: 500 }, }, { - id: 1220, + id: '1220', from: 'CMF', to: 'LBA', attributes: { prediction: 629 }, }, { - id: 1221, + id: '1221', from: 'LBD', to: 'LED', attributes: { prediction: 1016 }, }, { - id: 1222, + id: '1222', from: 'NJC', to: 'LED', attributes: { prediction: 767 }, }, { - id: 1223, + id: '1223', from: 'ILM', to: 'LGA', attributes: { prediction: 2848 }, }, { - id: 1224, + id: '1224', from: 'CAI', to: 'KIX', attributes: { prediction: 2558 }, }, { - id: 1225, + id: '1225', from: 'PVG', to: 'KIX', attributes: { prediction: 35499 }, }, { - id: 1226, + id: '1226', from: 'OSL', to: 'KKN', attributes: { prediction: 6304 }, }, { - id: 1227, + id: '1227', from: 'RUH', to: 'KMC', attributes: { prediction: 385 }, }, { - id: 1228, + id: '1228', from: 'CGD', to: 'KMG', attributes: { prediction: 2228 }, }, { - id: 1229, + id: '1229', from: 'MDL', to: 'KMG', attributes: { prediction: 1031 }, }, { - id: 1230, + id: '1230', from: 'WRY', to: 'KOI', attributes: { prediction: 97 }, }, { - id: 1231, + id: '1231', from: 'STN', to: 'KRK', attributes: { prediction: 8021 }, }, { - id: 1232, + id: '1232', from: 'BVA', to: 'KTW', attributes: { prediction: 1624 }, }, { - id: 1233, + id: '1233', from: 'CAN', to: 'KUL', attributes: { prediction: 10406 }, }, { - id: 1234, + id: '1234', from: 'DPS', to: 'KUL', attributes: { prediction: 22278 }, }, { - id: 1235, + id: '1235', from: 'MYY', to: 'KUL', attributes: { prediction: 15526 }, }, { - id: 1236, + id: '1236', from: 'LPL', to: 'KUN', attributes: { prediction: 1119 }, }, { - id: 1237, + id: '1237', from: 'HFE', to: 'KWE', attributes: { prediction: 1109 }, }, { - id: 1238, + id: '1238', from: 'AMM', to: 'KWI', attributes: { prediction: 11221 }, }, { - id: 1239, + id: '1239', from: 'LXR', to: 'KWI', attributes: { prediction: 6041 }, }, { - id: 1240, + id: '1240', from: 'BIS', to: 'LAS', attributes: { prediction: 1737 }, }, { - id: 1241, + id: '1241', from: 'FAT', to: 'LAS', attributes: { prediction: 6068 }, }, { - id: 1242, + id: '1242', from: 'LAS', to: 'LAX', attributes: { prediction: 92849 }, }, { - id: 1243, + id: '1243', from: 'MXP', to: 'LGW', attributes: { prediction: 12414 }, }, { - id: 1244, + id: '1244', from: 'EMK', to: 'KOT', attributes: { prediction: 321 }, }, { - id: 1245, + id: '1245', from: 'VNS', to: 'KTM', attributes: { prediction: 1780 }, }, { - id: 1246, + id: '1246', from: 'FRA', to: 'KTW', attributes: { prediction: 4848 }, }, { - id: 1247, + id: '1247', from: 'TRN', to: 'KTW', attributes: { prediction: 971 }, }, { - id: 1248, + id: '1248', from: 'AER', to: 'KUF', attributes: { prediction: 447 }, }, { - id: 1249, + id: '1249', from: 'FNT', to: 'MCO', attributes: { prediction: 2699 }, }, { - id: 1250, + id: '1250', from: 'DEL', to: 'LKO', attributes: { prediction: 22983 }, }, { - id: 1251, + id: '1251', from: 'ARN', to: 'LPA', attributes: { prediction: 477 }, }, { - id: 1252, + id: '1252', from: 'TXL', to: 'LPA', attributes: { prediction: 2834 }, }, { - id: 1253, + id: '1253', from: 'IQQ', to: 'LPB', attributes: { prediction: 1011 }, }, { - id: 1254, + id: '1254', from: 'RAK', to: 'LUX', attributes: { prediction: 382 }, }, { - id: 1255, + id: '1255', from: 'CPH', to: 'LYS', attributes: { prediction: 1252 }, }, { - id: 1256, + id: '1256', from: 'PTP', to: 'LYS', attributes: { prediction: 1727 }, }, { - id: 1257, + id: '1257', from: 'LIM', to: 'MAD', attributes: { prediction: 16042 }, }, { - id: 1258, + id: '1258', from: 'MUC', to: 'MAD', attributes: { prediction: 22356 }, }, { - id: 1259, + id: '1259', from: 'MVD', to: 'MAD', attributes: { prediction: 4336 }, }, { - id: 1260, + id: '1260', from: 'HRG', to: 'MAN', attributes: { prediction: 902 }, }, { - id: 1261, + id: '1261', from: 'ATL', to: 'MBJ', attributes: { prediction: 12368 }, }, { - id: 1262, + id: '1262', from: 'CVG', to: 'MCI', attributes: { prediction: 3411 }, }, { - id: 1263, + id: '1263', from: 'DOH', to: 'MEL', attributes: { prediction: 5896 }, }, { - id: 1264, + id: '1264', from: 'GLH', to: 'MEM', attributes: { prediction: 1148 }, }, { - id: 1265, + id: '1265', from: 'HEL', to: 'LGW', attributes: { prediction: 3587 }, }, { - id: 1266, + id: '1266', from: 'IBZ', to: 'NRN', attributes: { prediction: 2802 }, }, { - id: 1267, + id: '1267', from: 'SDJ', to: 'NRT', attributes: { prediction: 1923 }, }, { - id: 1268, + id: '1268', from: 'GDL', to: 'PHX', attributes: { prediction: 5725 }, }, { - id: 1269, + id: '1269', from: 'TUL', to: 'PHX', attributes: { prediction: 6509 }, }, { - id: 1270, + id: '1270', from: 'BFS', to: 'PMI', attributes: { prediction: 3876 }, }, { - id: 1271, + id: '1271', from: 'GRO', to: 'PMI', attributes: { prediction: 2560 }, }, { - id: 1272, + id: '1272', from: 'NBO', to: 'MRE', attributes: { prediction: 233 }, }, { - id: 1273, + id: '1273', from: 'ORY', to: 'MRU', attributes: { prediction: 3771 }, }, { - id: 1274, + id: '1274', from: 'ZRH', to: 'MUC', attributes: { prediction: 15775 }, }, { - id: 1275, + id: '1275', from: 'BUD', to: 'MXP', attributes: { prediction: 7540 }, }, { - id: 1276, + id: '1276', from: 'SVO', to: 'MXP', attributes: { prediction: 11101 }, }, { - id: 1277, + id: '1277', from: 'ITM', to: 'MYJ', attributes: { prediction: 9897 }, }, { - id: 1278, + id: '1278', from: 'MXP', to: 'RMF', attributes: { prediction: 146 }, }, { - id: 1279, + id: '1279', from: 'TIF', to: 'RUH', attributes: { prediction: 7259 }, }, { - id: 1280, + id: '1280', from: 'DOH', to: 'SAH', attributes: { prediction: 4119 }, }, { - id: 1281, + id: '1281', from: 'SVQ', to: 'SCQ', attributes: { prediction: 725 }, }, { - id: 1282, + id: '1282', from: 'YKS', to: 'PYJ', attributes: { prediction: 114 }, }, { - id: 1283, + id: '1283', from: 'BLA', to: 'PZO', attributes: { prediction: 1316 }, }, { - id: 1284, + id: '1284', from: 'GRU', to: 'REC', attributes: { prediction: 63786 }, }, { - id: 1285, + id: '1285', from: 'PIE', to: 'RFD', attributes: { prediction: 1614 }, }, { - id: 1286, + id: '1286', from: 'DSM', to: 'ORD', attributes: { prediction: 15664 }, }, { - id: 1287, + id: '1287', from: 'GSP', to: 'ORD', attributes: { prediction: 3501 }, }, { - id: 1288, + id: '1288', from: 'IAD', to: 'ORF', attributes: { prediction: 6398 }, }, { - id: 1289, + id: '1289', from: 'OSL', to: 'ORY', attributes: { prediction: 3425 }, }, { - id: 1290, + id: '1290', from: 'DXB', to: 'OTP', attributes: { prediction: 1191 }, }, { - id: 1291, + id: '1291', from: 'TGM', to: 'OTP', attributes: { prediction: 423 }, }, { - id: 1292, + id: '1292', from: 'LFW', to: 'OUA', attributes: { prediction: 3196 }, }, { - id: 1293, + id: '1293', from: 'JFK', to: 'PAP', attributes: { prediction: 7416 }, }, { - id: 1294, + id: '1294', from: 'CAY', to: 'PBM', attributes: { prediction: 310 }, }, { - id: 1295, + id: '1295', from: 'BKK', to: 'PEK', attributes: { prediction: 20200 }, }, { - id: 1296, + id: '1296', from: 'HAK', to: 'PEK', attributes: { prediction: 21245 }, }, { - id: 1297, + id: '1297', from: 'KVL', to: 'PHO', attributes: { prediction: 72 }, }, { - id: 1298, + id: '1298', from: 'SBP', to: 'PHX', attributes: { prediction: 3918 }, }, { - id: 1299, + id: '1299', from: 'AZA', to: 'PIA', attributes: { prediction: 1832 }, }, { - id: 1300, + id: '1300', from: 'PHL', to: 'PIT', attributes: { prediction: 25681 }, }, { - id: 1301, + id: '1301', from: 'AAX', to: 'PLU', attributes: { prediction: 1012 }, }, { - id: 1302, + id: '1302', from: 'WAW', to: 'PMI', attributes: { prediction: 954 }, }, { - id: 1303, + id: '1303', from: 'JNB', to: 'PNR', attributes: { prediction: 1888 }, }, { - id: 1304, + id: '1304', from: 'HHN', to: 'PRG', attributes: { prediction: 2197 }, }, { - id: 1305, + id: '1305', from: 'VNO', to: 'PRG', attributes: { prediction: 4713 }, }, { - id: 1306, + id: '1306', from: 'NNL', to: 'PTA', attributes: { prediction: 76 }, }, { - id: 1307, + id: '1307', from: 'GDL', to: 'PTY', attributes: { prediction: 1182 }, }, { - id: 1308, + id: '1308', from: 'DLC', to: 'PVG', attributes: { prediction: 52859 }, }, { - id: 1309, + id: '1309', from: 'MSP', to: 'RST', attributes: { prediction: 4038 }, }, { - id: 1310, + id: '1310', from: 'KHI', to: 'RZS', attributes: { prediction: 311 }, }, { - id: 1311, + id: '1311', from: 'SAT', to: 'SAN', attributes: { prediction: 6155 }, }, { - id: 1312, + id: '1312', from: 'ADB', to: 'SAW', attributes: { prediction: 28308 }, }, { - id: 1313, + id: '1313', from: 'BLE', to: 'SDL', attributes: { prediction: 79 }, }, { - id: 1314, + id: '1314', from: 'CDB', to: 'SDP', attributes: { prediction: 215 }, }, { - id: 1315, + id: '1315', from: 'EWR', to: 'SNN', attributes: { prediction: 3558 }, }, { - id: 1316, + id: '1316', from: 'AGP', to: 'SOF', attributes: { prediction: 436 }, }, { - id: 1317, + id: '1317', from: 'SVO', to: 'SOF', attributes: { prediction: 4264 }, }, { - id: 1318, + id: '1318', from: 'NDY', to: 'SOY', attributes: { prediction: 22 }, }, { - id: 1319, + id: '1319', from: 'BGO', to: 'SPU', attributes: { prediction: 460 }, }, { - id: 1320, + id: '1320', from: 'VIE', to: 'SPU', attributes: { prediction: 2478 }, }, { - id: 1321, + id: '1321', from: 'CTA', to: 'SSH', attributes: { prediction: 205 }, }, { - id: 1322, + id: '1322', from: 'ZRH', to: 'SSH', attributes: { prediction: 1950 }, }, { - id: 1323, + id: '1323', from: 'FLN', to: 'SCL', attributes: { prediction: 103 }, }, { - id: 1324, + id: '1324', from: 'MAD', to: 'SCU', attributes: { prediction: 950 }, }, { - id: 1325, + id: '1325', from: 'MCO', to: 'SEA', attributes: { prediction: 4667 }, }, { - id: 1326, + id: '1326', from: 'JSU', to: 'SFJ', attributes: { prediction: 316 }, }, { - id: 1327, + id: '1327', from: 'DFW', to: 'SFO', attributes: { prediction: 41616 }, }, { - id: 1328, + id: '1328', from: 'XMN', to: 'SHE', attributes: { prediction: 4524 }, }, { - id: 1329, + id: '1329', from: 'NBO', to: 'SHJ', attributes: { prediction: 1670 }, }, { - id: 1330, + id: '1330', from: 'MDW', to: 'SJC', attributes: { prediction: 3171 }, }, { - id: 1331, + id: '1331', from: 'SMF', to: 'SJC', attributes: { prediction: 2269 }, }, { - id: 1332, + id: '1332', from: 'IST', to: 'SJJ', attributes: { prediction: 5430 }, }, { - id: 1333, + id: '1333', from: 'SAT', to: 'SLC', attributes: { prediction: 3492 }, }, { - id: 1334, + id: '1334', from: 'ACE', to: 'STR', attributes: { prediction: 1205 }, }, { - id: 1335, + id: '1335', from: 'SAW', to: 'STR', attributes: { prediction: 9503 }, }, { - id: 1336, + id: '1336', from: 'LEJ', to: 'SUF', attributes: { prediction: 542 }, }, { - id: 1337, + id: '1337', from: 'OTP', to: 'SUJ', attributes: { prediction: 681 }, }, { - id: 1338, + id: '1338', from: 'DFW', to: 'SGF', attributes: { prediction: 10256 }, }, { - id: 1339, + id: '1339', from: 'LPL', to: 'TPS', attributes: { prediction: 1186 }, }, { - id: 1340, + id: '1340', from: 'NCE', to: 'TRD', attributes: { prediction: 472 }, }, { - id: 1341, + id: '1341', from: 'EZE', to: 'SYD', attributes: { prediction: 3181 }, }, { - id: 1342, + id: '1342', from: 'LNZ', to: 'SZG', attributes: { prediction: 1827 }, }, { - id: 1343, + id: '1343', from: 'CTU', to: 'SZX', attributes: { prediction: 35827 }, }, { - id: 1344, + id: '1344', from: 'NGB', to: 'TAO', attributes: { prediction: 5057 }, }, { - id: 1345, + id: '1345', from: 'DXB', to: 'TIP', attributes: { prediction: 6707 }, }, { - id: 1346, + id: '1346', from: 'GOT', to: 'TLL', attributes: { prediction: 411 }, }, { - id: 1347, + id: '1347', from: 'KRK', to: 'TLV', attributes: { prediction: 106 }, }, { - id: 1348, + id: '1348', from: 'ACY', to: 'TPA', attributes: { prediction: 3224 }, }, { - id: 1349, + id: '1349', from: 'HPN', to: 'TPA', attributes: { prediction: 2264 }, }, { - id: 1350, + id: '1350', from: 'HIJ', to: 'TPE', attributes: { prediction: 2037 }, }, { - id: 1351, + id: '1351', from: 'TIA', to: 'TSF', attributes: { prediction: 1443 }, }, { - id: 1352, + id: '1352', from: 'ZSJ', to: 'YWG', attributes: { prediction: 180 }, }, { - id: 1353, + id: '1353', from: 'YQT', to: 'YWP', attributes: { prediction: 50 }, }, { - id: 1354, + id: '1354', from: 'ZRH', to: 'ACE', attributes: { prediction: 1268 }, }, { - id: 1355, + id: '1355', from: 'PLO', to: 'ADL', attributes: { prediction: 8558 }, }, { - id: 1356, + id: '1356', from: 'EQS', to: 'AEP', attributes: { prediction: 595 }, }, { - id: 1357, + id: '1357', from: 'KRR', to: 'AER', attributes: { prediction: 1666 }, }, { - id: 1358, + id: '1358', from: 'STR', to: 'AGP', attributes: { prediction: 3633 }, }, { - id: 1359, + id: '1359', from: 'ALC', to: 'VLL', attributes: { prediction: 1986 }, }, { - id: 1360, + id: '1360', from: 'SLA', to: 'VVI', attributes: { prediction: 774 }, }, { - id: 1361, + id: '1361', from: 'EIN', to: 'WAW', attributes: { prediction: 1662 }, }, { - id: 1362, + id: '1362', from: 'EWR', to: 'WAW', attributes: { prediction: 2154 }, }, { - id: 1363, + id: '1363', from: 'FRL', to: 'WAW', attributes: { prediction: 1704 }, }, { - id: 1364, + id: '1364', from: 'LYG', to: 'XMN', attributes: { prediction: 500 }, }, { - id: 1365, + id: '1365', from: 'YOW', to: 'YEG', attributes: { prediction: 4011 }, }, { - id: 1366, + id: '1366', from: 'YVR', to: 'YPR', attributes: { prediction: 1226 }, }, { - id: 1367, + id: '1367', from: 'CUN', to: 'YUL', attributes: { prediction: 5347 }, }, { - id: 1368, + id: '1368', from: 'PVR', to: 'YUL', attributes: { prediction: 198 }, }, { - id: 1369, + id: '1369', from: 'YYC', to: 'YVR', attributes: { prediction: 69520 }, }, { - id: 1370, + id: '1370', from: 'SUR', to: 'YWP', attributes: { prediction: 62 }, }, { - id: 1371, + id: '1371', from: 'SFO', to: 'YYC', attributes: { prediction: 6213 }, }, { - id: 1372, + id: '1372', from: 'YLW', to: 'YYJ', attributes: { prediction: 2445 }, }, { - id: 1373, + id: '1373', from: 'GGT', to: 'YYZ', attributes: { prediction: 282 }, }, { - id: 1374, + id: '1374', from: 'YKT', to: 'ZEL', attributes: { prediction: 17 }, }, { - id: 1375, + id: '1375', from: 'FUE', to: 'ZQW', attributes: { prediction: 625 }, }, { - id: 1376, + id: '1376', from: 'SOF', to: 'ZRH', attributes: { prediction: 3799 }, }, { - id: 1377, + id: '1377', from: 'TIA', to: 'ZRH', attributes: { prediction: 660 }, }, { - id: 1378, + id: '1378', from: 'VIE', to: 'ZRH', attributes: { prediction: 36870 }, }, { - id: 1379, + id: '1379', from: 'SEA', to: 'ALW', attributes: { prediction: 2326 }, }, { - id: 1380, + id: '1380', from: 'MXP', to: 'AMM', attributes: { prediction: 1657 }, }, { - id: 1381, + id: '1381', from: 'AMM', to: 'ALY', attributes: { prediction: 1491 }, }, { - id: 1382, + id: '1382', from: 'ATH', to: 'AMM', attributes: { prediction: 1698 }, }, { - id: 1383, + id: '1383', from: 'YUL', to: 'BDL', attributes: { prediction: 1135 }, }, { - id: 1384, + id: '1384', from: 'EMK', to: 'BET', attributes: { prediction: 224 }, }, { - id: 1385, + id: '1385', from: 'BCN', to: 'BFS', attributes: { prediction: 4583 }, }, { - id: 1386, + id: '1386', from: 'LED', to: 'AMS', attributes: { prediction: 6427 }, }, { - id: 1387, + id: '1387', from: 'TMM', to: 'ANM', attributes: { prediction: 196 }, }, { - id: 1388, + id: '1388', from: 'NRN', to: 'AOI', attributes: { prediction: 1920 }, }, { - id: 1389, + id: '1389', from: 'SCL', to: 'ARI', attributes: { prediction: 4254 }, }, { - id: 1390, + id: '1390', from: 'SYD', to: 'ASP', attributes: { prediction: 3360 }, }, { - id: 1391, + id: '1391', from: 'BNA', to: 'ATL', attributes: { prediction: 20604 }, }, { - id: 1392, + id: '1392', from: 'GTR', to: 'ATL', attributes: { prediction: 1606 }, }, { - id: 1393, + id: '1393', from: 'MEX', to: 'ATL', attributes: { prediction: 12813 }, }, { - id: 1394, + id: '1394', from: 'ASB', to: 'ATQ', attributes: { prediction: 3991 }, }, { - id: 1395, + id: '1395', from: 'MCO', to: 'AUA', attributes: { prediction: 520 }, }, { - id: 1396, + id: '1396', from: 'LHR', to: 'AUH', attributes: { prediction: 23795 }, }, { - id: 1397, + id: '1397', from: 'FLL', to: 'AUS', attributes: { prediction: 5953 }, }, { - id: 1398, + id: '1398', from: 'CTS', to: 'AXT', attributes: { prediction: 3658 }, }, { - id: 1399, + id: '1399', from: 'TLV', to: 'AYT', attributes: { prediction: 2923 }, }, { - id: 1400, + id: '1400', from: 'MFR', to: 'AZA', attributes: { prediction: 1165 }, }, { - id: 1401, + id: '1401', from: 'DEL', to: 'BAH', attributes: { prediction: 8425 }, }, { - id: 1402, + id: '1402', from: 'SHJ', to: 'BAH', attributes: { prediction: 6639 }, }, { - id: 1403, + id: '1403', from: 'NTE', to: 'BGY', attributes: { prediction: 2619 }, }, { - id: 1404, + id: '1404', from: 'LPL', to: 'BHD', attributes: { prediction: 13082 }, }, { - id: 1405, + id: '1405', from: 'YYZ', to: 'BHX', attributes: { prediction: 1633 }, }, { - id: 1406, + id: '1406', from: 'BRU', to: 'BJM', attributes: { prediction: 1708 }, }, { - id: 1407, + id: '1407', from: 'DLM', to: 'BHX', attributes: { prediction: 3147 }, }, { - id: 1408, + id: '1408', from: 'ADD', to: 'BJR', attributes: { prediction: 4528 }, }, { - id: 1409, + id: '1409', from: 'SZX', to: 'BKI', attributes: { prediction: 3752 }, }, { - id: 1410, + id: '1410', from: 'NGO', to: 'BKK', attributes: { prediction: 11347 }, }, { - id: 1411, + id: '1411', from: 'AHO', to: 'BVA', attributes: { prediction: 1341 }, }, { - id: 1412, + id: '1412', from: 'MAN', to: 'BVC', attributes: { prediction: 796 }, }, { - id: 1413, + id: '1413', from: 'AUS', to: 'BWI', attributes: { prediction: 6636 }, }, { - id: 1414, + id: '1414', from: 'RSW', to: 'BWI', attributes: { prediction: 10642 }, }, { - id: 1415, + id: '1415', from: 'ZAG', to: 'BWK', attributes: { prediction: 292 }, }, { - id: 1416, + id: '1416', from: 'NYO', to: 'BZR', attributes: { prediction: 1279 }, }, { - id: 1417, + id: '1417', from: 'HAD', to: 'BMA', attributes: { prediction: 1122 }, }, { - id: 1418, + id: '1418', from: 'BDB', to: 'BNE', attributes: { prediction: 3414 }, }, { - id: 1419, + id: '1419', from: 'PMI', to: 'BOH', attributes: { prediction: 473 }, }, { - id: 1420, + id: '1420', from: 'AUS', to: 'BOS', attributes: { prediction: 2621 }, }, { - id: 1421, + id: '1421', from: 'PQI', to: 'BOS', attributes: { prediction: 1810 }, }, { - id: 1422, + id: '1422', from: 'SNW', to: 'BSX', attributes: { prediction: 106 }, }, { - id: 1423, + id: '1423', from: 'BRU', to: 'BUD', attributes: { prediction: 8781 }, }, { - id: 1424, + id: '1424', from: 'CGN', to: 'CAI', attributes: { prediction: 1221 }, }, { - id: 1425, + id: '1425', from: 'CGN', to: 'CAG', attributes: { prediction: 1923 }, }, { - id: 1426, + id: '1426', from: 'VIE', to: 'CAG', attributes: { prediction: 380 }, }, { - id: 1427, + id: '1427', from: 'LUX', to: 'CDG', attributes: { prediction: 2818 }, }, { - id: 1428, + id: '1428', from: 'RBA', to: 'CDG', attributes: { prediction: 7123 }, }, { - id: 1429, + id: '1429', from: 'ICN', to: 'CEB', attributes: { prediction: 13565 }, }, { - id: 1430, + id: '1430', from: 'FAI', to: 'CEM', attributes: { prediction: 160 }, }, { - id: 1431, + id: '1431', from: 'MEL', to: 'CHC', attributes: { prediction: 8615 }, }, { - id: 1432, + id: '1432', from: 'CJU', to: 'CJJ', attributes: { prediction: 21188 }, }, { - id: 1433, + id: '1433', from: 'CSX', to: 'CKG', attributes: { prediction: 10201 }, }, { - id: 1434, + id: '1434', from: 'MEX', to: 'CLQ', attributes: { prediction: 2619 }, }, { - id: 1435, + id: '1435', from: 'ICN', to: 'CNX', attributes: { prediction: 856 }, }, { - id: 1436, + id: '1436', from: 'SFO', to: 'COS', attributes: { prediction: 1107 }, }, { - id: 1437, + id: '1437', from: 'AGP', to: 'CPH', attributes: { prediction: 12520 }, }, { - id: 1438, + id: '1438', from: 'KEF', to: 'CPH', attributes: { prediction: 15650 }, }, { - id: 1439, + id: '1439', from: 'PMI', to: 'CPH', attributes: { prediction: 7624 }, }, { - id: 1440, + id: '1440', from: 'PSA', to: 'CPH', attributes: { prediction: 2612 }, }, { - id: 1441, + id: '1441', from: 'WAW', to: 'CPH', attributes: { prediction: 9537 }, }, { - id: 1442, + id: '1442', from: 'NQN', to: 'CRD', attributes: { prediction: 887 }, }, { - id: 1443, + id: '1443', from: 'GRO', to: 'CRL', attributes: { prediction: 9873 }, }, { - id: 1444, + id: '1444', from: 'JFK', to: 'CPH', attributes: { prediction: 4636 }, }, { - id: 1445, + id: '1445', from: 'KRP', to: 'CPH', attributes: { prediction: 19106 }, }, { - id: 1446, + id: '1446', from: 'SLC', to: 'HLN', attributes: { prediction: 3349 }, }, { - id: 1447, + id: '1447', from: 'SAN', to: 'HOU', attributes: { prediction: 6589 }, }, { - id: 1448, + id: '1448', from: 'BSL', to: 'HRG', attributes: { prediction: 1779 }, }, { - id: 1449, + id: '1449', from: 'LAS', to: 'HSV', attributes: { prediction: 250 }, }, { - id: 1450, + id: '1450', from: 'RDU', to: 'IAD', attributes: { prediction: 13043 }, }, { - id: 1451, + id: '1451', from: 'EBA', to: 'FDH', attributes: { prediction: 323 }, }, { - id: 1452, + id: '1452', from: 'GRZ', to: 'FDH', attributes: { prediction: 922 }, }, { - id: 1453, + id: '1453', from: 'VIE', to: 'FDH', attributes: { prediction: 3350 }, }, { - id: 1454, + id: '1454', from: 'ELH', to: 'FLL', attributes: { prediction: 1137 }, }, { - id: 1455, + id: '1455', from: 'XAP', to: 'FLN', attributes: { prediction: 7329 }, }, { - id: 1456, + id: '1456', from: 'CMN', to: 'FNA', attributes: { prediction: 1068 }, }, { - id: 1457, + id: '1457', from: 'LOS', to: 'FNA', attributes: { prediction: 271 }, }, { - id: 1458, + id: '1458', from: 'SFO', to: 'DFW', attributes: { prediction: 41850 }, }, { - id: 1459, + id: '1459', from: 'SCO', to: 'DME', attributes: { prediction: 1351 }, }, { - id: 1460, + id: '1460', from: 'SIN', to: 'DOH', attributes: { prediction: 7307 }, }, { - id: 1461, + id: '1461', from: 'MIR', to: 'DSA', attributes: { prediction: 676 }, }, { - id: 1462, + id: '1462', from: 'BWI', to: 'DTW', attributes: { prediction: 31859 }, }, { - id: 1463, + id: '1463', from: 'NTE', to: 'DUB', attributes: { prediction: 1849 }, }, { - id: 1464, + id: '1464', from: 'SVO', to: 'DXB', attributes: { prediction: 4635 }, }, { - id: 1465, + id: '1465', from: 'BWI', to: 'ECP', attributes: { prediction: 853 }, }, { - id: 1466, + id: '1466', from: 'MAD', to: 'EDI', attributes: { prediction: 3765 }, }, { - id: 1467, + id: '1467', from: 'SOU', to: 'EDI', attributes: { prediction: 11269 }, }, { - id: 1468, + id: '1468', from: 'AKN', to: 'EGX', attributes: { prediction: 34 }, }, { - id: 1469, + id: '1469', from: 'ORD', to: 'ELP', attributes: { prediction: 9941 }, }, { - id: 1470, + id: '1470', from: 'AUA', to: 'EWR', attributes: { prediction: 5108 }, }, { - id: 1471, + id: '1471', from: 'FLL', to: 'EWR', attributes: { prediction: 36389 }, }, { - id: 1472, + id: '1472', from: 'FRA', to: 'EWR', attributes: { prediction: 27494 }, }, { - id: 1473, + id: '1473', from: 'KBC', to: 'FAI', attributes: { prediction: 113 }, }, { - id: 1474, + id: '1474', from: 'SEA', to: 'FAI', attributes: { prediction: 14403 }, }, { - id: 1475, + id: '1475', from: 'GLA', to: 'FAO', attributes: { prediction: 1104 }, }, { - id: 1476, + id: '1476', from: 'ALG', to: 'FCO', attributes: { prediction: 6057 }, }, { - id: 1477, + id: '1477', from: 'FLR', to: 'FCO', attributes: { prediction: 8556 }, }, { - id: 1478, + id: '1478', from: 'MAD', to: 'FCO', attributes: { prediction: 55689 }, }, { - id: 1479, + id: '1479', from: 'MAN', to: 'FCO', attributes: { prediction: 2576 }, }, { - id: 1480, + id: '1480', from: 'EWR', to: 'GCM', attributes: { prediction: 1232 }, }, { - id: 1481, + id: '1481', from: 'IAH', to: 'GDL', attributes: { prediction: 6564 }, }, { - id: 1482, + id: '1482', from: 'TLC', to: 'GDL', attributes: { prediction: 13069 }, }, { - id: 1483, + id: '1483', from: 'CGN', to: 'GDN', attributes: { prediction: 1849 }, }, { - id: 1484, + id: '1484', from: 'EDI', to: 'GDN', attributes: { prediction: 2022 }, }, { - id: 1485, + id: '1485', from: 'DFW', to: 'GJT', attributes: { prediction: 2295 }, }, { - id: 1486, + id: '1486', from: 'BJF', to: 'HFT', attributes: { prediction: 137 }, }, { - id: 1487, + id: '1487', from: 'KIX', to: 'GMP', attributes: { prediction: 26359 }, }, { - id: 1488, + id: '1488', from: 'IST', to: 'GNY', attributes: { prediction: 6011 }, }, { - id: 1489, + id: '1489', from: 'BRS', to: 'GRO', attributes: { prediction: 1957 }, }, { - id: 1490, + id: '1490', from: 'BDL', to: 'GRR', attributes: { prediction: 81 }, }, { - id: 1491, + id: '1491', from: 'FSD', to: 'GRR', attributes: { prediction: 38 }, }, { - id: 1492, + id: '1492', from: 'IAH', to: 'GSP', attributes: { prediction: 3378 }, }, { - id: 1493, + id: '1493', from: 'BIA', to: 'GVA', attributes: { prediction: 586 }, }, { - id: 1494, + id: '1494', from: 'MIA', to: 'GYE', attributes: { prediction: 10718 }, }, { - id: 1495, + id: '1495', from: 'BHX', to: 'HAJ', attributes: { prediction: 2539 }, }, { - id: 1496, + id: '1496', from: 'KRK', to: 'HEL', attributes: { prediction: 860 }, }, { - id: 1497, + id: '1497', from: 'SVO', to: 'HEL', attributes: { prediction: 7296 }, }, { - id: 1498, + id: '1498', from: 'FRA', to: 'HER', attributes: { prediction: 4981 }, }, { - id: 1499, + id: '1499', from: 'DTW', to: 'HPN', attributes: { prediction: 4626 }, }, { - id: 1500, + id: '1500', from: 'IND', to: 'HSV', attributes: { prediction: 152 }, }, { - id: 1501, + id: '1501', from: 'PEK', to: 'JJN', attributes: { prediction: 1765 }, }, { - id: 1502, + id: '1502', from: 'MQP', to: 'JNB', attributes: { prediction: 7410 }, }, { - id: 1503, + id: '1503', from: 'ATH', to: 'JNX', attributes: { prediction: 1044 }, }, { - id: 1504, + id: '1504', from: 'KUL', to: 'JOG', attributes: { prediction: 5152 }, }, { - id: 1505, + id: '1505', from: 'DBV', to: 'KBP', attributes: { prediction: 657 }, }, { - id: 1506, + id: '1506', from: 'DTM', to: 'KBP', attributes: { prediction: 3679 }, }, { - id: 1507, + id: '1507', from: 'DUS', to: 'KBP', attributes: { prediction: 4782 }, }, { - id: 1508, + id: '1508', from: 'HAN', to: 'KHH', attributes: { prediction: 1233 }, }, { - id: 1509, + id: '1509', from: 'NGB', to: 'KHH', attributes: { prediction: 1119 }, }, { - id: 1510, + id: '1510', from: 'JEG', to: 'JQA', attributes: { prediction: 266 }, }, { - id: 1511, + id: '1511', from: 'XIY', to: 'JZH', attributes: { prediction: 2532 }, }, { - id: 1512, + id: '1512', from: 'WUH', to: 'ICN', attributes: { prediction: 1652 }, }, { - id: 1513, + id: '1513', from: 'ANC', to: 'ILI', attributes: { prediction: 367 }, }, { - id: 1514, + id: '1514', from: 'JGO', to: 'JEG', attributes: { prediction: 46 }, }, { - id: 1515, + id: '1515', from: 'ACK', to: 'JFK', attributes: { prediction: 334 }, }, { - id: 1516, + id: '1516', from: 'ARN', to: 'JFK', attributes: { prediction: 597 }, }, { - id: 1517, + id: '1517', from: 'DCA', to: 'JFK', attributes: { prediction: 13578 }, }, { - id: 1518, + id: '1518', from: 'GND', to: 'JFK', attributes: { prediction: 959 }, }, { - id: 1519, + id: '1519', from: 'MCI', to: 'JFK', attributes: { prediction: 2042 }, }, { - id: 1520, + id: '1520', from: 'SAN', to: 'JFK', attributes: { prediction: 19532 }, }, { - id: 1521, + id: '1521', from: 'SBW', to: 'JHB', attributes: { prediction: 1647 }, }, { - id: 1522, + id: '1522', from: 'ASO', to: 'JIM', attributes: { prediction: 639 }, }, { - id: 1523, + id: '1523', from: 'NKM', to: 'KMJ', attributes: { prediction: 1297 }, }, { - id: 1524, + id: '1524', from: 'DME', to: 'MAD', attributes: { prediction: 6280 }, }, { - id: 1525, + id: '1525', from: 'MVD', to: 'MAD', attributes: { prediction: 4200 }, }, { - id: 1526, + id: '1526', from: 'TFS', to: 'MAN', attributes: { prediction: 19768 }, }, { - id: 1527, + id: '1527', from: 'FLL', to: 'MCI', attributes: { prediction: 3405 }, }, { - id: 1528, + id: '1528', from: 'BNA', to: 'MCO', attributes: { prediction: 17466 }, }, { - id: 1529, + id: '1529', from: 'BQN', to: 'MCO', attributes: { prediction: 3903 }, }, { - id: 1530, + id: '1530', from: 'CNP', to: 'KUS', attributes: { prediction: 94 }, }, { - id: 1531, + id: '1531', from: 'MJT', to: 'KVA', attributes: { prediction: 370 }, }, { - id: 1532, + id: '1532', from: 'XUZ', to: 'KWE', attributes: { prediction: 458 }, }, { - id: 1533, + id: '1533', from: 'KHZ', to: 'KXU', attributes: { prediction: 126 }, }, { - id: 1534, + id: '1534', from: 'RHO', to: 'KZS', attributes: { prediction: 639 }, }, { - id: 1535, + id: '1535', from: 'JNB', to: 'LAD', attributes: { prediction: 8914 }, }, { - id: 1536, + id: '1536', from: 'BZN', to: 'LAS', attributes: { prediction: 911 }, }, { - id: 1537, + id: '1537', from: 'TUL', to: 'LAS', attributes: { prediction: 3510 }, }, { - id: 1538, + id: '1538', from: 'DUS', to: 'LAX', attributes: { prediction: 1706 }, }, { - id: 1539, + id: '1539', from: 'RKT', to: 'LBD', attributes: { prediction: 319 }, }, { - id: 1540, + id: '1540', from: 'ATH', to: 'LCA', attributes: { prediction: 38863 }, }, { - id: 1541, + id: '1541', from: 'HAM', to: 'LCA', attributes: { prediction: 680 }, }, { - id: 1542, + id: '1542', from: 'EIN', to: 'LCY', attributes: { prediction: 1604 }, }, { - id: 1543, + id: '1543', from: 'MJK', to: 'LEA', attributes: { prediction: 91 }, }, { - id: 1544, + id: '1544', from: 'CHO', to: 'LGA', attributes: { prediction: 1769 }, }, { - id: 1545, + id: '1545', from: 'YYZ', to: 'LGA', attributes: { prediction: 31265 }, }, { - id: 1546, + id: '1546', from: 'CTG', to: 'MDE', attributes: { prediction: 3064 }, }, { - id: 1547, + id: '1547', from: 'ABZ', to: 'LHR', attributes: { prediction: 30223 }, }, { - id: 1548, + id: '1548', from: 'YOW', to: 'LHR', attributes: { prediction: 4837 }, }, { - id: 1549, + id: '1549', from: 'GIG', to: 'LIM', attributes: { prediction: 2385 }, }, { - id: 1550, + id: '1550', from: 'MHT', to: 'LIR', attributes: { prediction: 139 }, }, { - id: 1551, + id: '1551', from: 'MYY', to: 'LMN', attributes: { prediction: 2149 }, }, { - id: 1552, + id: '1552', from: 'BRS', to: 'LRH', attributes: { prediction: 519 }, }, { - id: 1553, + id: '1553', from: 'CYB', to: 'LYB', attributes: { prediction: 176 }, }, { - id: 1554, + id: '1554', from: 'BES', to: 'LYS', attributes: { prediction: 5123 }, }, { - id: 1555, + id: '1555', from: 'LUX', to: 'LYS', attributes: { prediction: 527 }, }, { - id: 1556, + id: '1556', from: 'MLM', to: 'MEX', attributes: { prediction: 4929 }, }, { - id: 1557, + id: '1557', from: 'SAL', to: 'MGA', attributes: { prediction: 6995 }, }, { - id: 1558, + id: '1558', from: 'KRR', to: 'LED', attributes: { prediction: 1440 }, }, { - id: 1559, + id: '1559', from: 'ICN', to: 'MUC', attributes: { prediction: 4476 }, }, { - id: 1560, + id: '1560', from: 'JTR', to: 'MUC', attributes: { prediction: 400 }, }, { - id: 1561, + id: '1561', from: 'IAD', to: 'MVD', attributes: { prediction: 114 }, }, { - id: 1562, + id: '1562', from: 'BHX', to: 'MXP', attributes: { prediction: 2484 }, }, { - id: 1563, + id: '1563', from: 'GRU', to: 'MXP', attributes: { prediction: 6633 }, }, { - id: 1564, + id: '1564', from: 'DCA', to: 'MYR', attributes: { prediction: 357 }, }, { - id: 1565, + id: '1565', from: 'TSA', to: 'MZG', attributes: { prediction: 19062 }, }, { - id: 1566, + id: '1566', from: 'HKG', to: 'NAN', attributes: { prediction: 1638 }, }, { - id: 1567, + id: '1567', from: 'TCB', to: 'MHH', attributes: { prediction: 125 }, }, { - id: 1568, + id: '1568', from: 'CVG', to: 'MIA', attributes: { prediction: 4897 }, }, { - id: 1569, + id: '1569', from: 'SXM', to: 'MIA', attributes: { prediction: 5597 }, }, { - id: 1570, + id: '1570', from: 'TGZ', to: 'MID', attributes: { prediction: 1844 }, }, { - id: 1571, + id: '1571', from: 'MAN', to: 'MJV', attributes: { prediction: 5755 }, }, { - id: 1572, + id: '1572', from: 'BNA', to: 'MKE', attributes: { prediction: 2670 }, }, { - id: 1573, + id: '1573', from: 'MCO', to: 'MLI', attributes: { prediction: 1633 }, }, { - id: 1574, + id: '1574', from: 'AUH', to: 'MNL', attributes: { prediction: 15674 }, }, { - id: 1575, + id: '1575', from: 'HKG', to: 'MNL', attributes: { prediction: 86110 }, }, { - id: 1576, + id: '1576', from: 'CDG', to: 'MPL', attributes: { prediction: 14134 }, }, { - id: 1577, + id: '1577', from: 'AXU', to: 'MQX', attributes: { prediction: 143 }, }, { - id: 1578, + id: '1578', from: 'ORD', to: 'MSN', attributes: { prediction: 14543 }, }, { - id: 1579, + id: '1579', from: 'ESB', to: 'MSR', attributes: { prediction: 1689 }, }, { - id: 1580, + id: '1580', from: 'ALB', to: 'MSS', attributes: { prediction: 573 }, }, { - id: 1581, + id: '1581', from: 'SLP', to: 'MTY', attributes: { prediction: 1543 }, }, { - id: 1582, + id: '1582', from: 'CGN', to: 'NCE', attributes: { prediction: 3786 }, }, { - id: 1583, + id: '1583', from: 'CPH', to: 'NCE', attributes: { prediction: 13036 }, }, { - id: 1584, + id: '1584', from: 'SOU', to: 'NCE', attributes: { prediction: 1097 }, }, { - id: 1585, + id: '1585', from: 'SSH', to: 'NCL', attributes: { prediction: 2451 }, }, { - id: 1586, + id: '1586', from: 'BOM', to: 'NDC', attributes: { prediction: 354 }, }, { - id: 1587, + id: '1587', from: 'SYX', to: 'NGB', attributes: { prediction: 4319 }, }, { - id: 1588, + id: '1588', from: 'AOJ', to: 'NGO', attributes: { prediction: 6843 }, }, { - id: 1589, + id: '1589', from: 'HKG', to: 'NGO', attributes: { prediction: 8646 }, }, { - id: 1590, + id: '1590', from: 'MNL', to: 'NGO', attributes: { prediction: 13334 }, }, { - id: 1591, + id: '1591', from: 'PRG', to: 'NAP', attributes: { prediction: 3388 }, }, { - id: 1592, + id: '1592', from: 'FEN', to: 'NAT', attributes: { prediction: 1123 }, }, { - id: 1593, + id: '1593', from: 'CMN', to: 'NCE', attributes: { prediction: 2439 }, }, { - id: 1594, + id: '1594', from: 'AGP', to: 'NCL', attributes: { prediction: 9294 }, }, { - id: 1595, + id: '1595', from: 'PTP', to: 'PAP', attributes: { prediction: 3189 }, }, { - id: 1596, + id: '1596', from: 'BOS', to: 'PDX', attributes: { prediction: 4104 }, }, { - id: 1597, + id: '1597', from: 'IAH', to: 'PDX', attributes: { prediction: 17260 }, }, { - id: 1598, + id: '1598', from: 'BHY', to: 'PEK', attributes: { prediction: 2849 }, }, { - id: 1599, + id: '1599', from: 'CHG', to: 'PEK', attributes: { prediction: 318 }, }, { - id: 1600, + id: '1600', from: 'YYC', to: 'NRT', attributes: { prediction: 2172 }, }, { - id: 1601, + id: '1601', from: 'EDI', to: 'NWI', attributes: { prediction: 3412 }, }, { - id: 1602, + id: '1602', from: 'LBC', to: 'NYO', attributes: { prediction: 4255 }, }, { - id: 1603, + id: '1603', from: 'STL', to: 'OKC', attributes: { prediction: 5633 }, }, { - id: 1604, + id: '1604', from: 'EWR', to: 'OMA', attributes: { prediction: 3274 }, }, { - id: 1605, + id: '1605', from: 'GIG', to: 'OPO', attributes: { prediction: 1575 }, }, { - id: 1606, + id: '1606', from: 'BOS', to: 'ORD', attributes: { prediction: 67269 }, }, { - id: 1607, + id: '1607', from: 'PUJ', to: 'ORD', attributes: { prediction: 1744 }, }, { - id: 1608, + id: '1608', from: 'TRD', to: 'OSY', attributes: { prediction: 1034 }, }, { - id: 1609, + id: '1609', from: 'BNE', to: 'PER', attributes: { prediction: 27372 }, }, { - id: 1610, + id: '1610', from: 'CDG', to: 'PHL', attributes: { prediction: 10387 }, }, { - id: 1611, + id: '1611', from: 'MAD', to: 'OTP', attributes: { prediction: 9466 }, }, { - id: 1612, + id: '1612', from: 'SKG', to: 'OTP', attributes: { prediction: 1768 }, }, { - id: 1613, + id: '1613', from: 'TLV', to: 'OTP', attributes: { prediction: 12540 }, }, { - id: 1614, + id: '1614', from: 'LFW', to: 'OUA', attributes: { prediction: 4689 }, }, { - id: 1615, + id: '1615', from: 'CLE', to: 'PBI', attributes: { prediction: 1259 }, }, { - id: 1616, + id: '1616', from: 'TYS', to: 'PGD', attributes: { prediction: 1161 }, }, { - id: 1617, + id: '1617', from: 'DUB', to: 'PHL', attributes: { prediction: 4807 }, }, { - id: 1618, + id: '1618', from: 'ANC', to: 'PHX', attributes: { prediction: 2849 }, }, { - id: 1619, + id: '1619', from: 'SXB', to: 'PRG', attributes: { prediction: 1326 }, }, { - id: 1620, + id: '1620', from: 'SFO', to: 'PSP', attributes: { prediction: 11454 }, }, { - id: 1621, + id: '1621', from: 'MGA', to: 'PTY', attributes: { prediction: 3687 }, }, { - id: 1622, + id: '1622', from: 'LGW', to: 'PUJ', attributes: { prediction: 1621 }, }, { - id: 1623, + id: '1623', from: 'KIX', to: 'PVG', attributes: { prediction: 42652 }, }, { - id: 1624, + id: '1624', from: 'LGA', to: 'PWM', attributes: { prediction: 5434 }, }, { - id: 1625, + id: '1625', from: 'AZA', to: 'RDM', attributes: { prediction: 1141 }, }, { - id: 1626, + id: '1626', from: 'PDX', to: 'RDM', attributes: { prediction: 6597 }, }, { - id: 1627, + id: '1627', from: 'LGA', to: 'RDU', attributes: { prediction: 21322 }, }, { - id: 1628, + id: '1628', from: 'SXF', to: 'RHO', attributes: { prediction: 434 }, }, { - id: 1629, + id: '1629', from: 'HAM', to: 'RIX', attributes: { prediction: 4625 }, }, { - id: 1630, + id: '1630', from: 'VNO', to: 'RIX', attributes: { prediction: 11920 }, }, { - id: 1631, + id: '1631', from: 'PMV', to: 'PZO', attributes: { prediction: 1290 }, }, { - id: 1632, + id: '1632', from: 'VIE', to: 'TPE', attributes: { prediction: 2948 }, }, { - id: 1633, + id: '1633', from: 'CKG', to: 'XIY', attributes: { prediction: 12639 }, }, { - id: 1634, + id: '1634', from: 'JZH', to: 'XIY', attributes: { prediction: 2619 }, }, { - id: 1635, + id: '1635', from: 'IKS', to: 'YKS', attributes: { prediction: 624 }, }, { - id: 1636, + id: '1636', from: 'YVR', to: 'YLW', attributes: { prediction: 16858 }, }, { - id: 1637, + id: '1637', from: 'BRI', to: 'TIA', attributes: { prediction: 4281 }, }, { - id: 1638, + id: '1638', from: 'CUU', to: 'TLC', attributes: { prediction: 6807 }, }, { - id: 1639, + id: '1639', from: 'AUS', to: 'TLN', attributes: { prediction: 10 }, }, { - id: 1640, + id: '1640', from: 'DUS', to: 'TLV', attributes: { prediction: 638 }, }, { - id: 1641, + id: '1641', from: 'CTU', to: 'TNA', attributes: { prediction: 11263 }, }, { - id: 1642, + id: '1642', from: 'WAA', to: 'SHH', attributes: { prediction: 24 }, }, { - id: 1643, + id: '1643', from: 'ALA', to: 'SHJ', attributes: { prediction: 2429 }, }, { - id: 1644, + id: '1644', from: 'IAH', to: 'SHV', attributes: { prediction: 5737 }, }, { - id: 1645, + id: '1645', from: 'CPH', to: 'SIN', attributes: { prediction: 3279 }, }, { - id: 1646, + id: '1646', from: 'HRE', to: 'SIN', attributes: { prediction: 529 }, }, { - id: 1647, + id: '1647', from: 'PNH', to: 'SIN', attributes: { prediction: 8842 }, }, { - id: 1648, + id: '1648', from: 'FRA', to: 'SIP', attributes: { prediction: 621 }, }, { - id: 1649, + id: '1649', from: 'SKD', to: 'SIP', attributes: { prediction: 587 }, }, { - id: 1650, + id: '1650', from: 'SNA', to: 'SJC', attributes: { prediction: 19716 }, }, { - id: 1651, + id: '1651', from: 'MNL', to: 'SJI', attributes: { prediction: 3077 }, }, { - id: 1652, + id: '1652', from: 'SMI', to: 'SKG', attributes: { prediction: 488 }, }, { - id: 1653, + id: '1653', from: 'IAH', to: 'SLC', attributes: { prediction: 14582 }, }, { - id: 1654, + id: '1654', from: 'TOS', to: 'SOJ', attributes: { prediction: 911 }, }, { - id: 1655, + id: '1655', from: 'AMS', to: 'SPU', attributes: { prediction: 520 }, }, { - id: 1656, + id: '1656', from: 'LKL', to: 'TOS', attributes: { prediction: 1792 }, }, { - id: 1657, + id: '1657', from: 'KMQ', to: 'TPE', attributes: { prediction: 828 }, }, { - id: 1658, + id: '1658', from: 'MEX', to: 'TRC', attributes: { prediction: 11964 }, }, { - id: 1659, + id: '1659', from: 'ZAG', to: 'STR', attributes: { prediction: 2673 }, }, { - id: 1660, + id: '1660', from: 'MXP', to: 'SUF', attributes: { prediction: 8468 }, }, { - id: 1661, + id: '1661', from: 'WNN', to: 'SUR', attributes: { prediction: 36 }, }, { - id: 1662, + id: '1662', from: 'BLL', to: 'SVG', attributes: { prediction: 579 }, }, { - id: 1663, + id: '1663', from: 'TRD', to: 'SVG', attributes: { prediction: 3454 }, }, { - id: 1664, + id: '1664', from: 'SVO', to: 'SVX', attributes: { prediction: 15163 }, }, { - id: 1665, + id: '1665', from: 'PVG', to: 'TAE', attributes: { prediction: 2017 }, }, { - id: 1666, + id: '1666', from: 'KSQ', to: 'TAS', attributes: { prediction: 3326 }, }, { - id: 1667, + id: '1667', from: 'ROV', to: 'TAS', attributes: { prediction: 1459 }, }, { - id: 1668, + id: '1668', from: 'SCO', to: 'TBS', attributes: { prediction: 566 }, }, { - id: 1669, + id: '1669', from: 'TLV', to: 'TBS', attributes: { prediction: 1507 }, }, { - id: 1670, + id: '1670', from: 'APW', to: 'TBU', attributes: { prediction: 620 }, }, { - id: 1671, + id: '1671', from: 'GUA', to: 'TGU', attributes: { prediction: 1706 }, }, { - id: 1672, + id: '1672', from: 'NAP', to: 'TRS', attributes: { prediction: 2157 }, }, { - id: 1673, + id: '1673', from: 'DTM', to: 'TSR', attributes: { prediction: 1330 }, }, { - id: 1674, + id: '1674', from: 'OOL', to: 'TSV', attributes: { prediction: 1529 }, }, { - id: 1675, + id: '1675', from: 'CAI', to: 'TXL', attributes: { prediction: 1232 }, }, { - id: 1676, + id: '1676', from: 'CLE', to: 'ABE', attributes: { prediction: 3111 }, }, { - id: 1677, + id: '1677', from: 'YRF', to: 'YBI', attributes: { prediction: 97 }, }, { - id: 1678, + id: '1678', from: 'DMK', to: 'URT', attributes: { prediction: 3746 }, }, { - id: 1679, + id: '1679', from: 'DUS', to: 'VAR', attributes: { prediction: 1102 }, }, { - id: 1680, + id: '1680', from: 'BJF', to: 'VAW', attributes: { prediction: 131 }, }, { - id: 1681, + id: '1681', from: 'ARC', to: 'VEE', attributes: { prediction: 142 }, }, { - id: 1682, + id: '1682', from: 'CUN', to: 'VER', attributes: { prediction: 3615 }, }, { - id: 1683, + id: '1683', from: 'FNC', to: 'VIE', attributes: { prediction: 2087 }, }, { - id: 1684, + id: '1684', from: 'KHV', to: 'VKO', attributes: { prediction: 4716 }, }, { - id: 1685, + id: '1685', from: 'MAH', to: 'VLC', attributes: { prediction: 1238 }, }, { - id: 1686, + id: '1686', from: 'VLL', to: 'VLC', attributes: { prediction: 335 }, }, { - id: 1687, + id: '1687', from: 'PTY', to: 'VLN', attributes: { prediction: 1963 }, }, { - id: 1688, + id: '1688', from: 'VKO', to: 'VVO', attributes: { prediction: 9273 }, }, { - id: 1689, + id: '1689', from: 'SZZ', to: 'WAW', attributes: { prediction: 3940 }, }, { - id: 1690, + id: '1690', from: 'DTW', to: 'YHZ', attributes: { prediction: 1812 }, }, { - id: 1691, + id: '1691', from: 'YTZ', to: 'YOW', attributes: { prediction: 24641 }, }, { - id: 1692, + id: '1692', from: 'BHX', to: 'ABZ', attributes: { prediction: 7653 }, }, { - id: 1693, + id: '1693', from: 'LHR', to: 'ACC', attributes: { prediction: 4294 }, }, { - id: 1694, + id: '1694', from: 'OVD', to: 'ACE', attributes: { prediction: 1611 }, }, { - id: 1695, + id: '1695', from: 'SVI', to: 'ACR', attributes: { prediction: 160 }, }, { - id: 1696, + id: '1696', from: 'BOS', to: 'ACY', attributes: { prediction: 6701 }, }, { - id: 1697, + id: '1697', from: 'AMS', to: 'ADB', attributes: { prediction: 5023 }, }, { - id: 1698, + id: '1698', from: 'AKL', to: 'ADL', attributes: { prediction: 2273 }, }, { - id: 1699, + id: '1699', from: 'CBR', to: 'ADL', attributes: { prediction: 9104 }, }, { - id: 1700, + id: '1700', from: 'AFA', to: 'AEP', attributes: { prediction: 1230 }, }, { - id: 1701, + id: '1701', from: 'LEN', to: 'AGP', attributes: { prediction: 47 }, }, { - id: 1702, + id: '1702', from: 'TRD', to: 'AGP', attributes: { prediction: 693 }, }, { - id: 1703, + id: '1703', from: 'UIP', to: 'AJA', attributes: { prediction: 307 }, }, { - id: 1704, + id: '1704', from: 'LYC', to: 'AJR', attributes: { prediction: 1468 }, }, { - id: 1705, + id: '1705', from: 'NAN', to: 'AKL', attributes: { prediction: 16540 }, }, { - id: 1706, + id: '1706', from: 'BRU', to: 'ABJ', attributes: { prediction: 2037 }, }, { - id: 1707, + id: '1707', from: 'YXC', to: 'YYC', attributes: { prediction: 1209 }, }, { - id: 1708, + id: '1708', from: 'YYR', to: 'YYT', attributes: { prediction: 1367 }, }, { - id: 1709, + id: '1709', from: 'ATL', to: 'YYZ', attributes: { prediction: 16899 }, }, { - id: 1710, + id: '1710', from: 'YNA', to: 'YZV', attributes: { prediction: 225 }, }, { - id: 1711, + id: '1711', from: 'SJJ', to: 'ZAG', attributes: { prediction: 3232 }, }, { - id: 1712, + id: '1712', from: 'MTV', to: 'ZGU', attributes: { prediction: 54 }, }, { - id: 1713, + id: '1713', from: 'CGN', to: 'ZRH', attributes: { prediction: 10039 }, }, { - id: 1714, + id: '1714', from: 'PRG', to: 'ZRH', attributes: { prediction: 14025 }, }, { - id: 1715, + id: '1715', from: 'TLV', to: 'ZRH', attributes: { prediction: 16857 }, }, { - id: 1716, + id: '1716', from: 'TXL', to: 'ZRH', attributes: { prediction: 37000 }, }, { - id: 1717, + id: '1717', from: 'MEX', to: 'ACA', attributes: { prediction: 15450 }, }, { - id: 1718, + id: '1718', from: 'DUS', to: 'ACE', attributes: { prediction: 4448 }, }, { - id: 1719, + id: '1719', from: 'SXF', to: 'ADB', attributes: { prediction: 5171 }, }, { - id: 1720, + id: '1720', from: 'MRS', to: 'AGA', attributes: { prediction: 1561 }, }, { - id: 1721, + id: '1721', from: 'REC', to: 'AJU', attributes: { prediction: 2792 }, }, { - id: 1722, + id: '1722', from: 'SNW', to: 'AKY', attributes: { prediction: 19 }, }, { - id: 1723, + id: '1723', from: 'FKB', to: 'ALC', attributes: { prediction: 2475 }, }, { - id: 1724, + id: '1724', from: 'FRA', to: 'ALC', attributes: { prediction: 2477 }, }, { - id: 1725, + id: '1725', from: 'LGW', to: 'ALC', attributes: { prediction: 34009 }, }, { - id: 1726, + id: '1726', from: 'MME', to: 'ALC', attributes: { prediction: 701 }, }, { - id: 1727, + id: '1727', from: 'MIA', to: 'BAQ', attributes: { prediction: 3381 }, }, { - id: 1728, + id: '1728', from: 'ORD', to: 'BDL', attributes: { prediction: 23886 }, }, { - id: 1729, + id: '1729', from: 'TGD', to: 'BEG', attributes: { prediction: 5710 }, }, { - id: 1730, + id: '1730', from: 'JNU', to: 'ANC', attributes: { prediction: 10542 }, }, { - id: 1731, + id: '1731', from: 'SDP', to: 'ANC', attributes: { prediction: 540 }, }, { - id: 1732, + id: '1732', from: 'CTS', to: 'AOJ', attributes: { prediction: 4888 }, }, { - id: 1733, + id: '1733', from: 'HFS', to: 'ARN', attributes: { prediction: 611 }, }, { - id: 1734, + id: '1734', from: 'KLR', to: 'ARN', attributes: { prediction: 6095 }, }, { - id: 1735, + id: '1735', from: 'PMO', to: 'ARN', attributes: { prediction: 500 }, }, { - id: 1736, + id: '1736', from: 'KRT', to: 'ASM', attributes: { prediction: 353 }, }, { - id: 1737, + id: '1737', from: 'LPB', to: 'ASU', attributes: { prediction: 1821 }, }, { - id: 1738, + id: '1738', from: 'LGW', to: 'ATH', attributes: { prediction: 8141 }, }, { - id: 1739, + id: '1739', from: 'BAH', to: 'AUH', attributes: { prediction: 33485 }, }, { - id: 1740, + id: '1740', from: 'MSQ', to: 'AUH', attributes: { prediction: 1020 }, }, { - id: 1741, + id: '1741', from: 'MUC', to: 'AUH', attributes: { prediction: 6524 }, }, { - id: 1742, + id: '1742', from: 'ELP', to: 'AUS', attributes: { prediction: 11999 }, }, { - id: 1743, + id: '1743', from: 'AKI', to: 'BET', attributes: { prediction: 62 }, }, { - id: 1744, + id: '1744', from: 'LBA', to: 'BHD', attributes: { prediction: 5436 }, }, { - id: 1745, + id: '1745', from: 'GHA', to: 'ALG', attributes: { prediction: 1188 }, }, { - id: 1746, + id: '1746', from: 'AGA', to: 'AMS', attributes: { prediction: 1538 }, }, { - id: 1747, + id: '1747', from: 'JFK', to: 'AUH', attributes: { prediction: 7238 }, }, { - id: 1748, + id: '1748', from: 'CLT', to: 'AVL', attributes: { prediction: 6470 }, }, { - id: 1749, + id: '1749', from: 'DXB', to: 'AWZ', attributes: { prediction: 1030 }, }, { - id: 1750, + id: '1750', from: 'NAS', to: 'AXP', attributes: { prediction: 228 }, }, { - id: 1751, + id: '1751', from: 'LLI', to: 'AXU', attributes: { prediction: 1506 }, }, { - id: 1752, + id: '1752', from: 'AMS', to: 'AYT', attributes: { prediction: 9471 }, }, { - id: 1753, + id: '1753', from: 'ESB', to: 'AYT', attributes: { prediction: 11384 }, }, { - id: 1754, + id: '1754', from: 'LHR', to: 'CMN', attributes: { prediction: 4304 }, }, { - id: 1755, + id: '1755', from: 'JFK', to: 'BUD', attributes: { prediction: 5367 }, }, { - id: 1756, + id: '1756', from: 'PZO', to: 'BLA', attributes: { prediction: 1179 }, }, { - id: 1757, + id: '1757', from: 'OSD', to: 'BMA', attributes: { prediction: 1139 }, }, { - id: 1758, + id: '1758', from: 'MIA', to: 'BNA', attributes: { prediction: 6469 }, }, { - id: 1759, + id: '1759', from: 'HVB', to: 'BNE', attributes: { prediction: 2643 }, }, { - id: 1760, + id: '1760', from: 'MAR', to: 'BOG', attributes: { prediction: 780 }, }, { - id: 1761, + id: '1761', from: 'AUS', to: 'BOS', attributes: { prediction: 2654 }, }, { - id: 1762, + id: '1762', from: 'LAS', to: 'BOS', attributes: { prediction: 8568 }, }, { - id: 1763, + id: '1763', from: 'TMP', to: 'BRE', attributes: { prediction: 3020 }, }, { - id: 1764, + id: '1764', from: 'TXL', to: 'BRI', attributes: { prediction: 1134 }, }, { - id: 1765, + id: '1765', from: 'ESB', to: 'BRU', attributes: { prediction: 2073 }, }, { - id: 1766, + id: '1766', from: 'PEK', to: 'BRU', attributes: { prediction: 5587 }, }, { - id: 1767, + id: '1767', from: 'PIZ', to: 'BRW', attributes: { prediction: 275 }, }, { - id: 1768, + id: '1768', from: 'FAI', to: 'BTI', attributes: { prediction: 337 }, }, { - id: 1769, + id: '1769', from: 'AHO', to: 'BVA', attributes: { prediction: 1524 }, }, { - id: 1770, + id: '1770', from: 'MAN', to: 'BVC', attributes: { prediction: 764 }, }, { - id: 1771, + id: '1771', from: 'BEU', to: 'BVI', attributes: { prediction: 127 }, }, { - id: 1772, + id: '1772', from: 'BWI', to: 'COS', attributes: { prediction: 137 }, }, { - id: 1773, + id: '1773', from: 'BNA', to: 'DFW', attributes: { prediction: 28305 }, }, { - id: 1774, + id: '1774', from: 'JAC', to: 'DFW', attributes: { prediction: 3384 }, }, { - id: 1775, + id: '1775', from: 'NCE', to: 'DJE', attributes: { prediction: 457 }, }, { - id: 1776, + id: '1776', from: 'BTK', to: 'DME', attributes: { prediction: 1664 }, }, { - id: 1777, + id: '1777', from: 'LIS', to: 'DME', attributes: { prediction: 3434 }, }, { - id: 1778, + id: '1778', from: 'AHB', to: 'DMM', attributes: { prediction: 2979 }, }, { - id: 1779, + id: '1779', from: 'CAN', to: 'DOH', attributes: { prediction: 5508 }, }, { - id: 1780, + id: '1780', from: 'DTW', to: 'CHO', attributes: { prediction: 1237 }, }, { - id: 1781, + id: '1781', from: 'BUF', to: 'CLT', attributes: { prediction: 13687 }, }, { - id: 1782, + id: '1782', from: 'SHJ', to: 'CMB', attributes: { prediction: 5233 }, }, { - id: 1783, + id: '1783', from: 'CGR', to: 'CMG', attributes: { prediction: 753 }, }, { - id: 1784, + id: '1784', from: 'RAK', to: 'CMN', attributes: { prediction: 13513 }, }, { - id: 1785, + id: '1785', from: 'TIP', to: 'CMN', attributes: { prediction: 4264 }, }, { - id: 1786, + id: '1786', from: 'PSA', to: 'CND', attributes: { prediction: 1701 }, }, { - id: 1787, + id: '1787', from: 'UDI', to: 'CNF', attributes: { prediction: 2397 }, }, { - id: 1788, + id: '1788', from: 'TIM', to: 'DPS', attributes: { prediction: 2485 }, }, { - id: 1789, + id: '1789', from: 'CFU', to: 'DRS', attributes: { prediction: 1468 }, }, { - id: 1790, + id: '1790', from: 'BCN', to: 'DTM', attributes: { prediction: 2132 }, }, { - id: 1791, + id: '1791', from: 'HGH', to: 'CSX', attributes: { prediction: 9834 }, }, { - id: 1792, + id: '1792', from: 'CUR', to: 'CTG', attributes: { prediction: 289 }, }, { - id: 1793, + id: '1793', from: 'XMN', to: 'CTU', attributes: { prediction: 7401 }, }, { - id: 1794, + id: '1794', from: 'GYE', to: 'CUE', attributes: { prediction: 5908 }, }, { - id: 1795, + id: '1795', from: 'CLJ', to: 'CUF', attributes: { prediction: 1954 }, }, { - id: 1796, + id: '1796', from: 'MEX', to: 'CUL', attributes: { prediction: 16187 }, }, { - id: 1797, + id: '1797', from: 'TLC', to: 'CUN', attributes: { prediction: 17845 }, }, { - id: 1798, + id: '1798', from: 'DAY', to: 'CVG', attributes: { prediction: 53 }, }, { - id: 1799, + id: '1799', from: 'YWH', to: 'CXH', attributes: { prediction: 1807 }, }, { - id: 1800, + id: '1800', from: 'BET', to: 'CYF', attributes: { prediction: 620 }, }, { - id: 1801, + id: '1801', from: 'TRI', to: 'DAY', attributes: { prediction: 47 }, }, { - id: 1802, + id: '1802', from: 'GRB', to: 'DEN', attributes: { prediction: 1004 }, }, { - id: 1803, + id: '1803', from: 'GRR', to: 'DEN', attributes: { prediction: 1915 }, }, { - id: 1804, + id: '1804', from: 'MEM', to: 'DEN', attributes: { prediction: 6799 }, }, { - id: 1805, + id: '1805', from: 'MSN', to: 'DTW', attributes: { prediction: 14830 }, }, { - id: 1806, + id: '1806', from: 'ORD', to: 'DTW', attributes: { prediction: 39367 }, }, { - id: 1807, + id: '1807', from: 'SFO', to: 'DTW', attributes: { prediction: 17960 }, }, { - id: 1808, + id: '1808', from: 'FKB', to: 'DUB', attributes: { prediction: 2189 }, }, { - id: 1809, + id: '1809', from: 'IBZ', to: 'DUB', attributes: { prediction: 1987 }, }, { - id: 1810, + id: '1810', from: 'JER', to: 'DUB', attributes: { prediction: 1935 }, }, { - id: 1811, + id: '1811', from: 'DRS', to: 'DUS', attributes: { prediction: 18533 }, }, { - id: 1812, + id: '1812', from: 'FRU', to: 'DME', attributes: { prediction: 1344 }, }, { - id: 1813, + id: '1813', from: 'FRA', to: 'DMM', attributes: { prediction: 633 }, }, { - id: 1814, + id: '1814', from: 'KTM', to: 'DMM', attributes: { prediction: 2011 }, }, { - id: 1815, + id: '1815', from: 'IAD', to: 'DOH', attributes: { prediction: 8733 }, }, { - id: 1816, + id: '1816', from: 'FRA', to: 'FLL', attributes: { prediction: 1977 }, }, { - id: 1817, + id: '1817', from: 'SJO', to: 'FLL', attributes: { prediction: 4195 }, }, { - id: 1818, + id: '1818', from: 'GVA', to: 'FLR', attributes: { prediction: 1105 }, }, { - id: 1819, + id: '1819', from: 'ASM', to: 'DXB', attributes: { prediction: 1160 }, }, { - id: 1820, + id: '1820', from: 'CDG', to: 'DXB', attributes: { prediction: 28635 }, }, { - id: 1821, + id: '1821', from: 'FRU', to: 'DXB', attributes: { prediction: 593 }, }, { - id: 1822, + id: '1822', from: 'OTP', to: 'DXB', attributes: { prediction: 1187 }, }, { - id: 1823, + id: '1823', from: 'ASR', to: 'ECN', attributes: { prediction: 901 }, }, { - id: 1824, + id: '1824', from: 'BOH', to: 'EDI', attributes: { prediction: 5670 }, }, { - id: 1825, + id: '1825', from: 'ORK', to: 'EMA', attributes: { prediction: 2301 }, }, { - id: 1826, + id: '1826', from: 'ANF', to: 'ESR', attributes: { prediction: 781 }, }, { - id: 1827, + id: '1827', from: 'EDI', to: 'EWR', attributes: { prediction: 8784 }, }, { - id: 1828, + id: '1828', from: 'LIT', to: 'EWR', attributes: { prediction: 1351 }, }, { - id: 1829, + id: '1829', from: 'SPU', to: 'FRA', attributes: { prediction: 3637 }, }, { - id: 1830, + id: '1830', from: 'WDH', to: 'FRA', attributes: { prediction: 7368 }, }, { - id: 1831, + id: '1831', from: 'PRG', to: 'FRL', attributes: { prediction: 1332 }, }, { - id: 1832, + id: '1832', from: 'WRO', to: 'DTM', attributes: { prediction: 2426 }, }, { - id: 1833, + id: '1833', from: 'MDT', to: 'DTW', attributes: { prediction: 5372 }, }, { - id: 1834, + id: '1834', from: 'MAD', to: 'HAV', attributes: { prediction: 14391 }, }, { - id: 1835, + id: '1835', from: 'FRA', to: 'HEL', attributes: { prediction: 22037 }, }, { - id: 1836, + id: '1836', from: 'ATH', to: 'HER', attributes: { prediction: 53075 }, }, { - id: 1837, + id: '1837', from: 'PRG', to: 'HER', attributes: { prediction: 5028 }, }, { - id: 1838, + id: '1838', from: 'MIA', to: 'GHB', attributes: { prediction: 509 }, }, { - id: 1839, + id: '1839', from: 'TRE', to: 'GLA', attributes: { prediction: 504 }, }, { - id: 1840, + id: '1840', from: 'LPL', to: 'GNB', attributes: { prediction: 706 }, }, { - id: 1841, + id: '1841', from: 'CLE', to: 'GRB', attributes: { prediction: 2424 }, }, { - id: 1842, + id: '1842', from: 'LPL', to: 'GRO', attributes: { prediction: 2858 }, }, { - id: 1843, + id: '1843', from: 'MKE', to: 'GRR', attributes: { prediction: 3330 }, }, { - id: 1844, + id: '1844', from: 'CDG', to: 'GVA', attributes: { prediction: 37883 }, }, { - id: 1845, + id: '1845', from: 'DUB', to: 'GVA', attributes: { prediction: 4724 }, }, { - id: 1846, + id: '1846', from: 'HIR', to: 'GZO', attributes: { prediction: 845 }, }, { - id: 1847, + id: '1847', from: 'STN', to: 'GZT', attributes: { prediction: 1274 }, }, { - id: 1848, + id: '1848', from: 'TOS', to: 'HAA', attributes: { prediction: 552 }, }, { - id: 1849, + id: '1849', from: 'CAN', to: 'HAK', attributes: { prediction: 22209 }, }, { - id: 1850, + id: '1850', from: 'CTU', to: 'HAK', attributes: { prediction: 5808 }, }, { - id: 1851, + id: '1851', from: 'LGW', to: 'HAM', attributes: { prediction: 6849 }, }, { - id: 1852, + id: '1852', from: 'MAH', to: 'HAM', attributes: { prediction: 660 }, }, { - id: 1853, + id: '1853', from: 'NUE', to: 'HAM', attributes: { prediction: 18349 }, }, { - id: 1854, + id: '1854', from: 'BGO', to: 'HAU', attributes: { prediction: 997 }, }, { - id: 1855, + id: '1855', from: 'HAK', to: 'HGH', attributes: { prediction: 3166 }, }, { - id: 1856, + id: '1856', from: 'SOF', to: 'HHN', attributes: { prediction: 2218 }, }, { - id: 1857, + id: '1857', from: 'CGO', to: 'HKG', attributes: { prediction: 1694 }, }, { - id: 1858, + id: '1858', from: 'GUM', to: 'HKG', attributes: { prediction: 810 }, }, { - id: 1859, + id: '1859', from: 'JFK', to: 'DUB', attributes: { prediction: 24051 }, }, { - id: 1860, + id: '1860', from: 'ACE', to: 'DUS', attributes: { prediction: 3405 }, }, { - id: 1861, + id: '1861', from: 'VVI', to: 'EZE', attributes: { prediction: 6533 }, }, { - id: 1862, + id: '1862', from: 'TIJ', to: 'LAP', attributes: { prediction: 5124 }, }, { - id: 1863, + id: '1863', from: 'LIT', to: 'LAS', attributes: { prediction: 3333 }, }, { - id: 1864, + id: '1864', from: 'FRA', to: 'LAX', attributes: { prediction: 17561 }, }, { - id: 1865, + id: '1865', from: 'XNA', to: 'LAX', attributes: { prediction: 1131 }, }, { - id: 1866, + id: '1866', from: 'DAM', to: 'LCA', attributes: { prediction: 1507 }, }, { - id: 1867, + id: '1867', from: 'BKK', to: 'ICN', attributes: { prediction: 49214 }, }, { - id: 1868, + id: '1868', from: 'KKJ', to: 'ICN', attributes: { prediction: 2078 }, }, { - id: 1869, + id: '1869', from: 'MAD', to: 'ICN', attributes: { prediction: 3227 }, }, { - id: 1870, + id: '1870', from: 'TLV', to: 'ICN', attributes: { prediction: 3361 }, }, { - id: 1871, + id: '1871', from: 'IAD', to: 'ICT', attributes: { prediction: 141 }, }, { - id: 1872, + id: '1872', from: 'OMA', to: 'ICT', attributes: { prediction: 116 }, }, { - id: 1873, + id: '1873', from: 'ANC', to: 'ILI', attributes: { prediction: 409 }, }, { - id: 1874, + id: '1874', from: 'KNK', to: 'ILI', attributes: { prediction: 59 }, }, { - id: 1875, + id: '1875', from: 'UET', to: 'ISB', attributes: { prediction: 3932 }, }, { - id: 1876, + id: '1876', from: 'NAV', to: 'IST', attributes: { prediction: 7381 }, }, { - id: 1877, + id: '1877', from: 'HOU', to: 'JAN', attributes: { prediction: 8787 }, }, { - id: 1878, + id: '1878', from: 'CFC', to: 'JCB', attributes: { prediction: 396 }, }, { - id: 1879, + id: '1879', from: 'TUN', to: 'JED', attributes: { prediction: 3057 }, }, { - id: 1880, + id: '1880', from: 'MAN', to: 'JER', attributes: { prediction: 7579 }, }, { - id: 1881, + id: '1881', from: 'BDA', to: 'JFK', attributes: { prediction: 8595 }, }, { - id: 1882, + id: '1882', from: 'FEG', to: 'LED', attributes: { prediction: 736 }, }, { - id: 1883, + id: '1883', from: 'TRN', to: 'LUX', attributes: { prediction: 927 }, }, { - id: 1884, + id: '1884', from: 'KOE', to: 'LWE', attributes: { prediction: 598 }, }, { - id: 1885, + id: '1885', from: 'MAD', to: 'LYS', attributes: { prediction: 10718 }, }, { - id: 1886, + id: '1886', from: 'CMB', to: 'MAA', attributes: { prediction: 25877 }, }, { - id: 1887, + id: '1887', from: 'EAS', to: 'MAD', attributes: { prediction: 11635 }, }, { - id: 1888, + id: '1888', from: 'SDR', to: 'MAD', attributes: { prediction: 21012 }, }, { - id: 1889, + id: '1889', from: 'TXL', to: 'MAD', attributes: { prediction: 10314 }, }, { - id: 1890, + id: '1890', from: 'ABQ', to: 'MAF', attributes: { prediction: 2609 }, }, { - id: 1891, + id: '1891', from: 'WAT', to: 'LTN', attributes: { prediction: 3754 }, }, { - id: 1892, + id: '1892', from: 'FUE', to: 'LUX', attributes: { prediction: 552 }, }, { - id: 1893, + id: '1893', from: 'LEI', to: 'LUX', attributes: { prediction: 196 }, }, { - id: 1894, + id: '1894', from: 'WRL', to: 'LWT', attributes: { prediction: 418 }, }, { - id: 1895, + id: '1895', from: 'MCT', to: 'MAA', attributes: { prediction: 7102 }, }, { - id: 1896, + id: '1896', from: 'EMK', to: 'KSM', attributes: { prediction: 334 }, }, { - id: 1897, + id: '1897', from: 'BOM', to: 'KTM', attributes: { prediction: 1862 }, }, { - id: 1898, + id: '1898', from: 'MLE', to: 'KUL', attributes: { prediction: 2798 }, }, { - id: 1899, + id: '1899', from: 'CVG', to: 'LAS', attributes: { prediction: 6576 }, }, { - id: 1900, + id: '1900', from: 'DTW', to: 'LAS', attributes: { prediction: 32964 }, }, { - id: 1901, + id: '1901', from: 'PSP', to: 'LAS', attributes: { prediction: 1987 }, }, { - id: 1902, + id: '1902', from: 'BRU', to: 'LBA', attributes: { prediction: 1784 }, }, { - id: 1903, + id: '1903', from: 'GDN', to: 'LBC', attributes: { prediction: 3360 }, }, { - id: 1904, + id: '1904', from: 'URC', to: 'LBD', attributes: { prediction: 615 }, }, { - id: 1905, + id: '1905', from: 'GRX', to: 'MAD', attributes: { prediction: 18097 }, }, { - id: 1906, + id: '1906', from: 'DLA', to: 'LFW', attributes: { prediction: 1638 }, }, { - id: 1907, + id: '1907', from: 'ORF', to: 'LGA', attributes: { prediction: 7860 }, }, { - id: 1908, + id: '1908', from: 'LAX', to: 'LGB', attributes: { prediction: 10 }, }, { - id: 1909, + id: '1909', from: 'PMI', to: 'LIL', attributes: { prediction: 501 }, }, { - id: 1910, + id: '1910', from: 'DXB', to: 'LOS', attributes: { prediction: 21345 }, }, { - id: 1911, + id: '1911', from: 'BGY', to: 'LPL', attributes: { prediction: 2821 }, }, { - id: 1912, + id: '1912', from: 'SPC', to: 'MAN', attributes: { prediction: 709 }, }, { - id: 1913, + id: '1913', from: 'JFK', to: 'MCI', attributes: { prediction: 2034 }, }, { - id: 1914, + id: '1914', from: 'BKG', to: 'MCO', attributes: { prediction: 343 }, }, { - id: 1915, + id: '1915', from: 'EYW', to: 'MCO', attributes: { prediction: 1733 }, }, { - id: 1916, + id: '1916', from: 'BOB', to: 'MAU', attributes: { prediction: 81 }, }, { - id: 1917, + id: '1917', from: 'BOI', to: 'MSP', attributes: { prediction: 5531 }, }, { - id: 1918, + id: '1918', from: 'OKC', to: 'MSP', attributes: { prediction: 3812 }, }, { - id: 1919, + id: '1919', from: 'IAH', to: 'MSY', attributes: { prediction: 38380 }, }, { - id: 1920, + id: '1920', from: 'PKU', to: 'MES', attributes: { prediction: 4478 }, }, { - id: 1921, + id: '1921', from: 'VER', to: 'MEX', attributes: { prediction: 20266 }, }, { - id: 1922, + id: '1922', from: 'YYZ', to: 'MEX', attributes: { prediction: 10107 }, }, { - id: 1923, + id: '1923', from: 'GND', to: 'MIA', attributes: { prediction: 1212 }, }, { - id: 1924, + id: '1924', from: 'VKO', to: 'MJZ', attributes: { prediction: 1312 }, }, { - id: 1925, + id: '1925', from: 'CDG', to: 'MLA', attributes: { prediction: 3496 }, }, { - id: 1926, + id: '1926', from: 'EDI', to: 'MLA', attributes: { prediction: 1548 }, }, { - id: 1927, + id: '1927', from: 'DFW', to: 'MLI', attributes: { prediction: 2339 }, }, { - id: 1928, + id: '1928', from: 'MUC', to: 'MRS', attributes: { prediction: 9292 }, }, { - id: 1929, + id: '1929', from: 'LAX', to: 'MRY', attributes: { prediction: 7067 }, }, { - id: 1930, + id: '1930', from: 'DEA', to: 'MUX', attributes: { prediction: 190 }, }, { - id: 1931, + id: '1931', from: 'JMK', to: 'MXP', attributes: { prediction: 4272 }, }, { - id: 1932, + id: '1932', from: 'RIX', to: 'MXP', attributes: { prediction: 4444 }, }, { - id: 1933, + id: '1933', from: 'VLC', to: 'MXP', attributes: { prediction: 2529 }, }, { - id: 1934, + id: '1934', from: 'HYD', to: 'NAG', attributes: { prediction: 966 }, }, { - id: 1935, + id: '1935', from: 'IAD', to: 'MCO', attributes: { prediction: 38122 }, }, { - id: 1936, + id: '1936', from: 'HYD', to: 'MCT', attributes: { prediction: 5591 }, }, { - id: 1937, + id: '1937', from: 'ORB', to: 'MMX', attributes: { prediction: 76 }, }, { - id: 1938, + id: '1938', from: 'TUG', to: 'MNL', attributes: { prediction: 4319 }, }, { - id: 1939, + id: '1939', from: 'VNX', to: 'MPM', attributes: { prediction: 231 }, }, { - id: 1940, + id: '1940', from: 'AXU', to: 'MQX', attributes: { prediction: 178 }, }, { - id: 1941, + id: '1941', from: 'FLL', to: 'PTY', attributes: { prediction: 1496 }, }, { - id: 1942, + id: '1942', from: 'SVQ', to: 'ORY', attributes: { prediction: 13458 }, }, { - id: 1943, + id: '1943', from: 'PUS', to: 'NGO', attributes: { prediction: 3430 }, }, { - id: 1944, + id: '1944', from: 'GDN', to: 'NRN', attributes: { prediction: 2437 }, }, { - id: 1945, + id: '1945', from: 'MXP', to: 'NUE', attributes: { prediction: 3242 }, }, { - id: 1946, + id: '1946', from: 'GCI', to: 'NWI', attributes: { prediction: 284 }, }, { - id: 1947, + id: '1947', from: 'FMM', to: 'NYO', attributes: { prediction: 3026 }, }, { - id: 1948, + id: '1948', from: 'IAH', to: 'OKC', attributes: { prediction: 12792 }, }, { - id: 1949, + id: '1949', from: 'TNC', to: 'OME', attributes: { prediction: 152 }, }, { - id: 1950, + id: '1950', from: 'BOD', to: 'OPO', attributes: { prediction: 2099 }, }, { - id: 1951, + id: '1951', from: 'ABQ', to: 'ORD', attributes: { prediction: 9524 }, }, { - id: 1952, + id: '1952', from: 'FLL', to: 'ORD', attributes: { prediction: 16675 }, }, { - id: 1953, + id: '1953', from: 'ZRH', to: 'ORD', attributes: { prediction: 5760 }, }, { - id: 1954, + id: '1954', from: 'IAH', to: 'ORF', attributes: { prediction: 2770 }, }, { - id: 1955, + id: '1955', from: 'PSA', to: 'OSL', attributes: { prediction: 1749 }, }, { - id: 1956, + id: '1956', from: 'TOY', to: 'PVG', attributes: { prediction: 996 }, }, { - id: 1957, + id: '1957', from: 'HNL', to: 'SFO', attributes: { prediction: 43885 }, }, { - id: 1958, + id: '1958', from: 'LIH', to: 'SFO', attributes: { prediction: 7815 }, }, { - id: 1959, + id: '1959', from: 'AKL', to: 'SIN', attributes: { prediction: 10983 }, }, { - id: 1960, + id: '1960', from: 'CMB', to: 'SIN', attributes: { prediction: 21683 }, }, { - id: 1961, + id: '1961', from: 'MEL', to: 'SIN', attributes: { prediction: 41472 }, }, { - id: 1962, + id: '1962', from: 'MCO', to: 'PNS', attributes: { prediction: 1086 }, }, { - id: 1963, + id: '1963', from: 'KPB', to: 'PPV', attributes: { prediction: 0 }, }, { - id: 1964, + id: '1964', from: 'RHO', to: 'PRG', attributes: { prediction: 4173 }, }, { - id: 1965, + id: '1965', from: 'TIA', to: 'PSR', attributes: { prediction: 1830 }, }, { - id: 1966, + id: '1966', from: 'EZE', to: 'PUJ', attributes: { prediction: 753 }, }, { - id: 1967, + id: '1967', from: 'BWI', to: 'PVD', attributes: { prediction: 30951 }, }, { - id: 1968, + id: '1968', from: 'ZRH', to: 'PVG', attributes: { prediction: 5144 }, }, { - id: 1969, + id: '1969', from: 'GDL', to: 'SJD', attributes: { prediction: 6516 }, }, { - id: 1970, + id: '1970', from: 'DOM', to: 'SJU', attributes: { prediction: 2657 }, }, { - id: 1971, + id: '1971', from: 'CZS', to: 'RBR', attributes: { prediction: 2219 }, }, { - id: 1972, + id: '1972', from: 'WLH', to: 'RCL', attributes: { prediction: 95 }, }, { - id: 1973, + id: '1973', from: 'SFO', to: 'RDD', attributes: { prediction: 2358 }, }, { - id: 1974, + id: '1974', from: 'SZB', to: 'RDN', attributes: { prediction: 2665 }, }, { - id: 1975, + id: '1975', from: 'LAS', to: 'RDU', attributes: { prediction: 3866 }, }, { - id: 1976, + id: '1976', from: 'BSB', to: 'REC', attributes: { prediction: 29232 }, }, { - id: 1977, + id: '1977', from: 'KGD', to: 'RIX', attributes: { prediction: 2004 }, }, { - id: 1978, + id: '1978', from: 'BWI', to: 'ROC', attributes: { prediction: 5534 }, }, { - id: 1979, + id: '1979', from: 'SYD', to: 'ROT', attributes: { prediction: 887 }, }, { - id: 1980, + id: '1980', from: 'FNT', to: 'RSW', attributes: { prediction: 3234 }, }, { - id: 1981, + id: '1981', from: 'HKG', to: 'RUH', attributes: { prediction: 5937 }, }, { - id: 1982, + id: '1982', from: 'NYO', to: 'RYG', attributes: { prediction: 10048 }, }, { - id: 1983, + id: '1983', from: 'TSF', to: 'RYG', attributes: { prediction: 2457 }, }, { - id: 1984, + id: '1984', from: 'HOD', to: 'SAH', attributes: { prediction: 2786 }, }, { - id: 1985, + id: '1985', from: 'AUS', to: 'SAN', attributes: { prediction: 6560 }, }, { - id: 1986, + id: '1986', from: 'HPB', to: 'SCM', attributes: { prediction: 128 }, }, { - id: 1987, + id: '1987', from: 'EVN', to: 'SCO', attributes: { prediction: 440 }, }, { - id: 1988, + id: '1988', from: 'MUC', to: 'SKG', attributes: { prediction: 12690 }, }, { - id: 1989, + id: '1989', from: 'ANC', to: 'SNP', attributes: { prediction: 486 }, }, { - id: 1990, + id: '1990', from: 'LAS', to: 'SGF', attributes: { prediction: 2731 }, }, { - id: 1991, + id: '1991', from: 'XIY', to: 'SHE', attributes: { prediction: 4194 }, }, { - id: 1992, + id: '1992', from: 'CCU', to: 'SIN', attributes: { prediction: 4105 }, }, { - id: 1993, + id: '1993', from: 'HET', to: 'SZX', attributes: { prediction: 1500 }, }, { - id: 1994, + id: '1994', from: 'TZX', to: 'TBS', attributes: { prediction: 740 }, }, { - id: 1995, + id: '1995', from: 'VLI', to: 'TGH', attributes: { prediction: 72 }, }, { - id: 1996, + id: '1996', from: 'MAN', to: 'SOF', attributes: { prediction: 1329 }, }, { - id: 1997, + id: '1997', from: 'VIE', to: 'SPU', attributes: { prediction: 3706 }, }, { - id: 1998, + id: '1998', from: 'PEG', to: 'STN', attributes: { prediction: 2143 }, }, { - id: 1999, + id: '1999', from: 'BGO', to: 'SXF', attributes: { prediction: 1079 }, }, { - id: 2000, + id: '2000', from: 'KIN', to: 'SXM', attributes: { prediction: 2002 }, }, { - id: 2001, + id: '2001', from: 'SKB', to: 'SXM', attributes: { prediction: 2440 }, }, { - id: 2002, + id: '2002', from: 'BRU', to: 'TLS', attributes: { prediction: 6064 }, }, { - id: 2003, + id: '2003', from: 'CAN', to: 'TNA', attributes: { prediction: 10335 }, }, { - id: 2004, + id: '2004', from: 'ACY', to: 'TPA', attributes: { prediction: 3434 }, }, { - id: 2005, + id: '2005', from: 'CSX', to: 'TPE', attributes: { prediction: 2927 }, }, { - id: 2006, + id: '2006', from: 'PJM', to: 'SJO', attributes: { prediction: 1116 }, }, { - id: 2007, + id: '2007', from: 'CTU', to: 'SJW', attributes: { prediction: 4952 }, }, { - id: 2008, + id: '2008', from: 'NKG', to: 'SJW', attributes: { prediction: 388 }, }, { - id: 2009, + id: '2009', from: 'OVD', to: 'VLC', attributes: { prediction: 1639 }, }, { - id: 2010, + id: '2010', from: 'MQP', to: 'VNX', attributes: { prediction: 557 }, }, { - id: 2011, + id: '2011', from: 'XIY', to: 'TYN', attributes: { prediction: 4993 }, }, { - id: 2012, + id: '2012', from: 'CPH', to: 'UAK', attributes: { prediction: 741 }, }, { - id: 2013, + id: '2013', from: 'CNF', to: 'UDI', attributes: { prediction: 2518 }, }, { - id: 2014, + id: '2014', from: 'GWD', to: 'UET', attributes: { prediction: 192 }, }, { - id: 2015, + id: '2015', from: 'PBJ', to: 'ULB', attributes: { prediction: 9 }, }, { - id: 2016, + id: '2016', from: 'KDI', to: 'UPG', attributes: { prediction: 12569 }, }, { - id: 2017, + id: '2017', from: 'NRK', to: 'VBY', attributes: { prediction: 798 }, }, { - id: 2018, + id: '2018', from: 'RMI', to: 'VIE', attributes: { prediction: 1120 }, }, { - id: 2019, + id: '2019', from: 'BTK', to: 'VKO', attributes: { prediction: 1341 }, }, { - id: 2020, + id: '2020', from: 'WAG', to: 'WLG', attributes: { prediction: 257 }, }, { - id: 2021, + id: '2021', from: 'YGK', to: 'YYZ', attributes: { prediction: 3502 }, }, { - id: 2022, + id: '2022', from: 'LIS', to: 'ZAG', attributes: { prediction: 1702 }, }, { - id: 2023, + id: '2023', from: 'LAX', to: 'ZCL', attributes: { prediction: 3387 }, }, { - id: 2024, + id: '2024', from: 'ZTB', to: 'ZLT', attributes: { prediction: 102 }, }, { - id: 2025, + id: '2025', from: 'AKL', to: 'ZQN', attributes: { prediction: 20501 }, }, { - id: 2026, + id: '2026', from: 'DTW', to: 'APN', attributes: { prediction: 1517 }, }, { - id: 2027, + id: '2027', from: 'YYZ', to: 'YQG', attributes: { prediction: 4966 }, }, { - id: 2028, + id: '2028', from: 'YQD', to: 'YWG', attributes: { prediction: 1332 }, }, { - id: 2029, + id: '2029', from: 'YVR', to: 'YXY', attributes: { prediction: 11588 }, }, { - id: 2030, + id: '2030', from: 'YYJ', to: 'YYZ', attributes: { prediction: 9649 }, }, { - id: 2031, + id: '2031', from: 'ATL', to: 'ZRH', attributes: { prediction: 5593 }, }, { - id: 2032, + id: '2032', from: 'JFK', to: 'ZRH', attributes: { prediction: 21969 }, }, { - id: 2033, + id: '2033', from: 'MIA', to: 'ZRH', attributes: { prediction: 5615 }, }, { - id: 2034, + id: '2034', from: 'MJT', to: 'ATH', attributes: { prediction: 16124 }, }, { - id: 2035, + id: '2035', from: 'MLA', to: 'ATH', attributes: { prediction: 1084 }, }, { - id: 2036, + id: '2036', from: 'MYR', to: 'ATL', attributes: { prediction: 16946 }, }, { - id: 2037, + id: '2037', from: 'PUJ', to: 'ATL', attributes: { prediction: 3797 }, }, { - id: 2038, + id: '2038', from: 'BOG', to: 'AUC', attributes: { prediction: 2561 }, }, { - id: 2039, + id: '2039', from: 'DAC', to: 'AUH', attributes: { prediction: 9076 }, }, { - id: 2040, + id: '2040', from: 'SIN', to: 'AUH', attributes: { prediction: 9835 }, }, { - id: 2041, + id: '2041', from: 'FRA', to: 'BLQ', attributes: { prediction: 12169 }, }, { - id: 2042, + id: '2042', from: 'IXE', to: 'BLR', attributes: { prediction: 5440 }, }, { - id: 2043, + id: '2043', from: 'DAD', to: 'BMV', attributes: { prediction: 678 }, }, { - id: 2044, + id: '2044', from: 'KUL', to: 'BNE', attributes: { prediction: 2293 }, }, { - id: 2045, + id: '2045', from: 'RVK', to: 'BNN', attributes: { prediction: 486 }, }, { - id: 2046, + id: '2046', from: 'DTW', to: 'AMS', attributes: { prediction: 30707 }, }, { - id: 2047, + id: '2047', from: 'SNP', to: 'ANC', attributes: { prediction: 337 }, }, { - id: 2048, + id: '2048', from: 'CPO', to: 'ANF', attributes: { prediction: 304 }, }, { - id: 2049, + id: '2049', from: 'JFK', to: 'ANU', attributes: { prediction: 2380 }, }, { - id: 2050, + id: '2050', from: 'RHO', to: 'AOK', attributes: { prediction: 2809 }, }, { - id: 2051, + id: '2051', from: 'LYC', to: 'ARN', attributes: { prediction: 2793 }, }, { - id: 2052, + id: '2052', from: 'WAW', to: 'ARN', attributes: { prediction: 4265 }, }, { - id: 2053, + id: '2053', from: 'LRS', to: 'ATH', attributes: { prediction: 952 }, }, { - id: 2054, + id: '2054', from: 'PHL', to: 'ATH', attributes: { prediction: 5024 }, }, { - id: 2055, + id: '2055', from: 'BOG', to: 'ATL', attributes: { prediction: 5099 }, }, { - id: 2056, + id: '2056', from: 'BUF', to: 'ATL', attributes: { prediction: 29953 }, }, { - id: 2057, + id: '2057', from: 'GSO', to: 'ATL', attributes: { prediction: 13038 }, }, { - id: 2058, + id: '2058', from: 'VPS', to: 'ATL', attributes: { prediction: 14139 }, }, { - id: 2059, + id: '2059', from: 'CLT', to: 'AUA', attributes: { prediction: 6063 }, }, { - id: 2060, + id: '2060', from: 'VLN', to: 'AUA', attributes: { prediction: 1220 }, }, { - id: 2061, + id: '2061', from: 'CDG', to: 'AUH', attributes: { prediction: 14701 }, }, { - id: 2062, + id: '2062', from: 'DAC', to: 'AUH', attributes: { prediction: 9968 }, }, { - id: 2063, + id: '2063', from: 'VUP', to: 'BOG', attributes: { prediction: 6729 }, }, { - id: 2064, + id: '2064', from: 'SMF', to: 'BOI', attributes: { prediction: 1608 }, }, { - id: 2065, + id: '2065', from: 'SAH', to: 'BAH', attributes: { prediction: 5791 }, }, { - id: 2066, + id: '2066', from: 'MRV', to: 'BAX', attributes: { prediction: 820 }, }, { - id: 2067, + id: '2067', from: 'BFS', to: 'BCN', attributes: { prediction: 4790 }, }, { - id: 2068, + id: '2068', from: 'OTP', to: 'BEG', attributes: { prediction: 554 }, }, { - id: 2069, + id: '2069', from: 'CAI', to: 'BEN', attributes: { prediction: 3931 }, }, { - id: 2070, + id: '2070', from: 'FCO', to: 'BGO', attributes: { prediction: 1411 }, }, { - id: 2071, + id: '2071', from: 'BDS', to: 'BGY', attributes: { prediction: 5619 }, }, { - id: 2072, + id: '2072', from: 'KTM', to: 'BHR', attributes: { prediction: 310 }, }, { - id: 2073, + id: '2073', from: 'LXR', to: 'BHX', attributes: { prediction: 806 }, }, { - id: 2074, + id: '2074', from: 'SDY', to: 'BIL', attributes: { prediction: 427 }, }, { - id: 2075, + id: '2075', from: 'ORY', to: 'BIQ', attributes: { prediction: 27474 }, }, { - id: 2076, + id: '2076', from: 'ACC', to: 'BJL', attributes: { prediction: 3053 }, }, { - id: 2077, + id: '2077', from: 'LAX', to: 'BJX', attributes: { prediction: 3247 }, }, { - id: 2078, + id: '2078', from: 'MIR', to: 'BRS', attributes: { prediction: 589 }, }, { - id: 2079, + id: '2079', from: 'BBU', to: 'BRU', attributes: { prediction: 2894 }, }, { - id: 2080, + id: '2080', from: 'CAI', to: 'BRU', attributes: { prediction: 3414 }, }, { - id: 2081, + id: '2081', from: 'WST', to: 'BID', attributes: { prediction: 304 }, }, { - id: 2082, + id: '2082', from: 'LHR', to: 'BIO', attributes: { prediction: 4534 }, }, { - id: 2083, + id: '2083', from: 'DEN', to: 'BIS', attributes: { prediction: 4657 }, }, { - id: 2084, + id: '2084', from: 'PNK', to: 'CGK', attributes: { prediction: 20827 }, }, { - id: 2085, + id: '2085', from: 'CAI', to: 'CGN', attributes: { prediction: 1375 }, }, { - id: 2086, + id: '2086', from: 'VLC', to: 'CGN', attributes: { prediction: 3881 }, }, { - id: 2087, + id: '2087', from: 'TPE', to: 'CGO', attributes: { prediction: 2048 }, }, { - id: 2088, + id: '2088', from: 'CRL', to: 'CIA', attributes: { prediction: 16381 }, }, { - id: 2089, + id: '2089', from: 'WRO', to: 'BVA', attributes: { prediction: 1335 }, }, { - id: 2090, + id: '2090', from: 'MEM', to: 'BWI', attributes: { prediction: 5961 }, }, { - id: 2091, + id: '2091', from: 'EMA', to: 'BZG', attributes: { prediction: 1512 }, }, { - id: 2092, + id: '2092', from: 'HHN', to: 'CAG', attributes: { prediction: 1444 }, }, { - id: 2093, + id: '2093', from: 'BKK', to: 'CAN', attributes: { prediction: 35016 }, }, { - id: 2094, + id: '2094', from: 'NKG', to: 'CAN', attributes: { prediction: 25641 }, }, { - id: 2095, + id: '2095', from: 'FRA', to: 'CCS', attributes: { prediction: 8525 }, }, { - id: 2096, + id: '2096', from: 'DIB', to: 'CCU', attributes: { prediction: 5837 }, }, { - id: 2097, + id: '2097', from: 'NAG', to: 'CCU', attributes: { prediction: 3513 }, }, { - id: 2098, + id: '2098', from: 'DTW', to: 'CDG', attributes: { prediction: 7351 }, }, { - id: 2099, + id: '2099', from: 'HKG', to: 'CDG', attributes: { prediction: 29316 }, }, { - id: 2100, + id: '2100', from: 'KRK', to: 'CDG', attributes: { prediction: 4716 }, }, { - id: 2101, + id: '2101', from: 'KJA', to: 'CEK', attributes: { prediction: 656 }, }, { - id: 2102, + id: '2102', from: 'AVP', to: 'CLT', attributes: { prediction: 1982 }, }, { - id: 2103, + id: '2103', from: 'JED', to: 'CMB', attributes: { prediction: 3261 }, }, { - id: 2104, + id: '2104', from: 'ACK', to: 'DCA', attributes: { prediction: 1258 }, }, { - id: 2105, + id: '2105', from: 'KHI', to: 'DEL', attributes: { prediction: 383 }, }, { - id: 2106, + id: '2106', from: 'BNA', to: 'DEN', attributes: { prediction: 24716 }, }, { - id: 2107, + id: '2107', from: 'CLT', to: 'DEN', attributes: { prediction: 21670 }, }, { - id: 2108, + id: '2108', from: 'BGW', to: 'EBL', attributes: { prediction: 2293 }, }, { - id: 2109, + id: '2109', from: 'LIG', to: 'EDI', attributes: { prediction: 303 }, }, { - id: 2110, + id: '2110', from: 'EGN', to: 'ELF', attributes: { prediction: 372 }, }, { - id: 2111, + id: '2111', from: 'OME', to: 'ELI', attributes: { prediction: 157 }, }, { - id: 2112, + id: '2112', from: 'GRK', to: 'DFW', attributes: { prediction: 11361 }, }, { - id: 2113, + id: '2113', from: 'LHR', to: 'CPT', attributes: { prediction: 14966 }, }, { - id: 2114, + id: '2114', from: 'CTU', to: 'CSX', attributes: { prediction: 16785 }, }, { - id: 2115, + id: '2115', from: 'PER', to: 'CVQ', attributes: { prediction: 815 }, }, { - id: 2116, + id: '2116', from: 'GRU', to: 'CWB', attributes: { prediction: 55908 }, }, { - id: 2117, + id: '2117', from: 'CCU', to: 'DAC', attributes: { prediction: 10389 }, }, { - id: 2118, + id: '2118', from: 'BGW', to: 'DAM', attributes: { prediction: 3244 }, }, { - id: 2119, + id: '2119', from: 'PVD', to: 'DCA', attributes: { prediction: 9705 }, }, { - id: 2120, + id: '2120', from: 'ZRH', to: 'DEL', attributes: { prediction: 4441 }, }, { - id: 2121, + id: '2121', from: 'LBF', to: 'DEN', attributes: { prediction: 969 }, }, { - id: 2122, + id: '2122', from: 'MTY', to: 'DFW', attributes: { prediction: 13098 }, }, { - id: 2123, + id: '2123', from: 'TPA', to: 'DFW', attributes: { prediction: 27429 }, }, { - id: 2124, + id: '2124', from: 'YYZ', to: 'DFW', attributes: { prediction: 16162 }, }, { - id: 2125, + id: '2125', from: 'ESB', to: 'DIY', attributes: { prediction: 6590 }, }, { - id: 2126, + id: '2126', from: 'JNU', to: 'ELV', attributes: { prediction: 11 }, }, { - id: 2127, + id: '2127', from: 'BUD', to: 'EMA', attributes: { prediction: 1686 }, }, { - id: 2128, + id: '2128', from: 'TPA', to: 'EWR', attributes: { prediction: 26060 }, }, { - id: 2129, + id: '2129', from: 'CWL', to: 'GLA', attributes: { prediction: 3764 }, }, { - id: 2130, + id: '2130', from: 'LAS', to: 'GRI', attributes: { prediction: 1028 }, }, { - id: 2131, + id: '2131', from: 'GOT', to: 'DUS', attributes: { prediction: 2946 }, }, { - id: 2132, + id: '2132', from: 'NAP', to: 'DUS', attributes: { prediction: 1912 }, }, { - id: 2133, + id: '2133', from: 'BBO', to: 'DXB', attributes: { prediction: 451 }, }, { - id: 2134, + id: '2134', from: 'SFO', to: 'DXB', attributes: { prediction: 9233 }, }, { - id: 2135, + id: '2135', from: 'IST', to: 'DYU', attributes: { prediction: 1068 }, }, { - id: 2136, + id: '2136', from: 'HOU', to: 'ELP', attributes: { prediction: 8825 }, }, { - id: 2137, + id: '2137', from: 'STR', to: 'ESB', attributes: { prediction: 2094 }, }, { - id: 2138, + id: '2138', from: 'ATL', to: 'EWR', attributes: { prediction: 57613 }, }, { - id: 2139, + id: '2139', from: 'SFB', to: 'GSO', attributes: { prediction: 1350 }, }, { - id: 2140, + id: '2140', from: 'CTS', to: 'GUM', attributes: { prediction: 1070 }, }, { - id: 2141, + id: '2141', from: 'DBV', to: 'FCO', attributes: { prediction: 2176 }, }, { - id: 2142, + id: '2142', from: 'VCE', to: 'FCO', attributes: { prediction: 36910 }, }, { - id: 2143, + id: '2143', from: 'REC', to: 'FEN', attributes: { prediction: 5379 }, }, { - id: 2144, + id: '2144', from: 'CAP', to: 'FLL', attributes: { prediction: 96 }, }, { - id: 2145, + id: '2145', from: 'CTG', to: 'FLL', attributes: { prediction: 3617 }, }, { - id: 2146, + id: '2146', from: 'CLE', to: 'FNT', attributes: { prediction: 2386 }, }, { - id: 2147, + id: '2147', from: 'CMN', to: 'FRA', attributes: { prediction: 7091 }, }, { - id: 2148, + id: '2148', from: 'VRA', to: 'FRA', attributes: { prediction: 2914 }, }, { - id: 2149, + id: '2149', from: 'LGW', to: 'FRL', attributes: { prediction: 1108 }, }, { - id: 2150, + id: '2150', from: 'MSP', to: 'FSD', attributes: { prediction: 10049 }, }, { - id: 2151, + id: '2151', from: 'BCN', to: 'FUE', attributes: { prediction: 1518 }, }, { - id: 2152, + id: '2152', from: 'FMO', to: 'FUE', attributes: { prediction: 744 }, }, { - id: 2153, + id: '2153', from: 'CAI', to: 'GVA', attributes: { prediction: 4511 }, }, { - id: 2154, + id: '2154', from: 'CPH', to: 'HAJ', attributes: { prediction: 1616 }, }, { - id: 2155, + id: '2155', from: 'KSN', to: 'HAJ', attributes: { prediction: 556 }, }, { - id: 2156, + id: '2156', from: 'MAH', to: 'HAM', attributes: { prediction: 648 }, }, { - id: 2157, + id: '2157', from: 'TLS', to: 'HAM', attributes: { prediction: 2385 }, }, { - id: 2158, + id: '2158', from: 'BOG', to: 'EZE', attributes: { prediction: 5817 }, }, { - id: 2159, + id: '2159', from: 'SVG', to: 'FAE', attributes: { prediction: 468 }, }, { - id: 2160, + id: '2160', from: 'LEJ', to: 'FAO', attributes: { prediction: 883 }, }, { - id: 2161, + id: '2161', from: 'DRT', to: 'IAH', attributes: { prediction: 1451 }, }, { - id: 2162, + id: '2162', from: 'LRD', to: 'IAH', attributes: { prediction: 4196 }, }, { - id: 2163, + id: '2163', from: 'DPS', to: 'ICN', attributes: { prediction: 12479 }, }, { - id: 2164, + id: '2164', from: 'KHV', to: 'ICN', attributes: { prediction: 3215 }, }, { - id: 2165, + id: '2165', from: 'KJA', to: 'IKT', attributes: { prediction: 2336 }, }, { - id: 2166, + id: '2166', from: 'SDQ', to: 'HAV', attributes: { prediction: 1793 }, }, { - id: 2167, + id: '2167', from: 'BRE', to: 'HDF', attributes: { prediction: 250 }, }, { - id: 2168, + id: '2168', from: 'LGW', to: 'HEL', attributes: { prediction: 3132 }, }, { - id: 2169, + id: '2169', from: 'LGW', to: 'HER', attributes: { prediction: 8612 }, }, { - id: 2170, + id: '2170', from: 'WWK', to: 'HGU', attributes: { prediction: 185 }, }, { - id: 2171, + id: '2171', from: 'BGY', to: 'HHN', attributes: { prediction: 8737 }, }, { - id: 2172, + id: '2172', from: 'PSA', to: 'HHN', attributes: { prediction: 5133 }, }, { - id: 2173, + id: '2173', from: 'CEB', to: 'HKG', attributes: { prediction: 10151 }, }, { - id: 2174, + id: '2174', from: 'USM', to: 'HKG', attributes: { prediction: 2535 }, }, { - id: 2175, + id: '2175', from: 'YNZ', to: 'HKG', attributes: { prediction: 784 }, }, { - id: 2176, + id: '2176', from: 'KIX', to: 'HND', attributes: { prediction: 65345 }, }, { - id: 2177, + id: '2177', from: 'SNA', to: 'HNL', attributes: { prediction: 3318 }, }, { - id: 2178, + id: '2178', from: 'PPT', to: 'HOI', attributes: { prediction: 408 }, }, { - id: 2179, + id: '2179', from: 'RDU', to: 'HPN', attributes: { prediction: 185 }, }, { - id: 2180, + id: '2180', from: 'DXB', to: 'HYD', attributes: { prediction: 28894 }, }, { - id: 2181, + id: '2181', from: 'ALB', to: 'IAD', attributes: { prediction: 5402 }, }, { - id: 2182, + id: '2182', from: 'YUL', to: 'IAD', attributes: { prediction: 8024 }, }, { - id: 2183, + id: '2183', from: 'BWI', to: 'ISP', attributes: { prediction: 15890 }, }, { - id: 2184, + id: '2184', from: 'BGW', to: 'IST', attributes: { prediction: 5104 }, }, { - id: 2185, + id: '2185', from: 'ORD', to: 'JAX', attributes: { prediction: 11170 }, }, { - id: 2186, + id: '2186', from: 'BOM', to: 'JDH', attributes: { prediction: 1351 }, }, { - id: 2187, + id: '2187', from: 'DXB', to: 'KRT', attributes: { prediction: 13195 }, }, { - id: 2188, + id: '2188', from: 'IQT', to: 'LIM', attributes: { prediction: 16243 }, }, { - id: 2189, + id: '2189', from: 'FCO', to: 'KRK', attributes: { prediction: 766 }, }, { - id: 2190, + id: '2190', from: 'NRN', to: 'KRK', attributes: { prediction: 2646 }, }, { - id: 2191, + id: '2191', from: 'KWF', to: 'KTN', attributes: { prediction: 64 }, }, { - id: 2192, + id: '2192', from: 'FLL', to: 'JFK', attributes: { prediction: 39185 }, }, { - id: 2193, + id: '2193', from: 'JED', to: 'JFK', attributes: { prediction: 1188 }, }, { - id: 2194, + id: '2194', from: 'MUC', to: 'JFK', attributes: { prediction: 5848 }, }, { - id: 2195, + id: '2195', from: 'VIE', to: 'JMK', attributes: { prediction: 639 }, }, { - id: 2196, + id: '2196', from: 'AXD', to: 'JSH', attributes: { prediction: 618 }, }, { - id: 2197, + id: '2197', from: 'DUS', to: 'JSI', attributes: { prediction: 381 }, }, { - id: 2198, + id: '2198', from: 'TLV', to: 'KBP', attributes: { prediction: 8512 }, }, { - id: 2199, + id: '2199', from: 'KCG', to: 'KCL', attributes: { prediction: 3 }, }, { - id: 2200, + id: '2200', from: 'FRA', to: 'KEF', attributes: { prediction: 8262 }, }, { - id: 2201, + id: '2201', from: 'SEA', to: 'KEF', attributes: { prediction: 3865 }, }, { - id: 2202, + id: '2202', from: 'FRA', to: 'KIX', attributes: { prediction: 7707 }, }, { - id: 2203, + id: '2203', from: 'ARN', to: 'KLR', attributes: { prediction: 3004 }, }, { - id: 2204, + id: '2204', from: 'BSD', to: 'KMG', attributes: { prediction: 1434 }, }, { - id: 2205, + id: '2205', from: 'FUK', to: 'KMI', attributes: { prediction: 13973 }, }, { - id: 2206, + id: '2206', from: 'ITM', to: 'KMJ', attributes: { prediction: 21316 }, }, { - id: 2207, + id: '2207', from: 'HEL', to: 'KUO', attributes: { prediction: 6326 }, }, { - id: 2208, + id: '2208', from: 'HEL', to: 'LIS', attributes: { prediction: 4970 }, }, { - id: 2209, + id: '2209', from: 'VCE', to: 'LIS', attributes: { prediction: 3691 }, }, { - id: 2210, + id: '2210', from: 'VLI', to: 'LNE', attributes: { prediction: 170 }, }, { - id: 2211, + id: '2211', from: 'CRL', to: 'LPA', attributes: { prediction: 2457 }, }, { - id: 2212, + id: '2212', from: 'CIA', to: 'LPL', attributes: { prediction: 2268 }, }, { - id: 2213, + id: '2213', from: 'MIA', to: 'LRM', attributes: { prediction: 777 }, }, { - id: 2214, + id: '2214', from: 'IAH', to: 'MEM', attributes: { prediction: 11341 }, }, { - id: 2215, + id: '2215', from: 'PDX', to: 'MFR', attributes: { prediction: 6831 }, }, { - id: 2216, + id: '2216', from: 'LAS', to: 'MHT', attributes: { prediction: 3592 }, }, { - id: 2217, + id: '2217', from: 'AUA', to: 'MIA', attributes: { prediction: 10076 }, }, { - id: 2218, + id: '2218', from: 'CDG', to: 'LFW', attributes: { prediction: 3669 }, }, { - id: 2219, + id: '2219', from: 'EIN', to: 'LGW', attributes: { prediction: 4250 }, }, { - id: 2220, + id: '2220', from: 'ARN', to: 'LHR', attributes: { prediction: 39159 }, }, { - id: 2221, + id: '2221', from: 'GIG', to: 'LHR', attributes: { prediction: 2698 }, }, { - id: 2222, + id: '2222', from: 'CPH', to: 'LIN', attributes: { prediction: 3116 }, }, { - id: 2223, + id: '2223', from: 'FNC', to: 'LIS', attributes: { prediction: 52941 }, }, { - id: 2224, + id: '2224', from: 'ORD', to: 'LIT', attributes: { prediction: 11071 }, }, { - id: 2225, + id: '2225', from: 'STN', to: 'LJU', attributes: { prediction: 4729 }, }, { - id: 2226, + id: '2226', from: 'DKR', to: 'LOS', attributes: { prediction: 1024 }, }, { - id: 2227, + id: '2227', from: 'DRS', to: 'LPA', attributes: { prediction: 738 }, }, { - id: 2228, + id: '2228', from: 'BRS', to: 'MJV', attributes: { prediction: 4719 }, }, { - id: 2229, + id: '2229', from: 'ROK', to: 'MKY', attributes: { prediction: 1346 }, }, { - id: 2230, + id: '2230', from: 'KTW', to: 'MMX', attributes: { prediction: 1891 }, }, { - id: 2231, + id: '2231', from: 'DTW', to: 'MSN', attributes: { prediction: 15408 }, }, { - id: 2232, + id: '2232', from: 'PFO', to: 'NCL', attributes: { prediction: 2566 }, }, { - id: 2233, + id: '2233', from: 'ITM', to: 'NGS', attributes: { prediction: 15533 }, }, { - id: 2234, + id: '2234', from: 'PVG', to: 'NGS', attributes: { prediction: 720 }, }, { - id: 2235, + id: '2235', from: 'WUH', to: 'NNG', attributes: { prediction: 3132 }, }, { - id: 2236, + id: '2236', from: 'SZZ', to: 'NRN', attributes: { prediction: 1655 }, }, { - id: 2237, + id: '2237', from: 'AUH', to: 'MEL', attributes: { prediction: 7428 }, }, { - id: 2238, + id: '2238', from: 'MFE', to: 'MEM', attributes: { prediction: 3723 }, }, { - id: 2239, + id: '2239', from: 'CLT', to: 'MEX', attributes: { prediction: 3977 }, }, { - id: 2240, + id: '2240', from: 'TIJ', to: 'MEX', attributes: { prediction: 41437 }, }, { - id: 2241, + id: '2241', from: 'KIH', to: 'MHD', attributes: { prediction: 841 }, }, { - id: 2242, + id: '2242', from: 'LUX', to: 'MIR', attributes: { prediction: 3942 }, }, { - id: 2243, + id: '2243', from: 'BLK', to: 'MJV', attributes: { prediction: 1890 }, }, { - id: 2244, + id: '2244', from: 'EDI', to: 'MJV', attributes: { prediction: 3217 }, }, { - id: 2245, + id: '2245', from: 'NCL', to: 'MJV', attributes: { prediction: 6389 }, }, { - id: 2246, + id: '2246', from: 'LAX', to: 'MKE', attributes: { prediction: 14819 }, }, { - id: 2247, + id: '2247', from: 'DUS', to: 'MLA', attributes: { prediction: 4557 }, }, { - id: 2248, + id: '2248', from: 'BIA', to: 'NTE', attributes: { prediction: 791 }, }, { - id: 2249, + id: '2249', from: 'GTF', to: 'MSP', attributes: { prediction: 4101 }, }, { - id: 2250, + id: '2250', from: 'FWA', to: 'MSY', attributes: { prediction: 40 }, }, { - id: 2251, + id: '2251', from: 'YYC', to: 'MUC', attributes: { prediction: 1289 }, }, { - id: 2252, + id: '2252', from: 'CGN', to: 'MXP', attributes: { prediction: 4757 }, }, { - id: 2253, + id: '2253', from: 'KBP', to: 'MXP', attributes: { prediction: 4426 }, }, { - id: 2254, + id: '2254', from: 'HTS', to: 'MYR', attributes: { prediction: 1078 }, }, { - id: 2255, + id: '2255', from: 'LBV', to: 'NBO', attributes: { prediction: 932 }, }, { - id: 2256, + id: '2256', from: 'POL', to: 'NBO', attributes: { prediction: 782 }, }, { - id: 2257, + id: '2257', from: 'BUR', to: 'OAK', attributes: { prediction: 35876 }, }, { - id: 2258, + id: '2258', from: 'ALB', to: 'OGS', attributes: { prediction: 727 }, }, { - id: 2259, + id: '2259', from: 'GVA', to: 'OLB', attributes: { prediction: 1955 }, }, { - id: 2260, + id: '2260', from: 'LGA', to: 'OMA', attributes: { prediction: 1452 }, }, { - id: 2261, + id: '2261', from: 'TFS', to: 'OPO', attributes: { prediction: 1344 }, }, { - id: 2262, + id: '2262', from: 'GRU', to: 'ORD', attributes: { prediction: 5155 }, }, { - id: 2263, + id: '2263', from: 'CDG', to: 'NDJ', attributes: { prediction: 3243 }, }, { - id: 2264, + id: '2264', from: 'ABR', to: 'PIR', attributes: { prediction: 642 }, }, { - id: 2265, + id: '2265', from: 'MIA', to: 'PLS', attributes: { prediction: 10410 }, }, { - id: 2266, + id: '2266', from: 'ORY', to: 'PMI', attributes: { prediction: 2974 }, }, { - id: 2267, + id: '2267', from: 'BIQ', to: 'ORY', attributes: { prediction: 26810 }, }, { - id: 2268, + id: '2268', from: 'PRG', to: 'OSL', attributes: { prediction: 11584 }, }, { - id: 2269, + id: '2269', from: 'RJK', to: 'OSL', attributes: { prediction: 1116 }, }, { - id: 2270, + id: '2270', from: 'FAO', to: 'PAD', attributes: { prediction: 701 }, }, { - id: 2271, + id: '2271', from: 'DSN', to: 'PEK', attributes: { prediction: 7266 }, }, { - id: 2272, + id: '2272', from: 'IAD', to: 'PEK', attributes: { prediction: 6573 }, }, { - id: 2273, + id: '2273', from: 'EWR', to: 'PHL', attributes: { prediction: 6001 }, }, { - id: 2274, + id: '2274', from: 'MAG', to: 'POM', attributes: { prediction: 5212 }, }, { - id: 2275, + id: '2275', from: 'BEY', to: 'PRG', attributes: { prediction: 6835 }, }, { - id: 2276, + id: '2276', from: 'BOJ', to: 'PRG', attributes: { prediction: 3868 }, }, { - id: 2277, + id: '2277', from: 'SMF', to: 'SAN', attributes: { prediction: 34073 }, }, { - id: 2278, + id: '2278', from: 'CLT', to: 'STL', attributes: { prediction: 12970 }, }, { - id: 2279, + id: '2279', from: 'GLA', to: 'STN', attributes: { prediction: 17472 }, }, { - id: 2280, + id: '2280', from: 'MKE', to: 'SDF', attributes: { prediction: 1671 }, }, { - id: 2281, + id: '2281', from: 'TSO', to: 'PZE', attributes: { prediction: 871 }, }, { - id: 2282, + id: '2282', from: 'VLC', to: 'RAK', attributes: { prediction: 152 }, }, { - id: 2283, + id: '2283', from: 'VER', to: 'REX', attributes: { prediction: 1264 }, }, { - id: 2284, + id: '2284', from: 'JFK', to: 'RIC', attributes: { prediction: 9547 }, }, { - id: 2285, + id: '2285', from: 'EMA', to: 'RIX', attributes: { prediction: 2646 }, }, { - id: 2286, + id: '2286', from: 'MUC', to: 'RIX', attributes: { prediction: 3401 }, }, { - id: 2287, + id: '2287', from: 'HIR', to: 'RNA', attributes: { prediction: 72 }, }, { - id: 2288, + id: '2288', from: 'OSL', to: 'RRS', attributes: { prediction: 1244 }, }, { - id: 2289, + id: '2289', from: 'ORD', to: 'RST', attributes: { prediction: 5730 }, }, { - id: 2290, + id: '2290', from: 'MIA', to: 'RTB', attributes: { prediction: 466 }, }, { - id: 2291, + id: '2291', from: 'LGW', to: 'RTM', attributes: { prediction: 7433 }, }, { - id: 2292, + id: '2292', from: 'EVN', to: 'RTW', attributes: { prediction: 188 }, }, { - id: 2293, + id: '2293', from: 'EAM', to: 'RUH', attributes: { prediction: 3269 }, }, { - id: 2294, + id: '2294', from: 'MEX', to: 'SAL', attributes: { prediction: 4910 }, }, { - id: 2295, + id: '2295', from: 'AZA', to: 'SAT', attributes: { prediction: 255 }, }, { - id: 2296, + id: '2296', from: 'DFW', to: 'SAT', attributes: { prediction: 51702 }, }, { - id: 2297, + id: '2297', from: 'FRA', to: 'SAW', attributes: { prediction: 12066 }, }, { - id: 2298, + id: '2298', from: 'KIX', to: 'SEA', attributes: { prediction: 5387 }, }, { - id: 2299, + id: '2299', from: 'SKG', to: 'STR', attributes: { prediction: 11490 }, }, { - id: 2300, + id: '2300', from: 'WAW', to: 'SVG', attributes: { prediction: 1188 }, }, { - id: 2301, + id: '2301', from: 'TLL', to: 'SVO', attributes: { prediction: 1681 }, }, { - id: 2302, + id: '2302', from: 'FLL', to: 'SWF', attributes: { prediction: 3755 }, }, { - id: 2303, + id: '2303', from: 'TLS', to: 'SXB', attributes: { prediction: 4806 }, }, { - id: 2304, + id: '2304', from: 'STR', to: 'SXF', attributes: { prediction: 13091 }, }, { - id: 2305, + id: '2305', from: 'DXB', to: 'SXR', attributes: { prediction: 649 }, }, { - id: 2306, + id: '2306', from: 'BLQ', to: 'TPS', attributes: { prediction: 8516 }, }, { - id: 2307, + id: '2307', from: 'LTN', to: 'TPS', attributes: { prediction: 1650 }, }, { - id: 2308, + id: '2308', from: 'DUB', to: 'TRF', attributes: { prediction: 4606 }, }, { - id: 2309, + id: '2309', from: 'BRU', to: 'TRN', attributes: { prediction: 3503 }, }, { - id: 2310, + id: '2310', from: 'DMM', to: 'TRV', attributes: { prediction: 882 }, }, { - id: 2311, + id: '2311', from: 'SEA', to: 'TUS', attributes: { prediction: 3790 }, }, { - id: 2312, + id: '2312', from: 'CLY', to: 'TXL', attributes: { prediction: 601 }, }, { - id: 2313, + id: '2313', from: 'LAX', to: 'SMF', attributes: { prediction: 34362 }, }, { - id: 2314, + id: '2314', from: 'LAS', to: 'SMX', attributes: { prediction: 923 }, }, { - id: 2315, + id: '2315', from: 'YYZ', to: 'SNU', attributes: { prediction: 3294 }, }, { - id: 2316, + id: '2316', from: 'SZG', to: 'SOU', attributes: { prediction: 374 }, }, { - id: 2317, + id: '2317', from: 'TXL', to: 'SPC', attributes: { prediction: 826 }, }, { - id: 2318, + id: '2318', from: 'ABQ', to: 'STL', attributes: { prediction: 3600 }, }, { - id: 2319, + id: '2319', from: 'KDV', to: 'SUV', attributes: { prediction: 515 }, }, { - id: 2320, + id: '2320', from: 'ATH', to: 'SVO', attributes: { prediction: 6409 }, }, { - id: 2321, + id: '2321', from: 'TLL', to: 'SVO', attributes: { prediction: 1611 }, }, { - id: 2322, + id: '2322', from: 'UUS', to: 'SVO', attributes: { prediction: 10577 }, }, { - id: 2323, + id: '2323', from: 'DME', to: 'SVX', attributes: { prediction: 33056 }, }, { - id: 2324, + id: '2324', from: 'IAH', to: 'TYS', attributes: { prediction: 5075 }, }, { - id: 2325, + id: '2325', from: 'BSB', to: 'UDI', attributes: { prediction: 1018 }, }, { - id: 2326, + id: '2326', from: 'JFK', to: 'SYR', attributes: { prediction: 15385 }, }, { - id: 2327, + id: '2327', from: 'TSN', to: 'SZX', attributes: { prediction: 10317 }, }, { - id: 2328, + id: '2328', from: 'VGO', to: 'TFS', attributes: { prediction: 711 }, }, { - id: 2329, + id: '2329', from: 'LJU', to: 'TGD', attributes: { prediction: 1419 }, }, { - id: 2330, + id: '2330', from: 'PVG', to: 'TIJ', attributes: { prediction: 1892 }, }, { - id: 2331, + id: '2331', from: 'OSL', to: 'TLL', attributes: { prediction: 6345 }, }, { - id: 2332, + id: '2332', from: 'CMN', to: 'TLS', attributes: { prediction: 5982 }, }, { - id: 2333, + id: '2333', from: 'HAM', to: 'TLS', attributes: { prediction: 2361 }, }, { - id: 2334, + id: '2334', from: 'LIL', to: 'TLS', attributes: { prediction: 5779 }, }, { - id: 2335, + id: '2335', from: 'LIS', to: 'TLS', attributes: { prediction: 4605 }, }, { - id: 2336, + id: '2336', from: 'DME', to: 'TLV', attributes: { prediction: 23214 }, }, { - id: 2337, + id: '2337', from: 'GRU', to: 'TLV', attributes: { prediction: 3028 }, }, { - id: 2338, + id: '2338', from: 'LCA', to: 'TLV', attributes: { prediction: 4854 }, }, { - id: 2339, + id: '2339', from: 'XIY', to: 'TNA', attributes: { prediction: 4764 }, }, { - id: 2340, + id: '2340', from: 'AUS', to: 'TPA', attributes: { prediction: 3673 }, }, { - id: 2341, + id: '2341', from: 'PBI', to: 'TPA', attributes: { prediction: 10482 }, }, { - id: 2342, + id: '2342', from: 'HKT', to: 'TPE', attributes: { prediction: 933 }, }, { - id: 2343, + id: '2343', from: 'BMA', to: 'UME', attributes: { prediction: 6882 }, }, { - id: 2344, + id: '2344', from: 'TBG', to: 'UNG', attributes: { prediction: 698 }, }, { - id: 2345, + id: '2345', from: 'CAG', to: 'VCE', attributes: { prediction: 7794 }, }, { - id: 2346, + id: '2346', from: 'DBV', to: 'VIE', attributes: { prediction: 5711 }, }, { - id: 2347, + id: '2347', from: 'DLM', to: 'VIE', attributes: { prediction: 647 }, }, { - id: 2348, + id: '2348', from: 'LGW', to: 'VIE', attributes: { prediction: 13592 }, }, { - id: 2349, + id: '2349', from: 'LIN', to: 'VIE', attributes: { prediction: 2274 }, }, { - id: 2350, + id: '2350', from: 'PEK', to: 'SYX', attributes: { prediction: 12927 }, }, { - id: 2351, + id: '2351', from: 'YEV', to: 'YOC', attributes: { prediction: 125 }, }, { - id: 2352, + id: '2352', from: 'EWR', to: 'YQB', attributes: { prediction: 5188 }, }, { - id: 2353, + id: '2353', from: 'YXN', to: 'YRT', attributes: { prediction: 252 }, }, { - id: 2354, + id: '2354', from: 'MAN', to: 'YVR', attributes: { prediction: 3179 }, }, { - id: 2355, + id: '2355', from: 'ZRH', to: 'VIE', attributes: { prediction: 41013 }, }, { - id: 2356, + id: '2356', from: 'CRL', to: 'VLL', attributes: { prediction: 4158 }, }, { - id: 2357, + id: '2357', from: 'TLL', to: 'VNO', attributes: { prediction: 3625 }, }, { - id: 2358, + id: '2358', from: 'PAZ', to: 'VSA', attributes: { prediction: 1623 }, }, { - id: 2359, + id: '2359', from: 'BGY', to: 'WRO', attributes: { prediction: 2389 }, }, { - id: 2360, + id: '2360', from: 'HGU', to: 'WWK', attributes: { prediction: 148 }, }, { - id: 2361, + id: '2361', from: 'IAH', to: 'XNA', attributes: { prediction: 4932 }, }, { - id: 2362, + id: '2362', from: 'XIY', to: 'XNN', attributes: { prediction: 5572 }, }, { - id: 2363, + id: '2363', from: 'YXL', to: 'YAC', attributes: { prediction: 513 }, }, { - id: 2364, + id: '2364', from: 'YRL', to: 'YHD', attributes: { prediction: 494 }, }, { - id: 2365, + id: '2365', from: 'ZLT', to: 'YIF', attributes: { prediction: 182 }, }, { - id: 2366, + id: '2366', from: 'YVR', to: 'YXC', attributes: { prediction: 2470 }, }, { - id: 2367, + id: '2367', from: 'ABE', to: 'YYZ', attributes: { prediction: 754 }, }, { - id: 2368, + id: '2368', from: 'ISB', to: 'YYZ', attributes: { prediction: 1490 }, }, { - id: 2369, + id: '2369', from: 'BOS', to: 'BGR', attributes: { prediction: 214 }, }, { - id: 2370, + id: '2370', from: 'BHX', to: 'BHD', attributes: { prediction: 13388 }, }, { - id: 2371, + id: '2371', from: 'STR', to: 'BIA', attributes: { prediction: 732 }, }, { - id: 2372, + id: '2372', from: 'AZA', to: 'BIS', attributes: { prediction: 820 }, }, { - id: 2373, + id: '2373', from: 'KOE', to: 'BJW', attributes: { prediction: 524 }, }, { - id: 2374, + id: '2374', from: 'FNA', to: 'ACC', attributes: { prediction: 2322 }, }, { - id: 2375, + id: '2375', from: 'VIE', to: 'ACE', attributes: { prediction: 1035 }, }, { - id: 2376, + id: '2376', from: 'CTG', to: 'ADZ', attributes: { prediction: 3534 }, }, { - id: 2377, + id: '2377', from: 'PIK', to: 'AGP', attributes: { prediction: 3402 }, }, { - id: 2378, + id: '2378', from: 'SVO', to: 'AGP', attributes: { prediction: 2722 }, }, { - id: 2379, + id: '2379', from: 'URC', to: 'AKU', attributes: { prediction: 2891 }, }, { - id: 2380, + id: '2380', from: 'KYP', to: 'AKY', attributes: { prediction: 55 }, }, { - id: 2381, + id: '2381', from: 'ART', to: 'ALB', attributes: { prediction: 627 }, }, { - id: 2382, + id: '2382', from: 'YYZ', to: 'ALB', attributes: { prediction: 1142 }, }, { - id: 2383, + id: '2383', from: 'SVG', to: 'ALC', attributes: { prediction: 1083 }, }, { - id: 2384, + id: '2384', from: 'ZRH', to: 'ALC', attributes: { prediction: 2301 }, }, { - id: 2385, + id: '2385', from: 'MUC', to: 'AMM', attributes: { prediction: 916 }, }, { - id: 2386, + id: '2386', from: 'TLV', to: 'AMM', attributes: { prediction: 4695 }, }, { - id: 2387, + id: '2387', from: 'DUB', to: 'AMS', attributes: { prediction: 25571 }, }, { - id: 2388, + id: '2388', from: 'JFK', to: 'AMS', attributes: { prediction: 22213 }, }, { - id: 2389, + id: '2389', from: 'SGN', to: 'BKK', attributes: { prediction: 30496 }, }, { - id: 2390, + id: '2390', from: 'DAM', to: 'BUD', attributes: { prediction: 3164 }, }, { - id: 2391, + id: '2391', from: 'PMI', to: 'BUD', attributes: { prediction: 300 }, }, { - id: 2392, + id: '2392', from: 'CLE', to: 'BUF', attributes: { prediction: 3711 }, }, { - id: 2393, + id: '2393', from: 'AKL', to: 'BWN', attributes: { prediction: 2407 }, }, { - id: 2394, + id: '2394', from: 'KUL', to: 'BWN', attributes: { prediction: 6868 }, }, { - id: 2395, + id: '2395', from: 'DUB', to: 'BZG', attributes: { prediction: 2186 }, }, { - id: 2396, + id: '2396', from: 'FCO', to: 'CCS', attributes: { prediction: 5616 }, }, { - id: 2397, + id: '2397', from: 'LGA', to: 'BTV', attributes: { prediction: 4951 }, }, { - id: 2398, + id: '2398', from: 'SKP', to: 'BUD', attributes: { prediction: 4106 }, }, { - id: 2399, + id: '2399', from: 'SLC', to: 'BZN', attributes: { prediction: 6321 }, }, { - id: 2400, + id: '2400', from: 'BBU', to: 'BCN', attributes: { prediction: 4067 }, }, { - id: 2401, + id: '2401', from: 'CMN', to: 'BCN', attributes: { prediction: 9685 }, }, { - id: 2402, + id: '2402', from: 'MEX', to: 'BCN', attributes: { prediction: 1333 }, }, { - id: 2403, + id: '2403', from: 'BNE', to: 'BDB', attributes: { prediction: 3488 }, }, { - id: 2404, + id: '2404', from: 'YYZ', to: 'BDL', attributes: { prediction: 2918 }, }, { - id: 2405, + id: '2405', from: 'TPS', to: 'BDS', attributes: { prediction: 2192 }, }, { - id: 2406, + id: '2406', from: 'MAO', to: 'BEL', attributes: { prediction: 12738 }, }, { - id: 2407, + id: '2407', from: 'CDG', to: 'BES', attributes: { prediction: 8396 }, }, { - id: 2408, + id: '2408', from: 'JFK', to: 'CAI', attributes: { prediction: 13307 }, }, { - id: 2409, + id: '2409', from: 'TSV', to: 'CBR', attributes: { prediction: 1468 }, }, { - id: 2410, + id: '2410', from: 'AUH', to: 'CCJ', attributes: { prediction: 4802 }, }, { - id: 2411, + id: '2411', from: 'FRA', to: 'BLL', attributes: { prediction: 7858 }, }, { - id: 2412, + id: '2412', from: 'PHE', to: 'BME', attributes: { prediction: 138 }, }, { - id: 2413, + id: '2413', from: 'UIB', to: 'BOG', attributes: { prediction: 1690 }, }, { - id: 2414, + id: '2414', from: 'GRO', to: 'BOH', attributes: { prediction: 2332 }, }, { - id: 2415, + id: '2415', from: 'DAC', to: 'BOM', attributes: { prediction: 4538 }, }, { - id: 2416, + id: '2416', from: 'ICN', to: 'BOM', attributes: { prediction: 2645 }, }, { - id: 2417, + id: '2417', from: 'ZRH', to: 'BOS', attributes: { prediction: 6201 }, }, { - id: 2418, + id: '2418', from: 'SSA', to: 'BPS', attributes: { prediction: 7696 }, }, { - id: 2419, + id: '2419', from: 'HAM', to: 'BRI', attributes: { prediction: 1178 }, }, { - id: 2420, + id: '2420', from: 'MXP', to: 'BRI', attributes: { prediction: 19439 }, }, { - id: 2421, + id: '2421', from: 'TPS', to: 'BRI', attributes: { prediction: 1514 }, }, { - id: 2422, + id: '2422', from: 'MAN', to: 'BRU', attributes: { prediction: 9759 }, }, { - id: 2423, + id: '2423', from: 'PMI', to: 'BRU', attributes: { prediction: 539 }, }, { - id: 2424, + id: '2424', from: 'FAI', to: 'BRW', attributes: { prediction: 2654 }, }, { - id: 2425, + id: '2425', from: 'HER', to: 'BSL', attributes: { prediction: 2479 }, }, { - id: 2426, + id: '2426', from: 'TRV', to: 'CCJ', attributes: { prediction: 622 }, }, { - id: 2427, + id: '2427', from: 'DUB', to: 'CDG', attributes: { prediction: 33434 }, }, { - id: 2428, + id: '2428', from: 'KUL', to: 'CDG', attributes: { prediction: 7822 }, }, { - id: 2429, + id: '2429', from: 'OZC', to: 'CEB', attributes: { prediction: 2447 }, }, { - id: 2430, + id: '2430', from: 'BKK', to: 'CEI', attributes: { prediction: 19202 }, }, { - id: 2431, + id: '2431', from: 'DFW', to: 'CDG', attributes: { prediction: 5656 }, }, { - id: 2432, + id: '2432', from: 'PUF', to: 'CDG', attributes: { prediction: 11771 }, }, { - id: 2433, + id: '2433', from: 'CRK', to: 'CEB', attributes: { prediction: 1361 }, }, { - id: 2434, + id: '2434', from: 'KUN', to: 'CRL', attributes: { prediction: 2457 }, }, { - id: 2435, + id: '2435', from: 'ACA', to: 'CRP', attributes: { prediction: 30 }, }, { - id: 2436, + id: '2436', from: 'ZRH', to: 'CTA', attributes: { prediction: 2083 }, }, { - id: 2437, + id: '2437', from: 'RIS', to: 'CTS', attributes: { prediction: 1876 }, }, { - id: 2438, + id: '2438', from: 'SJW', to: 'CTU', attributes: { prediction: 4457 }, }, { - id: 2439, + id: '2439', from: 'TIA', to: 'CGN', attributes: { prediction: 732 }, }, { - id: 2440, + id: '2440', from: 'CAN', to: 'CGO', attributes: { prediction: 10471 }, }, { - id: 2441, + id: '2441', from: 'GRO', to: 'CIA', attributes: { prediction: 11877 }, }, { - id: 2442, + id: '2442', from: 'CJU', to: 'CJJ', attributes: { prediction: 17156 }, }, { - id: 2443, + id: '2443', from: 'BNA', to: 'CLE', attributes: { prediction: 7238 }, }, { - id: 2444, + id: '2444', from: 'MBJ', to: 'CLT', attributes: { prediction: 8988 }, }, { - id: 2445, + id: '2445', from: 'STT', to: 'CLT', attributes: { prediction: 4830 }, }, { - id: 2446, + id: '2446', from: 'AGP', to: 'CMN', attributes: { prediction: 1725 }, }, { - id: 2447, + id: '2447', from: 'EWR', to: 'CPH', attributes: { prediction: 12005 }, }, { - id: 2448, + id: '2448', from: 'VNO', to: 'CPH', attributes: { prediction: 11198 }, }, { - id: 2449, + id: '2449', from: 'YYZ', to: 'CZM', attributes: { prediction: 373 }, }, { - id: 2450, + id: '2450', from: 'DXB', to: 'DAC', attributes: { prediction: 25557 }, }, { - id: 2451, + id: '2451', from: 'ZNZ', to: 'DAR', attributes: { prediction: 5956 }, }, { - id: 2452, + id: '2452', from: 'BUD', to: 'CFU', attributes: { prediction: 446 }, }, { - id: 2453, + id: '2453', from: 'CGN', to: 'CPH', attributes: { prediction: 3144 }, }, { - id: 2454, + id: '2454', from: 'JNB', to: 'CPT', attributes: { prediction: 164691 }, }, { - id: 2455, + id: '2455', from: 'PGF', to: 'CRL', attributes: { prediction: 1332 }, }, { - id: 2456, + id: '2456', from: 'EMA', to: 'GCI', attributes: { prediction: 1824 }, }, { - id: 2457, + id: '2457', from: 'NDR', to: 'DUS', attributes: { prediction: 1371 }, }, { - id: 2458, + id: '2458', from: 'ORD', to: 'DUS', attributes: { prediction: 6154 }, }, { - id: 2459, + id: '2459', from: 'CUU', to: 'DFW', attributes: { prediction: 2198 }, }, { - id: 2460, + id: '2460', from: 'LBB', to: 'DFW', attributes: { prediction: 11064 }, }, { - id: 2461, + id: '2461', from: 'ABJ', to: 'DKR', attributes: { prediction: 5033 }, }, { - id: 2462, + id: '2462', from: 'VLI', to: 'DLY', attributes: { prediction: 33 }, }, { - id: 2463, + id: '2463', from: 'BAX', to: 'DME', attributes: { prediction: 6138 }, }, { - id: 2464, + id: '2464', from: 'BAH', to: 'DMM', attributes: { prediction: 5938 }, }, { - id: 2465, + id: '2465', from: 'CGK', to: 'DOH', attributes: { prediction: 7177 }, }, { - id: 2466, + id: '2466', from: 'SKG', to: 'DRS', attributes: { prediction: 786 }, }, { - id: 2467, + id: '2467', from: 'IST', to: 'DTM', attributes: { prediction: 674 }, }, { - id: 2468, + id: '2468', from: 'TOL', to: 'DTW', attributes: { prediction: 2097 }, }, { - id: 2469, + id: '2469', from: 'CPT', to: 'DUR', attributes: { prediction: 37541 }, }, { - id: 2470, + id: '2470', from: 'GPA', to: 'DUS', attributes: { prediction: 847 }, }, { - id: 2471, + id: '2471', from: 'LKO', to: 'DXB', attributes: { prediction: 2660 }, }, { - id: 2472, + id: '2472', from: 'PHX', to: 'GEG', attributes: { prediction: 5109 }, }, { - id: 2473, + id: '2473', from: 'CVQ', to: 'GET', attributes: { prediction: 297 }, }, { - id: 2474, + id: '2474', from: 'LIM', to: 'GIG', attributes: { prediction: 2090 }, }, { - id: 2475, + id: '2475', from: 'HAC', to: 'HND', attributes: { prediction: 9368 }, }, { - id: 2476, + id: '2476', from: 'MMY', to: 'HND', attributes: { prediction: 3128 }, }, { - id: 2477, + id: '2477', from: 'SYD', to: 'HBA', attributes: { prediction: 18349 }, }, { - id: 2478, + id: '2478', from: 'CGN', to: 'HDF', attributes: { prediction: 200 }, }, { - id: 2479, + id: '2479', from: 'ALC', to: 'HEL', attributes: { prediction: 772 }, }, { - id: 2480, + id: '2480', from: 'KEM', to: 'HEL', attributes: { prediction: 6931 }, }, { - id: 2481, + id: '2481', from: 'TKU', to: 'HEL', attributes: { prediction: 4164 }, }, { - id: 2482, + id: '2482', from: 'LCA', to: 'HER', attributes: { prediction: 2405 }, }, { - id: 2483, + id: '2483', from: 'TSN', to: 'HET', attributes: { prediction: 1307 }, }, { - id: 2484, + id: '2484', from: 'NRT', to: 'HGH', attributes: { prediction: 1456 }, }, { - id: 2485, + id: '2485', from: 'TAO', to: 'HGH', attributes: { prediction: 3388 }, }, { - id: 2486, + id: '2486', from: 'KIX', to: 'HKD', attributes: { prediction: 2511 }, }, { - id: 2487, + id: '2487', from: 'JFK', to: 'HKG', attributes: { prediction: 15981 }, }, { - id: 2488, + id: '2488', from: 'MEL', to: 'HKG', attributes: { prediction: 31598 }, }, { - id: 2489, + id: '2489', from: 'WNZ', to: 'HKG', attributes: { prediction: 2281 }, }, { - id: 2490, + id: '2490', from: 'GDL', to: 'HMO', attributes: { prediction: 6773 }, }, { - id: 2491, + id: '2491', from: 'AKL', to: 'HNL', attributes: { prediction: 2120 }, }, { - id: 2492, + id: '2492', from: 'OGG', to: 'HNL', attributes: { prediction: 56268 }, }, { - id: 2493, + id: '2493', from: 'NAP', to: 'FCO', attributes: { prediction: 12893 }, }, { - id: 2494, + id: '2494', from: 'REC', to: 'FEN', attributes: { prediction: 4437 }, }, { - id: 2495, + id: '2495', from: 'ATL', to: 'FLL', attributes: { prediction: 92677 }, }, { - id: 2496, + id: '2496', from: 'LGW', to: 'FLR', attributes: { prediction: 7142 }, }, { - id: 2497, + id: '2497', from: 'TIA', to: 'FLR', attributes: { prediction: 4313 }, }, { - id: 2498, + id: '2498', from: 'STN', to: 'FNC', attributes: { prediction: 3588 }, }, { - id: 2499, + id: '2499', from: 'RZE', to: 'FRA', attributes: { prediction: 1833 }, }, { - id: 2500, + id: '2500', from: 'SKG', to: 'FRA', attributes: { prediction: 6232 }, }, { - id: 2501, + id: '2501', from: 'SOF', to: 'FRL', attributes: { prediction: 2215 }, }, { - id: 2502, + id: '2502', from: 'REL', to: 'FTE', attributes: { prediction: 2756 }, }, { - id: 2503, + id: '2503', from: 'FLL', to: 'HPN', attributes: { prediction: 10531 }, }, { - id: 2504, + id: '2504', from: 'AYT', to: 'GLA', attributes: { prediction: 1004 }, }, { - id: 2505, + id: '2505', from: 'EWR', to: 'GLA', attributes: { prediction: 4705 }, }, { - id: 2506, + id: '2506', from: 'KUS', to: 'GOH', attributes: { prediction: 135 }, }, { - id: 2507, + id: '2507', from: 'EIN', to: 'GRO', attributes: { prediction: 5415 }, }, { - id: 2508, + id: '2508', from: 'KGS', to: 'GRZ', attributes: { prediction: 653 }, }, { - id: 2509, + id: '2509', from: 'DFW', to: 'GSP', attributes: { prediction: 4940 }, }, { - id: 2510, + id: '2510', from: 'DJE', to: 'GVA', attributes: { prediction: 1045 }, }, { - id: 2511, + id: '2511', from: 'ACE', to: 'HAJ', attributes: { prediction: 889 }, }, { - id: 2512, + id: '2512', from: 'MVY', to: 'HYA', attributes: { prediction: 229 }, }, { - id: 2513, + id: '2513', from: 'FRA', to: 'HYD', attributes: { prediction: 2296 }, }, { - id: 2514, + id: '2514', from: 'BRU', to: 'IAD', attributes: { prediction: 4868 }, }, { - id: 2515, + id: '2515', from: 'KWI', to: 'IAD', attributes: { prediction: 6332 }, }, { - id: 2516, + id: '2516', from: 'SAH', to: 'HOD', attributes: { prediction: 2293 }, }, { - id: 2517, + id: '2517', from: 'CVG', to: 'HOU', attributes: { prediction: 119 }, }, { - id: 2518, + id: '2518', from: 'VFA', to: 'HRE', attributes: { prediction: 810 }, }, { - id: 2519, + id: '2519', from: 'BOM', to: 'HYD', attributes: { prediction: 60215 }, }, { - id: 2520, + id: '2520', from: 'PHX', to: 'JFK', attributes: { prediction: 19740 }, }, { - id: 2521, + id: '2521', from: 'DXB', to: 'JNB', attributes: { prediction: 26022 }, }, { - id: 2522, + id: '2522', from: 'JAV', to: 'JQA', attributes: { prediction: 212 }, }, { - id: 2523, + id: '2523', from: 'RUH', to: 'KBL', attributes: { prediction: 1062 }, }, { - id: 2524, + id: '2524', from: 'HEL', to: 'KBP', attributes: { prediction: 3063 }, }, { - id: 2525, + id: '2525', from: 'UIO', to: 'IAH', attributes: { prediction: 3441 }, }, { - id: 2526, + id: '2526', from: 'MAH', to: 'IBZ', attributes: { prediction: 1546 }, }, { - id: 2527, + id: '2527', from: 'GOT', to: 'IST', attributes: { prediction: 2482 }, }, { - id: 2528, + id: '2528', from: 'KRT', to: 'IST', attributes: { prediction: 4081 }, }, { - id: 2529, + id: '2529', from: 'CCU', to: 'IXA', attributes: { prediction: 9314 }, }, { - id: 2530, + id: '2530', from: 'IXS', to: 'IXA', attributes: { prediction: 225 }, }, { - id: 2531, + id: '2531', from: 'DEN', to: 'JAC', attributes: { prediction: 16997 }, }, { - id: 2532, + id: '2532', from: 'KEF', to: 'JFK', attributes: { prediction: 10935 }, }, { - id: 2533, + id: '2533', from: 'NBO', to: 'KGL', attributes: { prediction: 6398 }, }, { - id: 2534, + id: '2534', from: 'MDL', to: 'KHM', attributes: { prediction: 290 }, }, { - id: 2535, + id: '2535', from: 'AXR', to: 'KKR', attributes: { prediction: 82 }, }, { - id: 2536, + id: '2536', from: 'MNL', to: 'KLO', attributes: { prediction: 22141 }, }, { - id: 2537, + id: '2537', from: 'RNB', to: 'KLR', attributes: { prediction: 334 }, }, { - id: 2538, + id: '2538', from: 'LJG', to: 'KMG', attributes: { prediction: 9136 }, }, { - id: 2539, + id: '2539', from: 'NGO', to: 'KMI', attributes: { prediction: 8235 }, }, { - id: 2540, + id: '2540', from: 'PEK', to: 'HYN', attributes: { prediction: 5336 }, }, { - id: 2541, + id: '2541', from: 'IND', to: 'IAD', attributes: { prediction: 5425 }, }, { - id: 2542, + id: '2542', from: 'CAE', to: 'IAH', attributes: { prediction: 3288 }, }, { - id: 2543, + id: '2543', from: 'HNL', to: 'IAH', attributes: { prediction: 12247 }, }, { - id: 2544, + id: '2544', from: 'DLA', to: 'LFW', attributes: { prediction: 1820 }, }, { - id: 2545, + id: '2545', from: 'DTW', to: 'LGA', attributes: { prediction: 41696 }, }, { - id: 2546, + id: '2546', from: 'LEI', to: 'LGW', attributes: { prediction: 4404 }, }, { - id: 2547, + id: '2547', from: 'HYL', to: 'KTN', attributes: { prediction: 774 }, }, { - id: 2548, + id: '2548', from: 'OME', to: 'KTS', attributes: { prediction: 12 }, }, { - id: 2549, + id: '2549', from: 'NYO', to: 'KTW', attributes: { prediction: 2252 }, }, { - id: 2550, + id: '2550', from: 'PEK', to: 'KUL', attributes: { prediction: 10225 }, }, { - id: 2551, + id: '2551', from: 'RHO', to: 'KZS', attributes: { prediction: 657 }, }, { - id: 2552, + id: '2552', from: 'LAX', to: 'LAP', attributes: { prediction: 629 }, }, { - id: 2553, + id: '2553', from: 'SGF', to: 'LAS', attributes: { prediction: 2600 }, }, { - id: 2554, + id: '2554', from: 'DXB', to: 'LAX', attributes: { prediction: 6398 }, }, { - id: 2555, + id: '2555', from: 'TUS', to: 'LAX', attributes: { prediction: 22338 }, }, { - id: 2556, + id: '2556', from: 'BHD', to: 'LBA', attributes: { prediction: 5740 }, }, { - id: 2557, + id: '2557', from: 'DUB', to: 'LBC', attributes: { prediction: 3100 }, }, { - id: 2558, + id: '2558', from: 'DUB', to: 'LCY', attributes: { prediction: 12178 }, }, { - id: 2559, + id: '2559', from: 'RTM', to: 'LCY', attributes: { prediction: 6331 }, }, { - id: 2560, + id: '2560', from: 'MUC', to: 'LED', attributes: { prediction: 13917 }, }, { - id: 2561, + id: '2561', from: 'PMI', to: 'LEJ', attributes: { prediction: 13315 }, }, { - id: 2562, + id: '2562', from: 'SSH', to: 'LGW', attributes: { prediction: 14598 }, }, { - id: 2563, + id: '2563', from: 'AUH', to: 'LHR', attributes: { prediction: 25581 }, }, { - id: 2564, + id: '2564', from: 'CTU', to: 'LHW', attributes: { prediction: 9815 }, }, { - id: 2565, + id: '2565', from: 'ZIH', to: 'MEX', attributes: { prediction: 7737 }, }, { - id: 2566, + id: '2566', from: 'LUX', to: 'NCE', attributes: { prediction: 1584 }, }, { - id: 2567, + id: '2567', from: 'DPS', to: 'NGO', attributes: { prediction: 2408 }, }, { - id: 2568, + id: '2568', from: 'LDY', to: 'LTN', attributes: { prediction: 3213 }, }, { - id: 2569, + id: '2569', from: 'FCO', to: 'LWO', attributes: { prediction: 1226 }, }, { - id: 2570, + id: '2570', from: 'GOT', to: 'LYS', attributes: { prediction: 885 }, }, { - id: 2571, + id: '2571', from: 'SPU', to: 'LYS', attributes: { prediction: 286 }, }, { - id: 2572, + id: '2572', from: 'LGW', to: 'MAD', attributes: { prediction: 24585 }, }, { - id: 2573, + id: '2573', from: 'BOH', to: 'MAH', attributes: { prediction: 659 }, }, { - id: 2574, + id: '2574', from: 'BIA', to: 'MAN', attributes: { prediction: 828 }, }, { - id: 2575, + id: '2575', from: 'CAI', to: 'MCT', attributes: { prediction: 6595 }, }, { - id: 2576, + id: '2576', from: 'GIG', to: 'MCZ', attributes: { prediction: 3038 }, }, { - id: 2577, + id: '2577', from: 'MHT', to: 'MDW', attributes: { prediction: 12600 }, }, { - id: 2578, + id: '2578', from: 'VLL', to: 'NRN', attributes: { prediction: 2255 }, }, { - id: 2579, + id: '2579', from: 'TAS', to: 'NRT', attributes: { prediction: 1379 }, }, { - id: 2580, + id: '2580', from: 'CGK', to: 'PGK', attributes: { prediction: 16555 }, }, { - id: 2581, + id: '2581', from: 'DSM', to: 'PHX', attributes: { prediction: 2808 }, }, { - id: 2582, + id: '2582', from: 'IKA', to: 'ORY', attributes: { prediction: 1958 }, }, { - id: 2583, + id: '2583', from: 'LRT', to: 'ORY', attributes: { prediction: 6965 }, }, { - id: 2584, + id: '2584', from: 'NCY', to: 'ORY', attributes: { prediction: 3960 }, }, { - id: 2585, + id: '2585', from: 'SXB', to: 'ORY', attributes: { prediction: 7681 }, }, { - id: 2586, + id: '2586', from: 'RIX', to: 'OSL', attributes: { prediction: 11636 }, }, { - id: 2587, + id: '2587', from: 'CDG', to: 'MSP', attributes: { prediction: 6001 }, }, { - id: 2588, + id: '2588', from: 'PIA', to: 'MSP', attributes: { prediction: 2437 }, }, { - id: 2589, + id: '2589', from: 'STL', to: 'MSY', attributes: { prediction: 3211 }, }, { - id: 2590, + id: '2590', from: 'ATL', to: 'MTY', attributes: { prediction: 3521 }, }, { - id: 2591, + id: '2591', from: 'MIR', to: 'MUC', attributes: { prediction: 1780 }, }, { - id: 2592, + id: '2592', from: 'DBV', to: 'MXP', attributes: { prediction: 2380 }, }, { - id: 2593, + id: '2593', from: 'SSH', to: 'MXP', attributes: { prediction: 810 }, }, { - id: 2594, + id: '2594', from: 'NGO', to: 'MYJ', attributes: { prediction: 1974 }, }, { - id: 2595, + id: '2595', from: 'MUR', to: 'MYY', attributes: { prediction: 535 }, }, { - id: 2596, + id: '2596', from: 'MEL', to: 'NAN', attributes: { prediction: 4397 }, }, { - id: 2597, + id: '2597', from: 'BIM', to: 'NAS', attributes: { prediction: 953 }, }, { - id: 2598, + id: '2598', from: 'KUL', to: 'PDG', attributes: { prediction: 4363 }, }, { - id: 2599, + id: '2599', from: 'DEN', to: 'PDX', attributes: { prediction: 45779 }, }, { - id: 2600, + id: '2600', from: 'BUD', to: 'PEK', attributes: { prediction: 3475 }, }, { - id: 2601, + id: '2601', from: 'EWR', to: 'PEK', attributes: { prediction: 6461 }, }, { - id: 2602, + id: '2602', from: 'HLD', to: 'PEK', attributes: { prediction: 2287 }, }, { - id: 2603, + id: '2603', from: 'CGN', to: 'NUE', attributes: { prediction: 874 }, }, { - id: 2604, + id: '2604', from: 'FWA', to: 'ORD', attributes: { prediction: 6275 }, }, { - id: 2605, + id: '2605', from: 'LGB', to: 'ORD', attributes: { prediction: 4095 }, }, { - id: 2606, + id: '2606', from: 'NRT', to: 'ORD', attributes: { prediction: 38429 }, }, { - id: 2607, + id: '2607', from: 'SAT', to: 'ORD', attributes: { prediction: 16482 }, }, { - id: 2608, + id: '2608', from: 'YHZ', to: 'ORD', attributes: { prediction: 1877 }, }, { - id: 2609, + id: '2609', from: 'YYC', to: 'ORD', attributes: { prediction: 10639 }, }, { - id: 2610, + id: '2610', from: 'LIS', to: 'ORK', attributes: { prediction: 1341 }, }, { - id: 2611, + id: '2611', from: 'ORY', to: 'ORN', attributes: { prediction: 4068 }, }, { - id: 2612, + id: '2612', from: 'YNZ', to: 'PEK', attributes: { prediction: 2584 }, }, { - id: 2613, + id: '2613', from: 'AKL', to: 'PER', attributes: { prediction: 4010 }, }, { - id: 2614, + id: '2614', from: 'EPR', to: 'PER', attributes: { prediction: 1640 }, }, { - id: 2615, + id: '2615', from: 'BDL', to: 'PHL', attributes: { prediction: 14487 }, }, { - id: 2616, + id: '2616', from: 'BWI', to: 'PHL', attributes: { prediction: 10102 }, }, { - id: 2617, + id: '2617', from: 'IAH', to: 'PHL', attributes: { prediction: 26302 }, }, { - id: 2618, + id: '2618', from: 'MDT', to: 'PHL', attributes: { prediction: 4029 }, }, { - id: 2619, + id: '2619', from: 'GYM', to: 'PHX', attributes: { prediction: 485 }, }, { - id: 2620, + id: '2620', from: 'MSP', to: 'PIA', attributes: { prediction: 2382 }, }, { - id: 2621, + id: '2621', from: 'SBN', to: 'PIE', attributes: { prediction: 1200 }, }, { - id: 2622, + id: '2622', from: 'GDX', to: 'PKC', attributes: { prediction: 127 }, }, { - id: 2623, + id: '2623', from: 'TAL', to: 'RBY', attributes: { prediction: 64 }, }, { - id: 2624, + id: '2624', from: 'BOS', to: 'RDU', attributes: { prediction: 17436 }, }, { - id: 2625, + id: '2625', from: 'SNW', to: 'RGN', attributes: { prediction: 1034 }, }, { - id: 2626, + id: '2626', from: 'CTA', to: 'PMF', attributes: { prediction: 4651 }, }, { - id: 2627, + id: '2627', from: 'MXP', to: 'PMO', attributes: { prediction: 23291 }, }, { - id: 2628, + id: '2628', from: 'ORY', to: 'PMO', attributes: { prediction: 4507 }, }, { - id: 2629, + id: '2629', from: 'FLG', to: 'PRC', attributes: { prediction: 1283 }, }, { - id: 2630, + id: '2630', from: 'ROV', to: 'PRG', attributes: { prediction: 2169 }, }, { - id: 2631, + id: '2631', from: 'LIN', to: 'PSR', attributes: { prediction: 2716 }, }, { - id: 2632, + id: '2632', from: 'PAP', to: 'PTP', attributes: { prediction: 2963 }, }, { - id: 2633, + id: '2633', from: 'SJU', to: 'PUJ', attributes: { prediction: 2697 }, }, { - id: 2634, + id: '2634', from: 'EWR', to: 'PVD', attributes: { prediction: 5933 }, }, { - id: 2635, + id: '2635', from: 'EMA', to: 'RAK', attributes: { prediction: 1586 }, }, { - id: 2636, + id: '2636', from: 'MDW', to: 'RNO', attributes: { prediction: 3453 }, }, { - id: 2637, + id: '2637', from: 'ACC', to: 'ROB', attributes: { prediction: 4206 }, }, { - id: 2638, + id: '2638', from: 'AMM', to: 'RUH', attributes: { prediction: 8945 }, }, { - id: 2639, + id: '2639', from: 'ATH', to: 'RYG', attributes: { prediction: 1435 }, }, { - id: 2640, + id: '2640', from: 'KHI', to: 'RYK', attributes: { prediction: 1608 }, }, { - id: 2641, + id: '2641', from: 'RLG', to: 'PMI', attributes: { prediction: 1102 }, }, { - id: 2642, + id: '2642', from: 'REC', to: 'PNZ', attributes: { prediction: 5570 }, }, { - id: 2643, + id: '2643', from: 'HGU', to: 'POM', attributes: { prediction: 5587 }, }, { - id: 2644, + id: '2644', from: 'HKN', to: 'POM', attributes: { prediction: 1068 }, }, { - id: 2645, + id: '2645', from: 'KUL', to: 'TRZ', attributes: { prediction: 7330 }, }, { - id: 2646, + id: '2646', from: 'BBU', to: 'TSF', attributes: { prediction: 3023 }, }, { - id: 2647, + id: '2647', from: 'ALC', to: 'SOU', attributes: { prediction: 2958 }, }, { - id: 2648, + id: '2648', from: 'GUM', to: 'SPN', attributes: { prediction: 3291 }, }, { - id: 2649, + id: '2649', from: 'KBP', to: 'SPU', attributes: { prediction: 1295 }, }, { - id: 2650, + id: '2650', from: 'RAO', to: 'SDU', attributes: { prediction: 3848 }, }, { - id: 2651, + id: '2651', from: 'DFW', to: 'SEA', attributes: { prediction: 44715 }, }, { - id: 2652, + id: '2652', from: 'KOA', to: 'SFO', attributes: { prediction: 10799 }, }, { - id: 2653, + id: '2653', from: 'MKE', to: 'SFO', attributes: { prediction: 7115 }, }, { - id: 2654, + id: '2654', from: 'CJB', to: 'SHJ', attributes: { prediction: 2554 }, }, { - id: 2655, + id: '2655', from: 'MXP', to: 'SIN', attributes: { prediction: 6621 }, }, { - id: 2656, + id: '2656', from: 'GYE', to: 'SJO', attributes: { prediction: 3062 }, }, { - id: 2657, + id: '2657', from: 'OBX', to: 'SKC', attributes: { prediction: 180 }, }, { - id: 2658, + id: '2658', from: 'CUN', to: 'SLC', attributes: { prediction: 2136 }, }, { - id: 2659, + id: '2659', from: 'DXB', to: 'SLL', attributes: { prediction: 851 }, }, { - id: 2660, + id: '2660', from: 'TNR', to: 'SMS', attributes: { prediction: 1785 }, }, { - id: 2661, + id: '2661', from: 'TFS', to: 'SNN', attributes: { prediction: 612 }, }, { - id: 2662, + id: '2662', from: 'BSX', to: 'SNW', attributes: { prediction: 36 }, }, { - id: 2663, + id: '2663', from: 'VLC', to: 'TSR', attributes: { prediction: 1408 }, }, { - id: 2664, + id: '2664', from: 'DFW', to: 'TXK', attributes: { prediction: 3499 }, }, { - id: 2665, + id: '2665', from: 'YVR', to: 'YCD', attributes: { prediction: 3587 }, }, { - id: 2666, + id: '2666', from: 'YYR', to: 'YDF', attributes: { prediction: 1461 }, }, { - id: 2667, + id: '2667', from: 'YZF', to: 'YEV', attributes: { prediction: 1086 }, }, { - id: 2668, + id: '2668', from: 'DUS', to: 'VOL', attributes: { prediction: 601 }, }, { - id: 2669, + id: '2669', from: 'CAG', to: 'VRN', attributes: { prediction: 7378 }, }, { - id: 2670, + id: '2670', from: 'TIA', to: 'VRN', attributes: { prediction: 7692 }, }, { - id: 2671, + id: '2671', from: 'LTN', to: 'WAT', attributes: { prediction: 3703 }, }, { - id: 2672, + id: '2672', from: 'VIE', to: 'WAW', attributes: { prediction: 13983 }, }, { - id: 2673, + id: '2673', from: 'YVP', to: 'XGR', attributes: { prediction: 196 }, }, { - id: 2674, + id: '2674', from: 'TXL', to: 'XRY', attributes: { prediction: 1412 }, }, { - id: 2675, + id: '2675', from: 'SHJ', to: 'SYZ', attributes: { prediction: 2073 }, }, { - id: 2676, + id: '2676', from: 'KWL', to: 'SZX', attributes: { prediction: 5983 }, }, { - id: 2677, + id: '2677', from: 'BGI', to: 'TAB', attributes: { prediction: 760 }, }, { - id: 2678, + id: '2678', from: 'SHA', to: 'TAO', attributes: { prediction: 20613 }, }, { - id: 2679, + id: '2679', from: 'IKA', to: 'TAS', attributes: { prediction: 598 }, }, { - id: 2680, + id: '2680', from: 'GRW', to: 'TER', attributes: { prediction: 1279 }, }, { - id: 2681, + id: '2681', from: 'RZR', to: 'THR', attributes: { prediction: 768 }, }, { - id: 2682, + id: '2682', from: 'KTS', to: 'TLA', attributes: { prediction: 0 }, }, { - id: 2683, + id: '2683', from: 'BEG', to: 'TLV', attributes: { prediction: 556 }, }, { - id: 2684, + id: '2684', from: 'SJO', to: 'TNO', attributes: { prediction: 695 }, }, { - id: 2685, + id: '2685', from: 'FTU', to: 'TNR', attributes: { prediction: 1761 }, }, { - id: 2686, + id: '2686', from: 'YCB', to: 'YCO', attributes: { prediction: 315 }, }, { - id: 2687, + id: '2687', from: 'CRL', to: 'TRN', attributes: { prediction: 2914 }, }, { - id: 2688, + id: '2688', from: 'CGN', to: 'TXL', attributes: { prediction: 68704 }, }, { - id: 2689, + id: '2689', from: 'HAK', to: 'TYN', attributes: { prediction: 3040 }, }, { - id: 2690, + id: '2690', from: 'SXF', to: 'VCE', attributes: { prediction: 7261 }, }, { - id: 2691, + id: '2691', from: 'CNF', to: 'VIX', attributes: { prediction: 14847 }, }, { - id: 2692, + id: '2692', from: 'KEF', to: 'YHZ', attributes: { prediction: 1278 }, }, { - id: 2693, + id: '2693', from: 'YQC', to: 'YKG', attributes: { prediction: 195 }, }, { - id: 2694, + id: '2694', from: 'YVZ', to: 'YPM', attributes: { prediction: 128 }, }, { - id: 2695, + id: '2695', from: 'YYZ', to: 'YQM', attributes: { prediction: 11791 }, }, { - id: 2696, + id: '2696', from: 'YYH', to: 'YHK', attributes: { prediction: 436 }, }, { - id: 2697, + id: '2697', from: 'LHR', to: 'YHZ', attributes: { prediction: 4804 }, }, { - id: 2698, + id: '2698', from: 'YDF', to: 'YJT', attributes: { prediction: 348 }, }, { - id: 2699, + id: '2699', from: 'SHE', to: 'YNJ', attributes: { prediction: 1559 }, }, { - id: 2700, + id: '2700', from: 'YGW', to: 'YPH', attributes: { prediction: 73 }, }, { - id: 2701, + id: '2701', from: 'PMI', to: 'AAL', attributes: { prediction: 447 }, }, { - id: 2702, + id: '2702', from: 'BKO', to: 'ABJ', attributes: { prediction: 137 }, }, { - id: 2703, + id: '2703', from: 'JER', to: 'ABZ', attributes: { prediction: 230 }, }, { - id: 2704, + id: '2704', from: 'BJL', to: 'ACC', attributes: { prediction: 2258 }, }, { - id: 2705, + id: '2705', from: 'FRA', to: 'ADA', attributes: { prediction: 988 }, }, { - id: 2706, + id: '2706', from: 'DEL', to: 'ADD', attributes: { prediction: 3706 }, }, { - id: 2707, + id: '2707', from: 'BBO', to: 'ADE', attributes: { prediction: 522 }, }, { - id: 2708, + id: '2708', from: 'AKL', to: 'ADL', attributes: { prediction: 2107 }, }, { - id: 2709, + id: '2709', from: 'MRV', to: 'AER', attributes: { prediction: 1385 }, }, { - id: 2710, + id: '2710', from: 'LUX', to: 'AGA', attributes: { prediction: 244 }, }, { - id: 2711, + id: '2711', from: 'YFB', to: 'YRT', attributes: { prediction: 952 }, }, { - id: 2712, + id: '2712', from: 'MAN', to: 'YVR', attributes: { prediction: 2680 }, }, { - id: 2713, + id: '2713', from: 'YYC', to: 'YXE', attributes: { prediction: 18215 }, }, { - id: 2714, + id: '2714', from: 'YLW', to: 'YXS', attributes: { prediction: 269 }, }, { - id: 2715, + id: '2715', from: 'YXJ', to: 'YYE', attributes: { prediction: 623 }, }, { - id: 2716, + id: '2716', from: 'PIE', to: 'YYZ', attributes: { prediction: 139 }, }, { - id: 2717, + id: '2717', from: 'YSB', to: 'YYZ', attributes: { prediction: 7934 }, }, { - id: 2718, + id: '2718', from: 'YZR', to: 'YYZ', attributes: { prediction: 1192 }, }, { - id: 2719, + id: '2719', from: 'AKL', to: 'ZQN', attributes: { prediction: 18073 }, }, { - id: 2720, + id: '2720', from: 'LPA', to: 'ZQW', attributes: { prediction: 704 }, }, { - id: 2721, + id: '2721', from: 'GRO', to: 'AHO', attributes: { prediction: 2179 }, }, { - id: 2722, + id: '2722', from: 'FMM', to: 'ALC', attributes: { prediction: 1903 }, }, { - id: 2723, + id: '2723', from: 'ALG', to: 'YUL', attributes: { prediction: 1614 }, }, { - id: 2724, + id: '2724', from: 'YUL', to: 'YVR', attributes: { prediction: 27290 }, }, { - id: 2725, + id: '2725', from: 'ABA', to: 'BAX', attributes: { prediction: 820 }, }, { - id: 2726, + id: '2726', from: 'TLV', to: 'BCN', attributes: { prediction: 4834 }, }, { - id: 2727, + id: '2727', from: 'SOU', to: 'AMS', attributes: { prediction: 3747 }, }, { - id: 2728, + id: '2728', from: 'TIP', to: 'AMS', attributes: { prediction: 3458 }, }, { - id: 2729, + id: '2729', from: 'BOO', to: 'ANX', attributes: { prediction: 595 }, }, { - id: 2730, + id: '2730', from: 'ATL', to: 'ATH', attributes: { prediction: 3747 }, }, { - id: 2731, + id: '2731', from: 'JTY', to: 'ATH', attributes: { prediction: 694 }, }, { - id: 2732, + id: '2732', from: 'TBS', to: 'ATH', attributes: { prediction: 407 }, }, { - id: 2733, + id: '2733', from: 'VCE', to: 'ATH', attributes: { prediction: 1227 }, }, { - id: 2734, + id: '2734', from: 'ABE', to: 'ATL', attributes: { prediction: 3075 }, }, { - id: 2735, + id: '2735', from: 'EVV', to: 'ATL', attributes: { prediction: 3342 }, }, { - id: 2736, + id: '2736', from: 'PHF', to: 'ATL', attributes: { prediction: 14276 }, }, { - id: 2737, + id: '2737', from: 'PRG', to: 'ATL', attributes: { prediction: 2826 }, }, { - id: 2738, + id: '2738', from: 'COK', to: 'AUH', attributes: { prediction: 6415 }, }, { - id: 2739, + id: '2739', from: 'EXT', to: 'AVN', attributes: { prediction: 215 }, }, { - id: 2740, + id: '2740', from: 'BJV', to: 'AYT', attributes: { prediction: 381 }, }, { - id: 2741, + id: '2741', from: 'FMO', to: 'AYT', attributes: { prediction: 467 }, }, { - id: 2742, + id: '2742', from: 'BOG', to: 'BAQ', attributes: { prediction: 34973 }, }, { - id: 2743, + id: '2743', from: 'GRU', to: 'BEL', attributes: { prediction: 11856 }, }, { - id: 2744, + id: '2744', from: 'AUC', to: 'BGA', attributes: { prediction: 308 }, }, { - id: 2745, + id: '2745', from: 'DXB', to: 'BLR', attributes: { prediction: 20201 }, }, { - id: 2746, + id: '2746', from: 'ZAG', to: 'BUD', attributes: { prediction: 1327 }, }, { - id: 2747, + id: '2747', from: 'JNB', to: 'BUQ', attributes: { prediction: 1179 }, }, { - id: 2748, + id: '2748', from: 'MRS', to: 'BVA', attributes: { prediction: 7356 }, }, { - id: 2749, + id: '2749', from: 'TZX', to: 'ECN', attributes: { prediction: 742 }, }, { - id: 2750, + id: '2750', from: 'IOM', to: 'EDI', attributes: { prediction: 848 }, }, { - id: 2751, + id: '2751', from: 'CWB', to: 'GIG', attributes: { prediction: 31241 }, }, { - id: 2752, + id: '2752', from: 'AKL', to: 'GIS', attributes: { prediction: 2553 }, }, { - id: 2753, + id: '2753', from: 'KWI', to: 'GOI', attributes: { prediction: 1157 }, }, { - id: 2754, + id: '2754', from: 'BGO', to: 'BLL', attributes: { prediction: 998 }, }, { - id: 2755, + id: '2755', from: 'RVK', to: 'BNN', attributes: { prediction: 345 }, }, { - id: 2756, + id: '2756', from: 'FLA', to: 'BOG', attributes: { prediction: 769 }, }, { - id: 2757, + id: '2757', from: 'LET', to: 'BOG', attributes: { prediction: 3999 }, }, { - id: 2758, + id: '2758', from: 'CCU', to: 'BOM', attributes: { prediction: 55817 }, }, { - id: 2759, + id: '2759', from: 'MXP', to: 'BPS', attributes: { prediction: 641 }, }, { - id: 2760, + id: '2760', from: 'EDI', to: 'BRE', attributes: { prediction: 2868 }, }, { - id: 2761, + id: '2761', from: 'FRA', to: 'BRE', attributes: { prediction: 20578 }, }, { - id: 2762, + id: '2762', from: 'HAJ', to: 'BRU', attributes: { prediction: 2489 }, }, { - id: 2763, + id: '2763', from: 'MRS', to: 'BRU', attributes: { prediction: 5273 }, }, { - id: 2764, + id: '2764', from: 'SXF', to: 'BRU', attributes: { prediction: 7045 }, }, { - id: 2765, + id: '2765', from: 'BGW', to: 'BSR', attributes: { prediction: 426 }, }, { - id: 2766, + id: '2766', from: 'TBS', to: 'HRK', attributes: { prediction: 164 }, }, { - id: 2767, + id: '2767', from: 'JED', to: 'IAD', attributes: { prediction: 1021 }, }, { - id: 2768, + id: '2768', from: 'MHT', to: 'IAD', attributes: { prediction: 3811 }, }, { - id: 2769, + id: '2769', from: 'SAN', to: 'IAD', attributes: { prediction: 14686 }, }, { - id: 2770, + id: '2770', from: 'BGF', to: 'DLA', attributes: { prediction: 978 }, }, { - id: 2771, + id: '2771', from: 'VLI', to: 'DLY', attributes: { prediction: 32 }, }, { - id: 2772, + id: '2772', from: 'LBD', to: 'DME', attributes: { prediction: 2761 }, }, { - id: 2773, + id: '2773', from: 'NVI', to: 'DME', attributes: { prediction: 864 }, }, { - id: 2774, + id: '2774', from: 'UUS', to: 'DME', attributes: { prediction: 6900 }, }, { - id: 2775, + id: '2775', from: 'BLR', to: 'CJB', attributes: { prediction: 4099 }, }, { - id: 2776, + id: '2776', from: 'MCO', to: 'CLE', attributes: { prediction: 7480 }, }, { - id: 2777, + id: '2777', from: 'ORF', to: 'CLE', attributes: { prediction: 2242 }, }, { - id: 2778, + id: '2778', from: 'LGW', to: 'CLT', attributes: { prediction: 6650 }, }, { - id: 2779, + id: '2779', from: 'SAV', to: 'CLT', attributes: { prediction: 11642 }, }, { - id: 2780, + id: '2780', from: 'IST', to: 'CMN', attributes: { prediction: 5918 }, }, { - id: 2781, + id: '2781', from: 'BMU', to: 'DPS', attributes: { prediction: 1393 }, }, { - id: 2782, + id: '2782', from: 'WRO', to: 'DSA', attributes: { prediction: 1291 }, }, { - id: 2783, + id: '2783', from: 'BNA', to: 'DTW', attributes: { prediction: 20408 }, }, { - id: 2784, + id: '2784', from: 'ZRH', to: 'CTA', attributes: { prediction: 1578 }, }, { - id: 2785, + id: '2785', from: 'MAD', to: 'CUN', attributes: { prediction: 4089 }, }, { - id: 2786, + id: '2786', from: 'MEM', to: 'DAL', attributes: { prediction: 3145 }, }, { - id: 2787, + id: '2787', from: 'CLE', to: 'DAY', attributes: { prediction: 3541 }, }, { - id: 2788, + id: '2788', from: 'SXF', to: 'DBV', attributes: { prediction: 1077 }, }, { - id: 2789, + id: '2789', from: 'ALB', to: 'DCA', attributes: { prediction: 4410 }, }, { - id: 2790, + id: '2790', from: 'BNA', to: 'DCA', attributes: { prediction: 7722 }, }, { - id: 2791, + id: '2791', from: 'JAX', to: 'DCA', attributes: { prediction: 8237 }, }, { - id: 2792, + id: '2792', from: 'BIL', to: 'DEN', attributes: { prediction: 11947 }, }, { - id: 2793, + id: '2793', from: 'BCN', to: 'DUB', attributes: { prediction: 8341 }, }, { - id: 2794, + id: '2794', from: 'JFK', to: 'DUS', attributes: { prediction: 6488 }, }, { - id: 2795, + id: '2795', from: 'ALG', to: 'DXB', attributes: { prediction: 2361 }, }, { - id: 2796, + id: '2796', from: 'FSZ', to: 'ICN', attributes: { prediction: 7911 }, }, { - id: 2797, + id: '2797', from: 'FUE', to: 'FMO', attributes: { prediction: 644 }, }, { - id: 2798, + id: '2798', from: 'CKY', to: 'FNA', attributes: { prediction: 137 }, }, { - id: 2799, + id: '2799', from: 'NBO', to: 'EBB', attributes: { prediction: 15647 }, }, { - id: 2800, + id: '2800', from: 'CPH', to: 'EDI', attributes: { prediction: 3839 }, }, { - id: 2801, + id: '2801', from: 'LGW', to: 'EGC', attributes: { prediction: 379 }, }, { - id: 2802, + id: '2802', from: 'AGP', to: 'EMA', attributes: { prediction: 11394 }, }, { - id: 2803, + id: '2803', from: 'PMY', to: 'EQS', attributes: { prediction: 300 }, }, { - id: 2804, + id: '2804', from: 'ERC', to: 'ESB', attributes: { prediction: 1876 }, }, { - id: 2805, + id: '2805', from: 'AZA', to: 'EUG', attributes: { prediction: 980 }, }, { - id: 2806, + id: '2806', from: 'BOO', to: 'EVE', attributes: { prediction: 1096 }, }, { - id: 2807, + id: '2807', from: 'AER', to: 'EVN', attributes: { prediction: 1345 }, }, { - id: 2808, + id: '2808', from: 'NRT', to: 'EWR', attributes: { prediction: 6151 }, }, { - id: 2809, + id: '2809', from: 'PMI', to: 'EXT', attributes: { prediction: 792 }, }, { - id: 2810, + id: '2810', from: 'CGN', to: 'FAO', attributes: { prediction: 2854 }, }, { - id: 2811, + id: '2811', from: 'CMN', to: 'FCO', attributes: { prediction: 8412 }, }, { - id: 2812, + id: '2812', from: 'TXL', to: 'FRL', attributes: { prediction: 1408 }, }, { - id: 2813, + id: '2813', from: 'OSS', to: 'FRU', attributes: { prediction: 4772 }, }, { - id: 2814, + id: '2814', from: 'TPE', to: 'ICN', attributes: { prediction: 46869 }, }, { - id: 2815, + id: '2815', from: 'MUC', to: 'IKA', attributes: { prediction: 909 }, }, { - id: 2816, + id: '2816', from: 'CPO', to: 'IQQ', attributes: { prediction: 1549 }, }, { - id: 2817, + id: '2817', from: 'BQL', to: 'ISA', attributes: { prediction: 146 }, }, { - id: 2818, + id: '2818', from: 'HKG', to: 'KHN', attributes: { prediction: 1228 }, }, { - id: 2819, + id: '2819', from: 'FEZ', to: 'HHN', attributes: { prediction: 1330 }, }, { - id: 2820, + id: '2820', from: 'SVD', to: 'GND', attributes: { prediction: 1351 }, }, { - id: 2821, + id: '2821', from: 'GRX', to: 'GRO', attributes: { prediction: 7546 }, }, { - id: 2822, + id: '2822', from: 'LHR', to: 'GRU', attributes: { prediction: 16357 }, }, { - id: 2823, + id: '2823', from: 'ALC', to: 'GSE', attributes: { prediction: 1914 }, }, { - id: 2824, + id: '2824', from: 'BOD', to: 'GVA', attributes: { prediction: 5826 }, }, { - id: 2825, + id: '2825', from: 'CDG', to: 'GVA', attributes: { prediction: 38417 }, }, { - id: 2826, + id: '2826', from: 'RIX', to: 'GVA', attributes: { prediction: 1351 }, }, { - id: 2827, + id: '2827', from: 'BSB', to: 'GYN', attributes: { prediction: 19135 }, }, { - id: 2828, + id: '2828', from: 'MUA', to: 'GZO', attributes: { prediction: 438 }, }, { - id: 2829, + id: '2829', from: 'MIR', to: 'HAJ', attributes: { prediction: 590 }, }, { - id: 2830, + id: '2830', from: 'VCE', to: 'HAJ', attributes: { prediction: 1458 }, }, { - id: 2831, + id: '2831', from: 'NHA', to: 'HAN', attributes: { prediction: 7148 }, }, { - id: 2832, + id: '2832', from: 'CGO', to: 'HKG', attributes: { prediction: 1473 }, }, { - id: 2833, + id: '2833', from: 'PUS', to: 'HKG', attributes: { prediction: 4047 }, }, { - id: 2834, + id: '2834', from: 'KUL', to: 'HKT', attributes: { prediction: 13818 }, }, { - id: 2835, + id: '2835', from: 'BOO', to: 'MJF', attributes: { prediction: 493 }, }, { - id: 2836, + id: '2836', from: 'FLL', to: 'MKE', attributes: { prediction: 559 }, }, { - id: 2837, + id: '2837', from: 'MAA', to: 'KWI', attributes: { prediction: 2874 }, }, { - id: 2838, + id: '2838', from: 'BOS', to: 'LAS', attributes: { prediction: 7684 }, }, { - id: 2839, + id: '2839', from: 'MFR', to: 'LAX', attributes: { prediction: 4462 }, }, { - id: 2840, + id: '2840', from: 'BAH', to: 'LCA', attributes: { prediction: 1637 }, }, { - id: 2841, + id: '2841', from: 'BBU', to: 'LCA', attributes: { prediction: 1519 }, }, { - id: 2842, + id: '2842', from: 'LHR', to: 'LCA', attributes: { prediction: 19284 }, }, { - id: 2843, + id: '2843', from: 'MEM', to: 'ICT', attributes: { prediction: 1941 }, }, { - id: 2844, + id: '2844', from: 'EVN', to: 'IKA', attributes: { prediction: 1160 }, }, { - id: 2845, + id: '2845', from: 'BOS', to: 'IND', attributes: { prediction: 3705 }, }, { - id: 2846, + id: '2846', from: 'SAT', to: 'IND', attributes: { prediction: 46 }, }, { - id: 2847, + id: '2847', from: 'SSA', to: 'IOS', attributes: { prediction: 5875 }, }, { - id: 2848, + id: '2848', from: 'MUX', to: 'ISB', attributes: { prediction: 755 }, }, { - id: 2849, + id: '2849', from: 'DEL', to: 'IST', attributes: { prediction: 6005 }, }, { - id: 2850, + id: '2850', from: 'KIX', to: 'IST', attributes: { prediction: 2861 }, }, { - id: 2851, + id: '2851', from: 'SIC', to: 'IST', attributes: { prediction: 3780 }, }, { - id: 2852, + id: '2852', from: 'MDE', to: 'JFK', attributes: { prediction: 2772 }, }, { - id: 2853, + id: '2853', from: 'PMO', to: 'JFK', attributes: { prediction: 663 }, }, { - id: 2854, + id: '2854', from: 'YHZ', to: 'JFK', attributes: { prediction: 3062 }, }, { - id: 2855, + id: '2855', from: 'JEG', to: 'JHS', attributes: { prediction: 651 }, }, { - id: 2856, + id: '2856', from: 'FAO', to: 'LDY', attributes: { prediction: 1213 }, }, { - id: 2857, + id: '2857', from: 'KCL', to: 'KCQ', attributes: { prediction: 10 }, }, { - id: 2858, + id: '2858', from: 'JFK', to: 'KEF', attributes: { prediction: 4536 }, }, { - id: 2859, + id: '2859', from: 'LED', to: 'KGD', attributes: { prediction: 12800 }, }, { - id: 2860, + id: '2860', from: 'HAM', to: 'KGS', attributes: { prediction: 2107 }, }, { - id: 2861, + id: '2861', from: 'NAS', to: 'KIN', attributes: { prediction: 1716 }, }, { - id: 2862, + id: '2862', from: 'ALC', to: 'KIR', attributes: { prediction: 1147 }, }, { - id: 2863, + id: '2863', from: 'PRG', to: 'KIV', attributes: { prediction: 337 }, }, { - id: 2864, + id: '2864', from: 'MMY', to: 'KIX', attributes: { prediction: 3550 }, }, { - id: 2865, + id: '2865', from: 'MRV', to: 'KJA', attributes: { prediction: 656 }, }, { - id: 2866, + id: '2866', from: 'OKA', to: 'KMQ', attributes: { prediction: 2900 }, }, { - id: 2867, + id: '2867', from: 'GEA', to: 'KNQ', attributes: { prediction: 384 }, }, { - id: 2868, + id: '2868', from: 'BGO', to: 'KRS', attributes: { prediction: 4488 }, }, { - id: 2869, + id: '2869', from: 'DYU', to: 'KUF', attributes: { prediction: 360 }, }, { - id: 2870, + id: '2870', from: 'BKI', to: 'KUL', attributes: { prediction: 82660 }, }, { - id: 2871, + id: '2871', from: 'ICN', to: 'KUL', attributes: { prediction: 9726 }, }, { - id: 2872, + id: '2872', from: 'SVX', to: 'LED', attributes: { prediction: 10562 }, }, { - id: 2873, + id: '2873', from: 'SJU', to: 'LGA', attributes: { prediction: 108 }, }, { - id: 2874, + id: '2874', from: 'BDA', to: 'LGW', attributes: { prediction: 6720 }, }, { - id: 2875, + id: '2875', from: 'MLE', to: 'LGW', attributes: { prediction: 4976 }, }, { - id: 2876, + id: '2876', from: 'ORK', to: 'LGW', attributes: { prediction: 4605 }, }, { - id: 2877, + id: '2877', from: 'OTP', to: 'LGW', attributes: { prediction: 4318 }, }, { - id: 2878, + id: '2878', from: 'DUB', to: 'NOC', attributes: { prediction: 1159 }, }, { - id: 2879, + id: '2879', from: 'GRO', to: 'NRN', attributes: { prediction: 7068 }, }, { - id: 2880, + id: '2880', from: 'BUD', to: 'MAD', attributes: { prediction: 6541 }, }, { - id: 2881, + id: '2881', from: 'MUC', to: 'MAD', attributes: { prediction: 25519 }, }, { - id: 2882, + id: '2882', from: 'BEL', to: 'MAO', attributes: { prediction: 12290 }, }, { - id: 2883, + id: '2883', from: 'MSP', to: 'MBS', attributes: { prediction: 1211 }, }, { - id: 2884, + id: '2884', from: 'CVG', to: 'MCI', attributes: { prediction: 3170 }, }, { - id: 2885, + id: '2885', from: 'DTW', to: 'MCI', attributes: { prediction: 9013 }, }, { - id: 2886, + id: '2886', from: 'SNN', to: 'LGW', attributes: { prediction: 9346 }, }, { - id: 2887, + id: '2887', from: 'AUH', to: 'LHE', attributes: { prediction: 9032 }, }, { - id: 2888, + id: '2888', from: 'HEL', to: 'LHR', attributes: { prediction: 29960 }, }, { - id: 2889, + id: '2889', from: 'BIA', to: 'LIL', attributes: { prediction: 535 }, }, { - id: 2890, + id: '2890', from: 'LHR', to: 'LIN', attributes: { prediction: 25169 }, }, { - id: 2891, + id: '2891', from: 'FON', to: 'LIR', attributes: { prediction: 151 }, }, { - id: 2892, + id: '2892', from: 'HAM', to: 'LIS', attributes: { prediction: 6052 }, }, { - id: 2893, + id: '2893', from: 'PDL', to: 'LIS', attributes: { prediction: 17268 }, }, { - id: 2894, + id: '2894', from: 'TMS', to: 'LIS', attributes: { prediction: 1880 }, }, { - id: 2895, + id: '2895', from: 'ALF', to: 'LKL', attributes: { prediction: 435 }, }, { - id: 2896, + id: '2896', from: 'PAD', to: 'LPA', attributes: { prediction: 1951 }, }, { - id: 2897, + id: '2897', from: 'CCF', to: 'LPL', attributes: { prediction: 1392 }, }, { - id: 2898, + id: '2898', from: 'OMA', to: 'LRD', attributes: { prediction: 107 }, }, { - id: 2899, + id: '2899', from: 'PHF', to: 'MCO', attributes: { prediction: 2650 }, }, { - id: 2900, + id: '2900', from: 'KHI', to: 'MCT', attributes: { prediction: 7920 }, }, { - id: 2901, + id: '2901', from: 'RUH', to: 'MCT', attributes: { prediction: 3955 }, }, { - id: 2902, + id: '2902', from: 'CHC', to: 'MEL', attributes: { prediction: 10108 }, }, { - id: 2903, + id: '2903', from: 'PHL', to: 'MEM', attributes: { prediction: 3282 }, }, { - id: 2904, + id: '2904', from: 'YVR', to: 'NRT', attributes: { prediction: 21763 }, }, { - id: 2905, + id: '2905', from: 'ADB', to: 'NUE', attributes: { prediction: 1212 }, }, { - id: 2906, + id: '2906', from: 'AMS', to: 'NUE', attributes: { prediction: 7746 }, }, { - id: 2907, + id: '2907', from: 'CGN', to: 'NUE', attributes: { prediction: 706 }, }, { - id: 2908, + id: '2908', from: 'HNL', to: 'OGG', attributes: { prediction: 62209 }, }, { - id: 2909, + id: '2909', from: 'HEL', to: 'MUC', attributes: { prediction: 16663 }, }, { - id: 2910, + id: '2910', from: 'SKG', to: 'MUC', attributes: { prediction: 11297 }, }, { - id: 2911, + id: '2911', from: 'THR', to: 'MHD', attributes: { prediction: 60586 }, }, { - id: 2912, + id: '2912', from: 'CFG', to: 'MIA', attributes: { prediction: 454 }, }, { - id: 2913, + id: '2913', from: 'TPA', to: 'MIA', attributes: { prediction: 17327 }, }, { - id: 2914, + id: '2914', from: 'TSF', to: 'MLA', attributes: { prediction: 1862 }, }, { - id: 2915, + id: '2915', from: 'ATL', to: 'MLB', attributes: { prediction: 7575 }, }, { - id: 2916, + id: '2916', from: 'HND', to: 'MMY', attributes: { prediction: 2285 }, }, { - id: 2917, + id: '2917', from: 'AMS', to: 'MNL', attributes: { prediction: 8591 }, }, { - id: 2918, + id: '2918', from: 'CYP', to: 'MNL', attributes: { prediction: 1706 }, }, { - id: 2919, + id: '2919', from: 'GUM', to: 'MNL', attributes: { prediction: 6196 }, }, { - id: 2920, + id: '2920', from: 'MKE', to: 'MQT', attributes: { prediction: 1079 }, }, { - id: 2921, + id: '2921', from: 'CDG', to: 'MRS', attributes: { prediction: 32401 }, }, { - id: 2922, + id: '2922', from: 'DUB', to: 'MRS', attributes: { prediction: 1186 }, }, { - id: 2923, + id: '2923', from: 'DVL', to: 'MSP', attributes: { prediction: 792 }, }, { - id: 2924, + id: '2924', from: 'LGA', to: 'MSP', attributes: { prediction: 23060 }, }, { - id: 2925, + id: '2925', from: 'MEM', to: 'MSP', attributes: { prediction: 15652 }, }, { - id: 2926, + id: '2926', from: 'TLL', to: 'MSQ', attributes: { prediction: 361 }, }, { - id: 2927, + id: '2927', from: 'VIE', to: 'MSQ', attributes: { prediction: 5023 }, }, { - id: 2928, + id: '2928', from: 'LAS', to: 'MSY', attributes: { prediction: 5671 }, }, { - id: 2929, + id: '2929', from: 'ACE', to: 'MUC', attributes: { prediction: 598 }, }, { - id: 2930, + id: '2930', from: 'RIX', to: 'MXP', attributes: { prediction: 4138 }, }, { - id: 2931, + id: '2931', from: 'TIP', to: 'MXP', attributes: { prediction: 1393 }, }, { - id: 2932, + id: '2932', from: 'LWY', to: 'MYY', attributes: { prediction: 1409 }, }, { - id: 2933, + id: '2933', from: 'MXP', to: 'NAP', attributes: { prediction: 33780 }, }, { - id: 2934, + id: '2934', from: 'VBS', to: 'NAP', attributes: { prediction: 3323 }, }, { - id: 2935, + id: '2935', from: 'LIL', to: 'OPO', attributes: { prediction: 1698 }, }, { - id: 2936, + id: '2936', from: 'CDG', to: 'VCE', attributes: { prediction: 32565 }, }, { - id: 2937, + id: '2937', from: 'DEL', to: 'VIE', attributes: { prediction: 5523 }, }, { - id: 2938, + id: '2938', from: 'FNC', to: 'VIE', attributes: { prediction: 561 }, }, { - id: 2939, + id: '2939', from: 'TFS', to: 'VIE', attributes: { prediction: 1287 }, }, { - id: 2940, + id: '2940', from: 'ONT', to: 'OAK', attributes: { prediction: 22385 }, }, { - id: 2941, + id: '2941', from: 'IST', to: 'ODS', attributes: { prediction: 3697 }, }, { - id: 2942, + id: '2942', from: 'BEG', to: 'OHD', attributes: { prediction: 434 }, }, { - id: 2943, + id: '2943', from: 'NRT', to: 'OKA', attributes: { prediction: 9007 }, }, { - id: 2944, + id: '2944', from: 'GAM', to: 'OME', attributes: { prediction: 335 }, }, { - id: 2945, + id: '2945', from: 'NRT', to: 'OOL', attributes: { prediction: 5505 }, }, { - id: 2946, + id: '2946', from: 'ARN', to: 'ORD', attributes: { prediction: 6508 }, }, { - id: 2947, + id: '2947', from: 'CAE', to: 'ORD', attributes: { prediction: 3487 }, }, { - id: 2948, + id: '2948', from: 'PIT', to: 'ORD', attributes: { prediction: 22256 }, }, { - id: 2949, + id: '2949', from: 'PRG', to: 'OSR', attributes: { prediction: 5240 }, }, { - id: 2950, + id: '2950', from: 'MUC', to: 'OTP', attributes: { prediction: 17321 }, }, { - id: 2951, + id: '2951', from: 'SFB', to: 'OWB', attributes: { prediction: 1238 }, }, { - id: 2952, + id: '2952', from: 'NRN', to: 'VLL', attributes: { prediction: 1804 }, }, { - id: 2953, + id: '2953', from: 'HJR', to: 'VNS', attributes: { prediction: 3447 }, }, { - id: 2954, + id: '2954', from: 'LIS', to: 'VRL', attributes: { prediction: 505 }, }, { - id: 2955, + id: '2955', from: 'TSR', to: 'VRN', attributes: { prediction: 899 }, }, { - id: 2956, + id: '2956', from: 'YYY', to: 'YUL', attributes: { prediction: 900 }, }, { - id: 2957, + id: '2957', from: 'YQB', to: 'YVB', attributes: { prediction: 91 }, }, { - id: 2958, + id: '2958', from: 'YOW', to: 'YVR', attributes: { prediction: 16189 }, }, { - id: 2959, + id: '2959', from: 'YXE', to: 'YWG', attributes: { prediction: 2101 }, }, { - id: 2960, + id: '2960', from: 'FSP', to: 'YYT', attributes: { prediction: 251 }, }, { - id: 2961, + id: '2961', from: 'YUL', to: 'YYY', attributes: { prediction: 845 }, }, { - id: 2962, + id: '2962', from: 'BNA', to: 'YYZ', attributes: { prediction: 2071 }, }, { - id: 2963, + id: '2963', from: 'MIA', to: 'YYZ', attributes: { prediction: 15992 }, }, { - id: 2964, + id: '2964', from: 'NRT', to: 'YYZ', attributes: { prediction: 8659 }, }, { - id: 2965, + id: '2965', from: 'SKB', to: 'YYZ', attributes: { prediction: 811 }, }, { - id: 2966, + id: '2966', from: 'TPE', to: 'SHE', attributes: { prediction: 1905 }, }, { - id: 2967, + id: '2967', from: 'BDO', to: 'SIN', attributes: { prediction: 3032 }, }, { - id: 2968, + id: '2968', from: 'LED', to: 'SIP', attributes: { prediction: 2625 }, }, { - id: 2969, + id: '2969', from: 'PLS', to: 'POP', attributes: { prediction: 145 }, }, { - id: 2970, + id: '2970', from: 'STN', to: 'POZ', attributes: { prediction: 5149 }, }, { - id: 2971, + id: '2971', from: 'NDY', to: 'PPW', attributes: { prediction: 17 }, }, { - id: 2972, + id: '2972', from: 'FRA', to: 'PRG', attributes: { prediction: 28046 }, }, { - id: 2973, + id: '2973', from: 'LBA', to: 'PRG', attributes: { prediction: 2192 }, }, { - id: 2974, + id: '2974', from: 'MVD', to: 'PTY', attributes: { prediction: 2420 }, }, { - id: 2975, + id: '2975', from: 'CLO', to: 'PUU', attributes: { prediction: 364 }, }, { - id: 2976, + id: '2976', from: 'CVG', to: 'PVD', attributes: { prediction: 141 }, }, { - id: 2977, + id: '2977', from: 'SPN', to: 'PVG', attributes: { prediction: 1750 }, }, { - id: 2978, + id: '2978', from: 'TPE', to: 'PVG', attributes: { prediction: 34751 }, }, { - id: 2979, + id: '2979', from: 'IAH', to: 'PVR', attributes: { prediction: 4539 }, }, { - id: 2980, + id: '2980', from: 'BOM', to: 'RAJ', attributes: { prediction: 6200 }, }, { - id: 2981, + id: '2981', from: 'SEA', to: 'RNO', attributes: { prediction: 7747 }, }, { - id: 2982, + id: '2982', from: 'CHC', to: 'ROT', attributes: { prediction: 3684 }, }, { - id: 2983, + id: '2983', from: 'ORD', to: 'RSW', attributes: { prediction: 10048 }, }, { - id: 2984, + id: '2984', from: 'ALY', to: 'RUH', attributes: { prediction: 2069 }, }, { - id: 2985, + id: '2985', from: 'KMC', to: 'RUH', attributes: { prediction: 401 }, }, { - id: 2986, + id: '2986', from: 'KHI', to: 'RYK', attributes: { prediction: 2001 }, }, { - id: 2987, + id: '2987', from: 'AMM', to: 'SAH', attributes: { prediction: 2303 }, }, { - id: 2988, + id: '2988', from: 'MIA', to: 'SAV', attributes: { prediction: 1060 }, }, { - id: 2989, + id: '2989', from: 'ZRH', to: 'SAW', attributes: { prediction: 3930 }, }, { - id: 2990, + id: '2990', from: 'LAS', to: 'SBN', attributes: { prediction: 1141 }, }, { - id: 2991, + id: '2991', from: 'MUC', to: 'SCN', attributes: { prediction: 4301 }, }, { - id: 2992, + id: '2992', from: 'ITM', to: 'SDJ', attributes: { prediction: 34777 }, }, { - id: 2993, + id: '2993', from: 'BOG', to: 'SDQ', attributes: { prediction: 1316 }, }, { - id: 2994, + id: '2994', from: 'BOS', to: 'SDQ', attributes: { prediction: 2054 }, }, { - id: 2995, + id: '2995', from: 'CCS', to: 'SDQ', attributes: { prediction: 3561 }, }, { - id: 2996, + id: '2996', from: 'LAX', to: 'SGF', attributes: { prediction: 1033 }, }, { - id: 2997, + id: '2997', from: 'HAJ', to: 'STN', attributes: { prediction: 6971 }, }, { - id: 2998, + id: '2998', from: 'YQB', to: 'YZV', attributes: { prediction: 2407 }, }, { - id: 2999, + id: '2999', from: 'CHC', to: 'ZQN', attributes: { prediction: 8465 }, }, { - id: 3000, + id: '3000', from: 'BDS', to: 'ZRH', attributes: { prediction: 589 }, }, { - id: 3001, + id: '3001', from: 'CAI', to: 'ZRH', attributes: { prediction: 4920 }, }, { - id: 3002, + id: '3002', from: 'STN', to: 'TIA', attributes: { prediction: 3790 }, }, { - id: 3003, + id: '3003', from: 'SVO', to: 'TJM', attributes: { prediction: 4168 }, }, { - id: 3004, + id: '3004', from: 'CDG', to: 'TLV', attributes: { prediction: 27859 }, }, { - id: 3005, + id: '3005', from: 'CGN', to: 'TLV', attributes: { prediction: 1965 }, }, { - id: 3006, + id: '3006', from: 'TRD', to: 'STN', attributes: { prediction: 2100 }, }, { - id: 3007, + id: '3007', from: 'BOJ', to: 'STR', attributes: { prediction: 403 }, }, { - id: 3008, + id: '3008', from: 'TRF', to: 'SVG', attributes: { prediction: 4708 }, }, { - id: 3009, + id: '3009', from: 'LEI', to: 'SVQ', attributes: { prediction: 1795 }, }, { - id: 3010, + id: '3010', from: 'IST', to: 'SVX', attributes: { prediction: 3617 }, }, { - id: 3011, + id: '3011', from: 'GVA', to: 'SXF', attributes: { prediction: 4075 }, }, { - id: 3012, + id: '3012', from: 'NRN', to: 'SXF', attributes: { prediction: 11340 }, }, { - id: 3013, + id: '3013', from: 'SAW', to: 'SXF', attributes: { prediction: 9048 }, }, { - id: 3014, + id: '3014', from: 'AMS', to: 'SXM', attributes: { prediction: 3381 }, }, { - id: 3015, + id: '3015', from: 'BHS', to: 'SYD', attributes: { prediction: 1439 }, }, { - id: 3016, + id: '3016', from: 'PEK', to: 'SZX', attributes: { prediction: 105881 }, }, { - id: 3017, + id: '3017', from: 'POS', to: 'TAB', attributes: { prediction: 10798 }, }, { - id: 3018, + id: '3018', from: 'IST', to: 'TBS', attributes: { prediction: 7307 }, }, { - id: 3019, + id: '3019', from: 'FMO', to: 'TFS', attributes: { prediction: 934 }, }, { - id: 3020, + id: '3020', from: 'LCG', to: 'TFS', attributes: { prediction: 413 }, }, { - id: 3021, + id: '3021', from: 'ELU', to: 'TGR', attributes: { prediction: 235 }, }, { - id: 3022, + id: '3022', from: 'CRL', to: 'TPS', attributes: { prediction: 2661 }, }, { - id: 3023, + id: '3023', from: 'COL', to: 'TRE', attributes: { prediction: 27 }, }, { - id: 3024, + id: '3024', from: 'TLV', to: 'ZRH', attributes: { prediction: 16852 }, }, { - id: 3025, + id: '3025', from: 'NVK', to: 'ANX', attributes: { prediction: 261 }, }, { - id: 3026, + id: '3026', from: 'AYT', to: 'ARN', attributes: { prediction: 452 }, }, { - id: 3027, + id: '3027', from: 'UME', to: 'ARN', attributes: { prediction: 12371 }, }, { - id: 3028, + id: '3028', from: 'BKK', to: 'ASB', attributes: { prediction: 1227 }, }, { - id: 3029, + id: '3029', from: 'CAE', to: 'ATL', attributes: { prediction: 11751 }, }, { - id: 3030, + id: '3030', from: 'BVA', to: 'WRO', attributes: { prediction: 1319 }, }, { - id: 3031, + id: '3031', from: 'AKN', to: 'WSN', attributes: { prediction: 9 }, }, { - id: 3032, + id: '3032', from: 'KKA', to: 'UNK', attributes: { prediction: 103 }, }, { - id: 3033, + id: '3033', from: 'LYS', to: 'VIE', attributes: { prediction: 5777 }, }, { - id: 3034, + id: '3034', from: 'SJU', to: 'VIJ', attributes: { prediction: 777 }, }, { - id: 3035, + id: '3035', from: 'LNB', to: 'VLI', attributes: { prediction: 88 }, }, { - id: 3036, + id: '3036', from: 'STN', to: 'VLL', attributes: { prediction: 2219 }, }, { - id: 3037, + id: '3037', from: 'PTY', to: 'VLN', attributes: { prediction: 1879 }, }, { - id: 3038, + id: '3038', from: 'LMC', to: 'VVC', attributes: { prediction: 169 }, }, { - id: 3039, + id: '3039', from: 'CMN', to: 'WAW', attributes: { prediction: 1154 }, }, { - id: 3040, + id: '3040', from: 'GVA', to: 'WAW', attributes: { prediction: 1987 }, }, { - id: 3041, + id: '3041', from: 'DUS', to: 'XRY', attributes: { prediction: 3779 }, }, { - id: 3042, + id: '3042', from: 'YQT', to: 'YAG', attributes: { prediction: 611 }, }, { - id: 3043, + id: '3043', from: 'ROR', to: 'YAP', attributes: { prediction: 500 }, }, { - id: 3044, + id: '3044', from: 'TUN', to: 'AMS', attributes: { prediction: 872 }, }, { - id: 3045, + id: '3045', from: 'KGX', to: 'ANV', attributes: { prediction: 64 }, }, { - id: 3046, + id: '3046', from: 'BNE', to: 'APW', attributes: { prediction: 470 }, }, { - id: 3047, + id: '3047', from: 'FRA', to: 'YYC', attributes: { prediction: 13995 }, }, { - id: 3048, + id: '3048', from: 'MCO', to: 'YYC', attributes: { prediction: 374 }, }, { - id: 3049, + id: '3049', from: 'LIS', to: 'YYZ', attributes: { prediction: 1649 }, }, { - id: 3050, + id: '3050', from: 'SPU', to: 'ZAG', attributes: { prediction: 10140 }, }, { - id: 3051, + id: '3051', from: 'RHO', to: 'ZQW', attributes: { prediction: 665 }, }, { - id: 3052, + id: '3052', from: 'CPH', to: 'ZRH', attributes: { prediction: 23377 }, }, { - id: 3053, + id: '3053', from: 'DOH', to: 'ZRH', attributes: { prediction: 5055 }, }, { - id: 3054, + id: '3054', from: 'GOT', to: 'ZRH', attributes: { prediction: 832 }, }, { - id: 3055, + id: '3055', from: 'LAX', to: 'ZRH', attributes: { prediction: 5788 }, }, { - id: 3056, + id: '3056', from: 'NBO', to: 'ZRH', attributes: { prediction: 5108 }, }, { - id: 3057, + id: '3057', from: 'RTM', to: 'AYT', attributes: { prediction: 472 }, }, { - id: 3058, + id: '3058', from: 'DXB', to: 'BAH', attributes: { prediction: 66757 }, }, { - id: 3059, + id: '3059', from: 'OTP', to: 'BCM', attributes: { prediction: 658 }, }, { - id: 3060, + id: '3060', from: 'DFW', to: 'ABQ', attributes: { prediction: 29605 }, }, { - id: 3061, + id: '3061', from: 'HAJ', to: 'ADB', attributes: { prediction: 1785 }, }, { - id: 3062, + id: '3062', from: 'MQX', to: 'ADD', attributes: { prediction: 4793 }, }, { - id: 3063, + id: '3063', from: 'PIK', to: 'AGP', attributes: { prediction: 2696 }, }, { - id: 3064, + id: '3064', from: 'PMI', to: 'AGP', attributes: { prediction: 15033 }, }, { - id: 3065, + id: '3065', from: 'LGW', to: 'AJA', attributes: { prediction: 114 }, }, { - id: 3066, + id: '3066', from: 'GOT', to: 'ALC', attributes: { prediction: 772 }, }, { - id: 3067, + id: '3067', from: 'ETZ', to: 'ALG', attributes: { prediction: 1287 }, }, { - id: 3068, + id: '3068', from: 'PMI', to: 'ALG', attributes: { prediction: 987 }, }, { - id: 3069, + id: '3069', from: 'DUS', to: 'AMS', attributes: { prediction: 7837 }, }, { - id: 3070, + id: '3070', from: 'KAN', to: 'AMS', attributes: { prediction: 2238 }, }, { - id: 3071, + id: '3071', from: 'SAH', to: 'BEY', attributes: { prediction: 707 }, }, { - id: 3072, + id: '3072', from: 'MAN', to: 'BFS', attributes: { prediction: 6365 }, }, { - id: 3073, + id: '3073', from: 'FRA', to: 'BGO', attributes: { prediction: 4258 }, }, { - id: 3074, + id: '3074', from: 'GRO', to: 'BGY', attributes: { prediction: 13415 }, }, { - id: 3075, + id: '3075', from: 'IAH', to: 'BHM', attributes: { prediction: 8769 }, }, { - id: 3076, + id: '3076', from: 'ORY', to: 'BIA', attributes: { prediction: 16482 }, }, { - id: 3077, + id: '3077', from: 'DEN', to: 'BIS', attributes: { prediction: 4330 }, }, { - id: 3078, + id: '3078', from: 'CPH', to: 'BKK', attributes: { prediction: 15001 }, }, { - id: 3079, + id: '3079', from: 'DXB', to: 'AUH', attributes: { prediction: 3461 }, }, { - id: 3080, + id: '3080', from: 'ECN', to: 'AYT', attributes: { prediction: 1670 }, }, { - id: 3081, + id: '3081', from: 'ALP', to: 'BAH', attributes: { prediction: 1046 }, }, { - id: 3082, + id: '3082', from: 'SYZ', to: 'BAH', attributes: { prediction: 3026 }, }, { - id: 3083, + id: '3083', from: 'AMM', to: 'BCN', attributes: { prediction: 835 }, }, { - id: 3084, + id: '3084', from: 'BRU', to: 'CMN', attributes: { prediction: 7142 }, }, { - id: 3085, + id: '3085', from: 'IOS', to: 'CNF', attributes: { prediction: 2920 }, }, { - id: 3086, + id: '3086', from: 'BLR', to: 'COK', attributes: { prediction: 12605 }, }, { - id: 3087, + id: '3087', from: 'BLQ', to: 'BOD', attributes: { prediction: 2202 }, }, { - id: 3088, + id: '3088', from: 'BBI', to: 'BOM', attributes: { prediction: 7013 }, }, { - id: 3089, + id: '3089', from: 'RET', to: 'BOO', attributes: { prediction: 295 }, }, { - id: 3090, + id: '3090', from: 'LHR', to: 'BOS', attributes: { prediction: 38162 }, }, { - id: 3091, + id: '3091', from: 'PIT', to: 'BOS', attributes: { prediction: 13096 }, }, { - id: 3092, + id: '3092', from: 'BHD', to: 'BRS', attributes: { prediction: 11144 }, }, { - id: 3093, + id: '3093', from: 'CPH', to: 'BRU', attributes: { prediction: 24249 }, }, { - id: 3094, + id: '3094', from: 'CTA', to: 'BSL', attributes: { prediction: 838 }, }, { - id: 3095, + id: '3095', from: 'NAP', to: 'BSL', attributes: { prediction: 1966 }, }, { - id: 3096, + id: '3096', from: 'TLV', to: 'BTS', attributes: { prediction: 1757 }, }, { - id: 3097, + id: '3097', from: 'DTM', to: 'BUD', attributes: { prediction: 1979 }, }, { - id: 3098, + id: '3098', from: 'AGP', to: 'CPH', attributes: { prediction: 12451 }, }, { - id: 3099, + id: '3099', from: 'MSP', to: 'BZN', attributes: { prediction: 6283 }, }, { - id: 3100, + id: '3100', from: 'SGN', to: 'CAH', attributes: { prediction: 1104 }, }, { - id: 3101, + id: '3101', from: 'KRT', to: 'CAI', attributes: { prediction: 22899 }, }, { - id: 3102, + id: '3102', from: 'TLV', to: 'CAI', attributes: { prediction: 3096 }, }, { - id: 3103, + id: '3103', from: 'VVI', to: 'CBB', attributes: { prediction: 13959 }, }, { - id: 3104, + id: '3104', from: 'HEA', to: 'CCN', attributes: { prediction: 82 }, }, { - id: 3105, + id: '3105', from: 'PBH', to: 'CCU', attributes: { prediction: 1079 }, }, { - id: 3106, + id: '3106', from: 'NAP', to: 'CDG', attributes: { prediction: 4552 }, }, { - id: 3107, + id: '3107', from: 'NCE', to: 'CDG', attributes: { prediction: 40980 }, }, { - id: 3108, + id: '3108', from: 'SGN', to: 'CDG', attributes: { prediction: 4216 }, }, { - id: 3109, + id: '3109', from: 'TNR', to: 'CDG', attributes: { prediction: 4045 }, }, { - id: 3110, + id: '3110', from: 'PSA', to: 'CGN', attributes: { prediction: 1842 }, }, { - id: 3111, + id: '3111', from: 'DFW', to: 'CHA', attributes: { prediction: 1152 }, }, { - id: 3112, + id: '3112', from: 'NAN', to: 'CHC', attributes: { prediction: 510 }, }, { - id: 3113, + id: '3113', from: 'WVB', to: 'CPT', attributes: { prediction: 1321 }, }, { - id: 3114, + id: '3114', from: 'MNL', to: 'CRM', attributes: { prediction: 2453 }, }, { - id: 3115, + id: '3115', from: 'SUB', to: 'BDJ', attributes: { prediction: 29897 }, }, { - id: 3116, + id: '3116', from: 'SHJ', to: 'BEY', attributes: { prediction: 5331 }, }, { - id: 3117, + id: '3117', from: 'HKT', to: 'DMK', attributes: { prediction: 9256 }, }, { - id: 3118, + id: '3118', from: 'DAC', to: 'DMM', attributes: { prediction: 776 }, }, { - id: 3119, + id: '3119', from: 'LOS', to: 'DOH', attributes: { prediction: 6109 }, }, { - id: 3120, + id: '3120', from: 'MKE', to: 'DTW', attributes: { prediction: 15695 }, }, { - id: 3121, + id: '3121', from: 'BOM', to: 'DAC', attributes: { prediction: 8088 }, }, { - id: 3122, + id: '3122', from: 'LCA', to: 'DAM', attributes: { prediction: 1316 }, }, { - id: 3123, + id: '3123', from: 'HAH', to: 'DAR', attributes: { prediction: 1536 }, }, { - id: 3124, + id: '3124', from: 'SYR', to: 'DAY', attributes: { prediction: 88 }, }, { - id: 3125, + id: '3125', from: 'GOP', to: 'DEL', attributes: { prediction: 818 }, }, { - id: 3126, + id: '3126', from: 'RUH', to: 'DEL', attributes: { prediction: 3948 }, }, { - id: 3127, + id: '3127', from: 'DSM', to: 'DEN', attributes: { prediction: 12295 }, }, { - id: 3128, + id: '3128', from: 'FLL', to: 'DEN', attributes: { prediction: 10529 }, }, { - id: 3129, + id: '3129', from: 'IDA', to: 'DEN', attributes: { prediction: 3378 }, }, { - id: 3130, + id: '3130', from: 'YQR', to: 'DEN', attributes: { prediction: 1899 }, }, { - id: 3131, + id: '3131', from: 'BNA', to: 'DFW', attributes: { prediction: 28451 }, }, { - id: 3132, + id: '3132', from: 'EVV', to: 'DFW', attributes: { prediction: 1213 }, }, { - id: 3133, + id: '3133', from: 'EWR', to: 'DUS', attributes: { prediction: 5246 }, }, { - id: 3134, + id: '3134', from: 'HDF', to: 'DUS', attributes: { prediction: 306 }, }, { - id: 3135, + id: '3135', from: 'KGS', to: 'DUS', attributes: { prediction: 2751 }, }, { - id: 3136, + id: '3136', from: 'IAH', to: 'BRO', attributes: { prediction: 5175 }, }, { - id: 3137, + id: '3137', from: 'MLA', to: 'BRU', attributes: { prediction: 4116 }, }, { - id: 3138, + id: '3138', from: 'PHL', to: 'BRU', attributes: { prediction: 2690 }, }, { - id: 3139, + id: '3139', from: 'NUE', to: 'FNC', attributes: { prediction: 554 }, }, { - id: 3140, + id: '3140', from: 'NDR', to: 'FRA', attributes: { prediction: 949 }, }, { - id: 3141, + id: '3141', from: 'NUE', to: 'FRA', attributes: { prediction: 15715 }, }, { - id: 3142, + id: '3142', from: 'ADF', to: 'ESB', attributes: { prediction: 1288 }, }, { - id: 3143, + id: '3143', from: 'FRA', to: 'EWR', attributes: { prediction: 23991 }, }, { - id: 3144, + id: '3144', from: 'BDS', to: 'FCO', attributes: { prediction: 19443 }, }, { - id: 3145, + id: '3145', from: 'EVN', to: 'FCO', attributes: { prediction: 1168 }, }, { - id: 3146, + id: '3146', from: 'POZ', to: 'FCO', attributes: { prediction: 1148 }, }, { - id: 3147, + id: '3147', from: 'SOG', to: 'FDE', attributes: { prediction: 31 }, }, { - id: 3148, + id: '3148', from: 'ALC', to: 'FEZ', attributes: { prediction: 1215 }, }, { - id: 3149, + id: '3149', from: 'BIM', to: 'FLL', attributes: { prediction: 237 }, }, { - id: 3150, + id: '3150', from: 'FRA', to: 'FLR', attributes: { prediction: 13531 }, }, { - id: 3151, + id: '3151', from: 'KMJ', to: 'FSZ', attributes: { prediction: 1371 }, }, { - id: 3152, + id: '3152', from: 'CGN', to: 'FUE', attributes: { prediction: 3075 }, }, { - id: 3153, + id: '3153', from: 'CTS', to: 'GAJ', attributes: { prediction: 950 }, }, { - id: 3154, + id: '3154', from: 'ADA', to: 'ESB', attributes: { prediction: 6445 }, }, { - id: 3155, + id: '3155', from: 'BJV', to: 'ESB', attributes: { prediction: 2004 }, }, { - id: 3156, + id: '3156', from: 'LED', to: 'EVN', attributes: { prediction: 503 }, }, { - id: 3157, + id: '3157', from: 'IAH', to: 'EWR', attributes: { prediction: 54169 }, }, { - id: 3158, + id: '3158', from: 'BHX', to: 'FAO', attributes: { prediction: 5792 }, }, { - id: 3159, + id: '3159', from: 'BRE', to: 'FAO', attributes: { prediction: 1308 }, }, { - id: 3160, + id: '3160', from: 'KBP', to: 'GYD', attributes: { prediction: 500 }, }, { - id: 3161, + id: '3161', from: 'DXB', to: 'HAM', attributes: { prediction: 9479 }, }, { - id: 3162, + id: '3162', from: 'OSL', to: 'HAU', attributes: { prediction: 10849 }, }, { - id: 3163, + id: '3163', from: 'CCN', to: 'HEA', attributes: { prediction: 73 }, }, { - id: 3164, + id: '3164', from: 'WWK', to: 'HGU', attributes: { prediction: 100 }, }, { - id: 3165, + id: '3165', from: 'PHL', to: 'GSP', attributes: { prediction: 2075 }, }, { - id: 3166, + id: '3166', from: 'CTS', to: 'GUM', attributes: { prediction: 861 }, }, { - id: 3167, + id: '3167', from: 'AYT', to: 'HAJ', attributes: { prediction: 5734 }, }, { - id: 3168, + id: '3168', from: 'SZX', to: 'HAK', attributes: { prediction: 8017 }, }, { - id: 3169, + id: '3169', from: 'SZX', to: 'HGH', attributes: { prediction: 40943 }, }, { - id: 3170, + id: '3170', from: 'NGB', to: 'HKG', attributes: { prediction: 6946 }, }, { - id: 3171, + id: '3171', from: 'SYD', to: 'HLZ', attributes: { prediction: 1279 }, }, { - id: 3172, + id: '3172', from: 'JFK', to: 'HKG', attributes: { prediction: 19034 }, }, { - id: 3173, + id: '3173', from: 'MUC', to: 'HKG', attributes: { prediction: 8102 }, }, { - id: 3174, + id: '3174', from: 'NRT', to: 'HKG', attributes: { prediction: 83625 }, }, { - id: 3175, + id: '3175', from: 'BHM', to: 'HOU', attributes: { prediction: 6459 }, }, { - id: 3176, + id: '3176', from: 'LAS', to: 'HOU', attributes: { prediction: 14851 }, }, { - id: 3177, + id: '3177', from: 'UFA', to: 'IST', attributes: { prediction: 1409 }, }, { - id: 3178, + id: '3178', from: 'DEL', to: 'IXB', attributes: { prediction: 9416 }, }, { - id: 3179, + id: '3179', from: 'BGY', to: 'OPO', attributes: { prediction: 2056 }, }, { - id: 3180, + id: '3180', from: 'BHM', to: 'ORD', attributes: { prediction: 3495 }, }, { - id: 3181, + id: '3181', from: 'BOS', to: 'LEB', attributes: { prediction: 1006 }, }, { - id: 3182, + id: '3182', from: 'LHR', to: 'LED', attributes: { prediction: 4716 }, }, { - id: 3183, + id: '3183', from: 'SXF', to: 'IST', attributes: { prediction: 2169 }, }, { - id: 3184, + id: '3184', from: 'TLV', to: 'IST', attributes: { prediction: 15053 }, }, { - id: 3185, + id: '3185', from: 'EWR', to: 'ITH', attributes: { prediction: 1978 }, }, { - id: 3186, + id: '3186', from: 'ISB', to: 'JED', attributes: { prediction: 2224 }, }, { - id: 3187, + id: '3187', from: 'BHX', to: 'JER', attributes: { prediction: 4307 }, }, { - id: 3188, + id: '3188', from: 'GEO', to: 'JFK', attributes: { prediction: 1839 }, }, { - id: 3189, + id: '3189', from: 'NRT', to: 'JFK', attributes: { prediction: 42692 }, }, { - id: 3190, + id: '3190', from: 'POS', to: 'JFK', attributes: { prediction: 14338 }, }, { - id: 3191, + id: '3191', from: 'MCI', to: 'JLN', attributes: { prediction: 728 }, }, { - id: 3192, + id: '3192', from: 'REC', to: 'JPA', attributes: { prediction: 5452 }, }, { - id: 3193, + id: '3193', from: 'DXB', to: 'LGW', attributes: { prediction: 25577 }, }, { - id: 3194, + id: '3194', from: 'NGO', to: 'KMJ', attributes: { prediction: 4741 }, }, { - id: 3195, + id: '3195', from: 'DXB', to: 'KRT', attributes: { prediction: 15919 }, }, { - id: 3196, + id: '3196', from: 'EGN', to: 'KRT', attributes: { prediction: 918 }, }, { - id: 3197, + id: '3197', from: 'BTS', to: 'KSC', attributes: { prediction: 2672 }, }, { - id: 3198, + id: '3198', from: 'BOM', to: 'KTM', attributes: { prediction: 3793 }, }, { - id: 3199, + id: '3199', from: 'IVL', to: 'KTT', attributes: { prediction: 154 }, }, { - id: 3200, + id: '3200', from: 'KSO', to: 'KZI', attributes: { prediction: 219 }, }, { - id: 3201, + id: '3201', from: 'ABQ', to: 'LAS', attributes: { prediction: 19402 }, }, { - id: 3202, + id: '3202', from: 'ORF', to: 'LAS', attributes: { prediction: 3549 }, }, { - id: 3203, + id: '3203', from: 'IAD', to: 'LAX', attributes: { prediction: 47824 }, }, { - id: 3204, + id: '3204', from: 'JFK', to: 'LAX', attributes: { prediction: 129906 }, }, { - id: 3205, + id: '3205', from: 'MSY', to: 'LAX', attributes: { prediction: 10920 }, }, { - id: 3206, + id: '3206', from: 'PPT', to: 'LAX', attributes: { prediction: 13024 }, }, { - id: 3207, + id: '3207', from: 'ALG', to: 'LHR', attributes: { prediction: 4220 }, }, { - id: 3208, + id: '3208', from: 'LCG', to: 'LHR', attributes: { prediction: 3782 }, }, { - id: 3209, + id: '3209', from: 'LIS', to: 'LIN', attributes: { prediction: 3036 }, }, { - id: 3210, + id: '3210', from: 'BRS', to: 'LIS', attributes: { prediction: 1453 }, }, { - id: 3211, + id: '3211', from: 'CLT', to: 'LIT', attributes: { prediction: 4776 }, }, { - id: 3212, + id: '3212', from: 'MYR', to: 'ORD', attributes: { prediction: 1146 }, }, { - id: 3213, + id: '3213', from: 'YVR', to: 'ORD', attributes: { prediction: 10076 }, }, { - id: 3214, + id: '3214', from: 'DXB', to: 'MCT', attributes: { prediction: 34173 }, }, { - id: 3215, + id: '3215', from: 'LGA', to: 'MDW', attributes: { prediction: 32972 }, }, { - id: 3216, + id: '3216', from: 'MKE', to: 'MEM', attributes: { prediction: 3984 }, }, { - id: 3217, + id: '3217', from: 'MLU', to: 'MEM', attributes: { prediction: 2286 }, }, { - id: 3218, + id: '3218', from: 'ADD', to: 'LOS', attributes: { prediction: 6455 }, }, { - id: 3219, + id: '3219', from: 'BSL', to: 'LPA', attributes: { prediction: 1939 }, }, { - id: 3220, + id: '3220', from: 'CNX', to: 'LPQ', attributes: { prediction: 1194 }, }, { - id: 3221, + id: '3221', from: 'DXB', to: 'LRR', attributes: { prediction: 1062 }, }, { - id: 3222, + id: '3222', from: 'MLA', to: 'LUX', attributes: { prediction: 169 }, }, { - id: 3223, + id: '3223', from: 'BCN', to: 'LXR', attributes: { prediction: 633 }, }, { - id: 3224, + id: '3224', from: 'SHJ', to: 'LXR', attributes: { prediction: 1317 }, }, { - id: 3225, + id: '3225', from: 'BOD', to: 'LYS', attributes: { prediction: 25727 }, }, { - id: 3226, + id: '3226', from: 'PUF', to: 'LYS', attributes: { prediction: 3863 }, }, { - id: 3227, + id: '3227', from: 'TOE', to: 'LYS', attributes: { prediction: 658 }, }, { - id: 3228, + id: '3228', from: 'LHR', to: 'MAA', attributes: { prediction: 4051 }, }, { - id: 3229, + id: '3229', from: 'TRN', to: 'MAD', attributes: { prediction: 5084 }, }, { - id: 3230, + id: '3230', from: 'KWA', to: 'MAJ', attributes: { prediction: 910 }, }, { - id: 3231, + id: '3231', from: 'WIL', to: 'MBA', attributes: { prediction: 1788 }, }, { - id: 3232, + id: '3232', from: 'FLL', to: 'MBJ', attributes: { prediction: 6011 }, }, { - id: 3233, + id: '3233', from: 'ECP', to: 'MCO', attributes: { prediction: 6493 }, }, { - id: 3234, + id: '3234', from: 'SLC', to: 'MEX', attributes: { prediction: 1695 }, }, { - id: 3235, + id: '3235', from: 'ADE', to: 'MGQ', attributes: { prediction: 381 }, }, { - id: 3236, + id: '3236', from: 'LAS', to: 'MHT', attributes: { prediction: 3543 }, }, { - id: 3237, + id: '3237', from: 'CTG', to: 'MIA', attributes: { prediction: 2524 }, }, { - id: 3238, + id: '3238', from: 'MHH', to: 'MIA', attributes: { prediction: 2330 }, }, { - id: 3239, + id: '3239', from: 'DYU', to: 'OVB', attributes: { prediction: 391 }, }, { - id: 3240, + id: '3240', from: 'GIG', to: 'PTY', attributes: { prediction: 3426 }, }, { - id: 3241, + id: '3241', from: 'JFK', to: 'PVG', attributes: { prediction: 5178 }, }, { - id: 3242, + id: '3242', from: 'EWR', to: 'PWM', attributes: { prediction: 6814 }, }, { - id: 3243, + id: '3243', from: 'MMO', to: 'RAI', attributes: { prediction: 180 }, }, { - id: 3244, + id: '3244', from: 'LED', to: 'NCE', attributes: { prediction: 967 }, }, { - id: 3245, + id: '3245', from: 'PQS', to: 'MOU', attributes: { prediction: 115 }, }, { - id: 3246, + id: '3246', from: 'CEB', to: 'MPH', attributes: { prediction: 3072 }, }, { - id: 3247, + id: '3247', from: 'CDG', to: 'MPL', attributes: { prediction: 16126 }, }, { - id: 3248, + id: '3248', from: 'MNL', to: 'MRQ', attributes: { prediction: 751 }, }, { - id: 3249, + id: '3249', from: 'LGA', to: 'MSN', attributes: { prediction: 1028 }, }, { - id: 3250, + id: '3250', from: 'TOH', to: 'MTV', attributes: { prediction: 48 }, }, { - id: 3251, + id: '3251', from: 'TIJ', to: 'MTY', attributes: { prediction: 6923 }, }, { - id: 3252, + id: '3252', from: 'SPU', to: 'MUC', attributes: { prediction: 2690 }, }, { - id: 3253, + id: '3253', from: 'AUH', to: 'MXP', attributes: { prediction: 4273 }, }, { - id: 3254, + id: '3254', from: 'BEY', to: 'MXP', attributes: { prediction: 1719 }, }, { - id: 3255, + id: '3255', from: 'MLA', to: 'MXP', attributes: { prediction: 4682 }, }, { - id: 3256, + id: '3256', from: 'MRU', to: 'MXP', attributes: { prediction: 1710 }, }, { - id: 3257, + id: '3257', from: 'SOF', to: 'MXP', attributes: { prediction: 3510 }, }, { - id: 3258, + id: '3258', from: 'LUX', to: 'NAP', attributes: { prediction: 185 }, }, { - id: 3259, + id: '3259', from: 'HUH', to: 'RFP', attributes: { prediction: 520 }, }, { - id: 3260, + id: '3260', from: 'DEN', to: 'RNO', attributes: { prediction: 11370 }, }, { - id: 3261, + id: '3261', from: 'LAX', to: 'RDD', attributes: { prediction: 1347 }, }, { - id: 3262, + id: '3262', from: 'EWR', to: 'RDU', attributes: { prediction: 13165 }, }, { - id: 3263, + id: '3263', from: 'ATH', to: 'RHO', attributes: { prediction: 45986 }, }, { - id: 3264, + id: '3264', from: 'PRG', to: 'RHO', attributes: { prediction: 2201 }, }, { - id: 3265, + id: '3265', from: 'MIA', to: 'RIC', attributes: { prediction: 1303 }, }, { - id: 3266, + id: '3266', from: 'KUN', to: 'RIX', attributes: { prediction: 1928 }, }, { - id: 3267, + id: '3267', from: 'TLS', to: 'RNS', attributes: { prediction: 3185 }, }, { - id: 3268, + id: '3268', from: 'ORD', to: 'ROA', attributes: { prediction: 3878 }, }, { - id: 3269, + id: '3269', from: 'PAH', to: 'ORD', attributes: { prediction: 2385 }, }, { - id: 3270, + id: '3270', from: 'BNA', to: 'ORF', attributes: { prediction: 3261 }, }, { - id: 3271, + id: '3271', from: 'BIA', to: 'ORY', attributes: { prediction: 15749 }, }, { - id: 3272, + id: '3272', from: 'DUS', to: 'ORY', attributes: { prediction: 3977 }, }, { - id: 3273, + id: '3273', from: 'FAO', to: 'OSL', attributes: { prediction: 1057 }, }, { - id: 3274, + id: '3274', from: 'DUB', to: 'OTP', attributes: { prediction: 2365 }, }, { - id: 3275, + id: '3275', from: 'FRL', to: 'OTP', attributes: { prediction: 986 }, }, { - id: 3276, + id: '3276', from: 'OMR', to: 'OTP', attributes: { prediction: 1820 }, }, { - id: 3277, + id: '3277', from: 'MAN', to: 'PAD', attributes: { prediction: 2038 }, }, { - id: 3278, + id: '3278', from: 'ELV', to: 'PEC', attributes: { prediction: 1 }, }, { - id: 3279, + id: '3279', from: 'BWN', to: 'PER', attributes: { prediction: 2546 }, }, { - id: 3280, + id: '3280', from: 'MEL', to: 'PER', attributes: { prediction: 65191 }, }, { - id: 3281, + id: '3281', from: 'MHT', to: 'PHL', attributes: { prediction: 24439 }, }, { - id: 3282, + id: '3282', from: 'BOO', to: 'RYG', attributes: { prediction: 3321 }, }, { - id: 3283, + id: '3283', from: 'DXB', to: 'SAH', attributes: { prediction: 9672 }, }, { - id: 3284, + id: '3284', from: 'LBA', to: 'PMI', attributes: { prediction: 6265 }, }, { - id: 3285, + id: '3285', from: 'TKK', to: 'PNI', attributes: { prediction: 1040 }, }, { - id: 3286, + id: '3286', from: 'SSA', to: 'PNZ', attributes: { prediction: 647 }, }, { - id: 3287, + id: '3287', from: 'CRL', to: 'PRG', attributes: { prediction: 3279 }, }, { - id: 3288, + id: '3288', from: 'ZAG', to: 'PRG', attributes: { prediction: 1499 }, }, { - id: 3289, + id: '3289', from: 'TIA', to: 'PRN', attributes: { prediction: 1820 }, }, { - id: 3290, + id: '3290', from: 'CCS', to: 'PTY', attributes: { prediction: 15444 }, }, { - id: 3291, + id: '3291', from: 'MEX', to: 'PTY', attributes: { prediction: 8508 }, }, { - id: 3292, + id: '3292', from: 'MAD', to: 'PUJ', attributes: { prediction: 4099 }, }, { - id: 3293, + id: '3293', from: 'LGW', to: 'PUY', attributes: { prediction: 933 }, }, { - id: 3294, + id: '3294', from: 'YYC', to: 'PVR', attributes: { prediction: 1272 }, }, { - id: 3295, + id: '3295', from: 'CTU', to: 'PZI', attributes: { prediction: 4502 }, }, { - id: 3296, + id: '3296', from: 'JFK', to: 'SAN', attributes: { prediction: 24126 }, }, { - id: 3297, + id: '3297', from: 'MIA', to: 'SAP', attributes: { prediction: 11103 }, }, { - id: 3298, + id: '3298', from: 'TGU', to: 'SAP', attributes: { prediction: 4115 }, }, { - id: 3299, + id: '3299', from: 'ATL', to: 'SAT', attributes: { prediction: 33584 }, }, { - id: 3300, + id: '3300', from: 'CCP', to: 'SCL', attributes: { prediction: 15269 }, }, { - id: 3301, + id: '3301', from: 'PUJ', to: 'SCL', attributes: { prediction: 777 }, }, { - id: 3302, + id: '3302', from: 'LAD', to: 'SDD', attributes: { prediction: 341 }, }, { - id: 3303, + id: '3303', from: 'ITM', to: 'SDJ', attributes: { prediction: 41559 }, }, { - id: 3304, + id: '3304', from: 'PAP', to: 'SDQ', attributes: { prediction: 1271 }, }, { - id: 3305, + id: '3305', from: 'ABZ', to: 'SOU', attributes: { prediction: 234 }, }, { - id: 3306, + id: '3306', from: 'MAN', to: 'SSH', attributes: { prediction: 8805 }, }, { - id: 3307, + id: '3307', from: 'DUS', to: 'STN', attributes: { prediction: 9821 }, }, { - id: 3308, + id: '3308', from: 'BIL', to: 'SDY', attributes: { prediction: 238 }, }, { - id: 3309, + id: '3309', from: 'CUN', to: 'SEA', attributes: { prediction: 104 }, }, { - id: 3310, + id: '3310', from: 'PHX', to: 'SEA', attributes: { prediction: 49603 }, }, { - id: 3311, + id: '3311', from: 'BAH', to: 'SHJ', attributes: { prediction: 6725 }, }, { - id: 3312, + id: '3312', from: 'AUH', to: 'SIN', attributes: { prediction: 7907 }, }, { - id: 3313, + id: '3313', from: 'VKO', to: 'SIP', attributes: { prediction: 487 }, }, { - id: 3314, + id: '3314', from: 'BUR', to: 'SJC', attributes: { prediction: 21530 }, }, { - id: 3315, + id: '3315', from: 'SBA', to: 'SJC', attributes: { prediction: 2571 }, }, { - id: 3316, + id: '3316', from: 'IAH', to: 'SJU', attributes: { prediction: 3945 }, }, { - id: 3317, + id: '3317', from: 'MUC', to: 'SKG', attributes: { prediction: 11894 }, }, { - id: 3318, + id: '3318', from: 'KWI', to: 'SKT', attributes: { prediction: 735 }, }, { - id: 3319, + id: '3319', from: 'BUR', to: 'SMF', attributes: { prediction: 21896 }, }, { - id: 3320, + id: '3320', from: 'BWN', to: 'SUB', attributes: { prediction: 1227 }, }, { - id: 3321, + id: '3321', from: 'BLQ', to: 'SUF', attributes: { prediction: 7109 }, }, { - id: 3322, + id: '3322', from: 'MSP', to: 'SUX', attributes: { prediction: 1707 }, }, { - id: 3323, + id: '3323', from: 'SXF', to: 'SVG', attributes: { prediction: 854 }, }, { - id: 3324, + id: '3324', from: 'CSX', to: 'SWA', attributes: { prediction: 4207 }, }, { - id: 3325, + id: '3325', from: 'LSY', to: 'SYD', attributes: { prediction: 2499 }, }, { - id: 3326, + id: '3326', from: 'PTP', to: 'SJU', attributes: { prediction: 1602 }, }, { - id: 3327, + id: '3327', from: 'YYZ', to: 'SJU', attributes: { prediction: 684 }, }, { - id: 3328, + id: '3328', from: 'HGH', to: 'TSN', attributes: { prediction: 4336 }, }, { - id: 3329, + id: '3329', from: 'GWD', to: 'TUK', attributes: { prediction: 432 }, }, { - id: 3330, + id: '3330', from: 'OKA', to: 'TAK', attributes: { prediction: 4521 }, }, { - id: 3331, + id: '3331', from: 'KUL', to: 'TGG', attributes: { prediction: 12947 }, }, { - id: 3332, + id: '3332', from: 'BEG', to: 'TIV', attributes: { prediction: 2937 }, }, { - id: 3333, + id: '3333', from: 'CUN', to: 'TLC', attributes: { prediction: 18045 }, }, { - id: 3334, + id: '3334', from: 'SZX', to: 'TNA', attributes: { prediction: 1534 }, }, { - id: 3335, + id: '3335', from: 'ORY', to: 'TOE', attributes: { prediction: 1581 }, }, { - id: 3336, + id: '3336', from: 'SSJ', to: 'TRD', attributes: { prediction: 139 }, }, { - id: 3337, + id: '3337', from: 'RHO', to: 'TXL', attributes: { prediction: 1005 }, }, { - id: 3338, + id: '3338', from: 'MEM', to: 'TYS', attributes: { prediction: 3987 }, }, { - id: 3339, + id: '3339', from: 'PIE', to: 'TYS', attributes: { prediction: 2075 }, }, { - id: 3340, + id: '3340', from: 'KGF', to: 'UKK', attributes: { prediction: 224 }, }, { - id: 3341, + id: '3341', from: 'DLA', to: 'ZRH', attributes: { prediction: 2759 }, }, { - id: 3342, + id: '3342', from: 'NUE', to: 'ZRH', attributes: { prediction: 6967 }, }, { - id: 3343, + id: '3343', from: 'SJU', to: 'AXA', attributes: { prediction: 1584 }, }, { - id: 3344, + id: '3344', from: 'LCA', to: 'BBU', attributes: { prediction: 2042 }, }, { - id: 3345, + id: '3345', from: 'ARN', to: 'BCN', attributes: { prediction: 9272 }, }, { - id: 3346, + id: '3346', from: 'LCG', to: 'BCN', attributes: { prediction: 6659 }, }, { - id: 3347, + id: '3347', from: 'SVQ', to: 'BCN', attributes: { prediction: 12213 }, }, { - id: 3348, + id: '3348', from: 'TNG', to: 'BCN', attributes: { prediction: 136 }, }, { - id: 3349, + id: '3349', from: 'ORD', to: 'BDL', attributes: { prediction: 19030 }, }, { - id: 3350, + id: '3350', from: 'DPS', to: 'BDO', attributes: { prediction: 4533 }, }, { - id: 3351, + id: '3351', from: 'IOM', to: 'BFS', attributes: { prediction: 524 }, }, { - id: 3352, + id: '3352', from: 'PHL', to: 'BGM', attributes: { prediction: 3248 }, }, { - id: 3353, + id: '3353', from: 'TBS', to: 'WAW', attributes: { prediction: 986 }, }, { - id: 3354, + id: '3354', from: 'DFW', to: 'XNA', attributes: { prediction: 14514 }, }, { - id: 3355, + id: '3355', from: 'FRA', to: 'XRY', attributes: { prediction: 2666 }, }, { - id: 3356, + id: '3356', from: 'LYG', to: 'XUZ', attributes: { prediction: 1787 }, }, { - id: 3357, + id: '3357', from: 'CDV', to: 'YAK', attributes: { prediction: 1336 }, }, { - id: 3358, + id: '3358', from: 'LAX', to: 'YEG', attributes: { prediction: 3229 }, }, { - id: 3359, + id: '3359', from: 'YQD', to: 'YFO', attributes: { prediction: 357 }, }, { - id: 3360, + id: '3360', from: 'YKU', to: 'YKQ', attributes: { prediction: 707 }, }, { - id: 3361, + id: '3361', from: 'EWR', to: 'YOW', attributes: { prediction: 4038 }, }, { - id: 3362, + id: '3362', from: 'YZF', to: 'YRA', attributes: { prediction: 224 }, }, { - id: 3363, + id: '3363', from: 'YCS', to: 'YRT', attributes: { prediction: 96 }, }, { - id: 3364, + id: '3364', from: 'DOK', to: 'BUS', attributes: { prediction: 112 }, }, { - id: 3365, + id: '3365', from: 'DAM', to: 'CAI', attributes: { prediction: 7190 }, }, { - id: 3366, + id: '3366', from: 'DUS', to: 'CAI', attributes: { prediction: 2166 }, }, { - id: 3367, + id: '3367', from: 'VVI', to: 'CBB', attributes: { prediction: 24130 }, }, { - id: 3368, + id: '3368', from: 'DAM', to: 'CCS', attributes: { prediction: 2141 }, }, { - id: 3369, + id: '3369', from: 'BOS', to: 'CDG', attributes: { prediction: 11636 }, }, { - id: 3370, + id: '3370', from: 'SXM', to: 'CDG', attributes: { prediction: 6034 }, }, { - id: 3371, + id: '3371', from: 'CGR', to: 'CGB', attributes: { prediction: 20328 }, }, { - id: 3372, + id: '3372', from: 'ICN', to: 'CGO', attributes: { prediction: 1716 }, }, { - id: 3373, + id: '3373', from: 'ACC', to: 'ABJ', attributes: { prediction: 3887 }, }, { - id: 3374, + id: '3374', from: 'OUA', to: 'ABJ', attributes: { prediction: 632 }, }, { - id: 3375, + id: '3375', from: 'TLC', to: 'ACA', attributes: { prediction: 5530 }, }, { - id: 3376, + id: '3376', from: 'KLN', to: 'ADQ', attributes: { prediction: 267 }, }, { - id: 3377, + id: '3377', from: 'CPH', to: 'AGP', attributes: { prediction: 3296 }, }, { - id: 3378, + id: '3378', from: 'GSE', to: 'AGP', attributes: { prediction: 1202 }, }, { - id: 3379, + id: '3379', from: 'SNN', to: 'AGP', attributes: { prediction: 1611 }, }, { - id: 3380, + id: '3380', from: 'ICN', to: 'ALA', attributes: { prediction: 1521 }, }, { - id: 3381, + id: '3381', from: 'TFS', to: 'ALC', attributes: { prediction: 930 }, }, { - id: 3382, + id: '3382', from: 'MSP', to: 'AMS', attributes: { prediction: 12744 }, }, { - id: 3383, + id: '3383', from: 'SEA', to: 'CUN', attributes: { prediction: 4867 }, }, { - id: 3384, + id: '3384', from: 'LEX', to: 'CVG', attributes: { prediction: 3177 }, }, { - id: 3385, + id: '3385', from: 'PER', to: 'HKG', attributes: { prediction: 10696 }, }, { - id: 3386, + id: '3386', from: 'IAH', to: 'HNL', attributes: { prediction: 14570 }, }, { - id: 3387, + id: '3387', from: 'SHE', to: 'CJU', attributes: { prediction: 663 }, }, { - id: 3388, + id: '3388', from: 'DAB', to: 'CLT', attributes: { prediction: 5672 }, }, { - id: 3389, + id: '3389', from: 'JAX', to: 'BHM', attributes: { prediction: 2767 }, }, { - id: 3390, + id: '3390', from: 'ACC', to: 'BJL', attributes: { prediction: 1386 }, }, { - id: 3391, + id: '3391', from: 'NCE', to: 'BLL', attributes: { prediction: 366 }, }, { - id: 3392, + id: '3392', from: 'RNO', to: 'BOI', attributes: { prediction: 5633 }, }, { - id: 3393, + id: '3393', from: 'MQN', to: 'BOO', attributes: { prediction: 1464 }, }, { - id: 3394, + id: '3394', from: 'SEA', to: 'BOS', attributes: { prediction: 5483 }, }, { - id: 3395, + id: '3395', from: 'PHX', to: 'CMH', attributes: { prediction: 10003 }, }, { - id: 3396, + id: '3396', from: 'PSA', to: 'CND', attributes: { prediction: 850 }, }, { - id: 3397, + id: '3397', from: 'CGH', to: 'CNF', attributes: { prediction: 67866 }, }, { - id: 3398, + id: '3398', from: 'ARN', to: 'CPH', attributes: { prediction: 38751 }, }, { - id: 3399, + id: '3399', from: 'PDG', to: 'BTH', attributes: { prediction: 6172 }, }, { - id: 3400, + id: '3400', from: 'AET', to: 'BTT', attributes: { prediction: 117 }, }, { - id: 3401, + id: '3401', from: 'BRS', to: 'BUD', attributes: { prediction: 1347 }, }, { - id: 3402, + id: '3402', from: 'VAR', to: 'BUD', attributes: { prediction: 1303 }, }, { - id: 3403, + id: '3403', from: 'NRN', to: 'BZR', attributes: { prediction: 1154 }, }, { - id: 3404, + id: '3404', from: 'PEN', to: 'CAN', attributes: { prediction: 1668 }, }, { - id: 3405, + id: '3405', from: 'JFK', to: 'CCS', attributes: { prediction: 1310 }, }, { - id: 3406, + id: '3406', from: 'VIG', to: 'CCS', attributes: { prediction: 9560 }, }, { - id: 3407, + id: '3407', from: 'ABJ', to: 'CDG', attributes: { prediction: 8399 }, }, { - id: 3408, + id: '3408', from: 'JFK', to: 'CDG', attributes: { prediction: 38937 }, }, { - id: 3409, + id: '3409', from: 'LIN', to: 'CDG', attributes: { prediction: 8782 }, }, { - id: 3410, + id: '3410', from: 'LIS', to: 'CDG', attributes: { prediction: 22122 }, }, { - id: 3411, + id: '3411', from: 'MRS', to: 'CDG', attributes: { prediction: 26251 }, }, { - id: 3412, + id: '3412', from: 'ROO', to: 'CGB', attributes: { prediction: 999 }, }, { - id: 3413, + id: '3413', from: 'HKG', to: 'CGK', attributes: { prediction: 29583 }, }, { - id: 3414, + id: '3414', from: 'VIE', to: 'CPH', attributes: { prediction: 14793 }, }, { - id: 3415, + id: '3415', from: 'DTW', to: 'CUN', attributes: { prediction: 1798 }, }, { - id: 3416, + id: '3416', from: 'GOH', to: 'JFR', attributes: { prediction: 700 }, }, { - id: 3417, + id: '3417', from: 'DXB', to: 'JNB', attributes: { prediction: 29756 }, }, { - id: 3418, + id: '3418', from: 'UTT', to: 'JNB', attributes: { prediction: 1032 }, }, { - id: 3419, + id: '3419', from: 'EVV', to: 'DTW', attributes: { prediction: 3367 }, }, { - id: 3420, + id: '3420', from: 'CCF', to: 'DUB', attributes: { prediction: 1716 }, }, { - id: 3421, + id: '3421', from: 'JNB', to: 'DUR', attributes: { prediction: 107579 }, }, { - id: 3422, + id: '3422', from: 'KRK', to: 'DUS', attributes: { prediction: 2916 }, }, { - id: 3423, + id: '3423', from: 'SDP', to: 'DUT', attributes: { prediction: 251 }, }, { - id: 3424, + id: '3424', from: 'AMS', to: 'DXB', attributes: { prediction: 26145 }, }, { - id: 3425, + id: '3425', from: 'IXB', to: 'DEL', attributes: { prediction: 8035 }, }, { - id: 3426, + id: '3426', from: 'TPE', to: 'DEL', attributes: { prediction: 4741 }, }, { - id: 3427, + id: '3427', from: 'SAW', to: 'DIY', attributes: { prediction: 4172 }, }, { - id: 3428, + id: '3428', from: 'PHC', to: 'DLA', attributes: { prediction: 373 }, }, { - id: 3429, + id: '3429', from: 'CGO', to: 'DLC', attributes: { prediction: 4608 }, }, { - id: 3430, + id: '3430', from: 'HIJ', to: 'DLC', attributes: { prediction: 3091 }, }, { - id: 3431, + id: '3431', from: 'HKG', to: 'DLC', attributes: { prediction: 3120 }, }, { - id: 3432, + id: '3432', from: 'ZRH', to: 'DOH', attributes: { prediction: 5386 }, }, { - id: 3433, + id: '3433', from: 'DUS', to: 'DRS', attributes: { prediction: 14280 }, }, { - id: 3434, + id: '3434', from: 'SYD', to: 'DRW', attributes: { prediction: 9357 }, }, { - id: 3435, + id: '3435', from: 'JIB', to: 'DXB', attributes: { prediction: 1956 }, }, { - id: 3436, + id: '3436', from: 'NCE', to: 'DXB', attributes: { prediction: 5825 }, }, { - id: 3437, + id: '3437', from: 'ABZ', to: 'EBJ', attributes: { prediction: 343 }, }, { - id: 3438, + id: '3438', from: 'AMM', to: 'EBL', attributes: { prediction: 736 }, }, { - id: 3439, + id: '3439', from: 'ZTH', to: 'EFL', attributes: { prediction: 122 }, }, { - id: 3440, + id: '3440', from: 'GRO', to: 'EMA', attributes: { prediction: 2538 }, }, { - id: 3441, + id: '3441', from: 'LHR', to: 'KEF', attributes: { prediction: 6157 }, }, { - id: 3442, + id: '3442', from: 'HRB', to: 'KIJ', attributes: { prediction: 1675 }, }, { - id: 3443, + id: '3443', from: 'PLS', to: 'KIN', attributes: { prediction: 208 }, }, { - id: 3444, + id: '3444', from: 'ALC', to: 'KIR', attributes: { prediction: 1325 }, }, { - id: 3445, + id: '3445', from: 'SVO', to: 'KLV', attributes: { prediction: 1378 }, }, { - id: 3446, + id: '3446', from: 'ARN', to: 'KRN', attributes: { prediction: 2346 }, }, { - id: 3447, + id: '3447', from: 'BLQ', to: 'GRO', attributes: { prediction: 4139 }, }, { - id: 3448, + id: '3448', from: 'MSP', to: 'FAR', attributes: { prediction: 10869 }, }, { - id: 3449, + id: '3449', from: 'MJM', to: 'FIH', attributes: { prediction: 1260 }, }, { - id: 3450, + id: '3450', from: 'TPA', to: 'FLL', attributes: { prediction: 32133 }, }, { - id: 3451, + id: '3451', from: 'FDH', to: 'FRA', attributes: { prediction: 3250 }, }, { - id: 3452, + id: '3452', from: 'SIN', to: 'FRA', attributes: { prediction: 37548 }, }, { - id: 3453, + id: '3453', from: 'DEN', to: 'GCK', attributes: { prediction: 1127 }, }, { - id: 3454, + id: '3454', from: 'LAX', to: 'GDL', attributes: { prediction: 32086 }, }, { - id: 3455, + id: '3455', from: 'ATL', to: 'GIG', attributes: { prediction: 5632 }, }, { - id: 3456, + id: '3456', from: 'REC', to: 'GIG', attributes: { prediction: 42627 }, }, { - id: 3457, + id: '3457', from: 'BCN', to: 'GRX', attributes: { prediction: 5800 }, }, { - id: 3458, + id: '3458', from: 'SEA', to: 'GTF', attributes: { prediction: 2972 }, }, { - id: 3459, + id: '3459', from: 'MYY', to: 'LGL', attributes: { prediction: 16 }, }, { - id: 3460, + id: '3460', from: 'BEL', to: 'PBM', attributes: { prediction: 497 }, }, { - id: 3461, + id: '3461', from: 'RGA', to: 'RGL', attributes: { prediction: 945 }, }, { - id: 3462, + id: '3462', from: 'HAM', to: 'RIX', attributes: { prediction: 2670 }, }, { - id: 3463, + id: '3463', from: 'CRK', to: 'KUL', attributes: { prediction: 4209 }, }, { - id: 3464, + id: '3464', from: 'XMN', to: 'KUL', attributes: { prediction: 7062 }, }, { - id: 3465, + id: '3465', from: 'LCA', to: 'KWI', attributes: { prediction: 614 }, }, { - id: 3466, + id: '3466', from: 'CJU', to: 'KWJ', attributes: { prediction: 15622 }, }, { - id: 3467, + id: '3467', from: 'MAN', to: 'LAS', attributes: { prediction: 860 }, }, { - id: 3468, + id: '3468', from: 'PSP', to: 'LAX', attributes: { prediction: 4368 }, }, { - id: 3469, + id: '3469', from: 'AMD', to: 'HYD', attributes: { prediction: 9730 }, }, { - id: 3470, + id: '3470', from: 'JAX', to: 'IAD', attributes: { prediction: 5098 }, }, { - id: 3471, + id: '3471', from: 'CLT', to: 'IAH', attributes: { prediction: 34369 }, }, { - id: 3472, + id: '3472', from: 'LNK', to: 'IAH', attributes: { prediction: 40 }, }, { - id: 3473, + id: '3473', from: 'MAF', to: 'IAH', attributes: { prediction: 7542 }, }, { - id: 3474, + id: '3474', from: 'SJU', to: 'IAH', attributes: { prediction: 8488 }, }, { - id: 3475, + id: '3475', from: 'BNE', to: 'ICN', attributes: { prediction: 4984 }, }, { - id: 3476, + id: '3476', from: 'NGO', to: 'ICN', attributes: { prediction: 25317 }, }, { - id: 3477, + id: '3477', from: 'PEK', to: 'ICN', attributes: { prediction: 55983 }, }, { - id: 3478, + id: '3478', from: 'BAH', to: 'ISB', attributes: { prediction: 2044 }, }, { - id: 3479, + id: '3479', from: 'FLL', to: 'ISP', attributes: { prediction: 6700 }, }, { - id: 3480, + id: '3480', from: 'FRU', to: 'IST', attributes: { prediction: 3458 }, }, { - id: 3481, + id: '3481', from: 'CGP', to: 'JED', attributes: { prediction: 2882 }, }, { - id: 3482, + id: '3482', from: 'DEN', to: 'JFK', attributes: { prediction: 4164 }, }, { - id: 3483, + id: '3483', from: 'SJU', to: 'JFK', attributes: { prediction: 23336 }, }, { - id: 3484, + id: '3484', from: 'GRU', to: 'JNB', attributes: { prediction: 11584 }, }, { - id: 3485, + id: '3485', from: 'FRA', to: 'KBP', attributes: { prediction: 10720 }, }, { - id: 3486, + id: '3486', from: 'IMK', to: 'KEP', attributes: { prediction: 44 }, }, { - id: 3487, + id: '3487', from: 'SYZ', to: 'KIH', attributes: { prediction: 1104 }, }, { - id: 3488, + id: '3488', from: 'SGN', to: 'KIX', attributes: { prediction: 5672 }, }, { - id: 3489, + id: '3489', from: 'NYO', to: 'KLU', attributes: { prediction: 417 }, }, { - id: 3490, + id: '3490', from: 'ICN', to: 'KMJ', attributes: { prediction: 1184 }, }, { - id: 3491, + id: '3491', from: 'KTN', to: 'KPB', attributes: { prediction: 156 }, }, { - id: 3492, + id: '3492', from: 'LED', to: 'KRR', attributes: { prediction: 1436 }, }, { - id: 3493, + id: '3493', from: 'AMS', to: 'KRS', attributes: { prediction: 3082 }, }, { - id: 3494, + id: '3494', from: 'SVG', to: 'KRS', attributes: { prediction: 1325 }, }, { - id: 3495, + id: '3495', from: 'AMM', to: 'KRT', attributes: { prediction: 2503 }, }, { - id: 3496, + id: '3496', from: 'AER', to: 'LED', attributes: { prediction: 2438 }, }, { - id: 3497, + id: '3497', from: 'OVB', to: 'LED', attributes: { prediction: 2706 }, }, { - id: 3498, + id: '3498', from: 'LCR', to: 'LET', attributes: { prediction: 88 }, }, { - id: 3499, + id: '3499', from: 'MCO', to: 'LGA', attributes: { prediction: 20360 }, }, { - id: 3500, + id: '3500', from: 'EYW', to: 'RSW', attributes: { prediction: 1084 }, }, { - id: 3501, + id: '3501', from: 'LCA', to: 'RUH', attributes: { prediction: 504 }, }, { - id: 3502, + id: '3502', from: 'RAH', to: 'RUH', attributes: { prediction: 1436 }, }, { - id: 3503, + id: '3503', from: 'MHK', to: 'MCK', attributes: { prediction: 230 }, }, { - id: 3504, + id: '3504', from: 'MIA', to: 'MCO', attributes: { prediction: 36538 }, }, { - id: 3505, + id: '3505', from: 'JAI', to: 'MCT', attributes: { prediction: 2828 }, }, { - id: 3506, + id: '3506', from: 'KUL', to: 'MDC', attributes: { prediction: 2155 }, }, { - id: 3507, + id: '3507', from: 'PHX', to: 'MDW', attributes: { prediction: 23505 }, }, { - id: 3508, + id: '3508', from: 'YYZ', to: 'LHE', attributes: { prediction: 1645 }, }, { - id: 3509, + id: '3509', from: 'ORD', to: 'LHR', attributes: { prediction: 39966 }, }, { - id: 3510, + id: '3510', from: 'IQT', to: 'LIM', attributes: { prediction: 15421 }, }, { - id: 3511, + id: '3511', from: 'LAX', to: 'LIM', attributes: { prediction: 9670 }, }, { - id: 3512, + id: '3512', from: 'GVA', to: 'LIS', attributes: { prediction: 10075 }, }, { - id: 3513, + id: '3513', from: 'ZRH', to: 'LJU', attributes: { prediction: 2835 }, }, { - id: 3514, + id: '3514', from: 'LAP', to: 'LMM', attributes: { prediction: 824 }, }, { - id: 3515, + id: '3515', from: 'MIA', to: 'LNK', attributes: { prediction: 115 }, }, { - id: 3516, + id: '3516', from: 'GRO', to: 'LPA', attributes: { prediction: 2397 }, }, { - id: 3517, + id: '3517', from: 'SHE', to: 'LYG', attributes: { prediction: 568 }, }, { - id: 3518, + id: '3518', from: 'BOS', to: 'MAD', attributes: { prediction: 2108 }, }, { - id: 3519, + id: '3519', from: 'SDR', to: 'MAD', attributes: { prediction: 10458 }, }, { - id: 3520, + id: '3520', from: 'PUJ', to: 'MAN', attributes: { prediction: 1809 }, }, { - id: 3521, + id: '3521', from: 'MIA', to: 'MAO', attributes: { prediction: 4609 }, }, { - id: 3522, + id: '3522', from: 'MEM', to: 'MBJ', attributes: { prediction: 2745 }, }, { - id: 3523, + id: '3523', from: 'SAT', to: 'MEX', attributes: { prediction: 5488 }, }, { - id: 3524, + id: '3524', from: 'CLO', to: 'MIA', attributes: { prediction: 7017 }, }, { - id: 3525, + id: '3525', from: 'PTP', to: 'MIA', attributes: { prediction: 121 }, }, { - id: 3526, + id: '3526', from: 'SYD', to: 'MIM', attributes: { prediction: 266 }, }, { - id: 3527, + id: '3527', from: 'ADE', to: 'SAH', attributes: { prediction: 11058 }, }, { - id: 3528, + id: '3528', from: 'ZRH', to: 'SAW', attributes: { prediction: 2854 }, }, { - id: 3529, + id: '3529', from: 'SEA', to: 'SMF', attributes: { prediction: 32991 }, }, { - id: 3530, + id: '3530', from: 'OAK', to: 'SNA', attributes: { prediction: 20100 }, }, { - id: 3531, + id: '3531', from: 'WNP', to: 'MNL', attributes: { prediction: 9695 }, }, { - id: 3532, + id: '3532', from: 'DUR', to: 'MQP', attributes: { prediction: 1055 }, }, { - id: 3533, + id: '3533', from: 'AUS', to: 'MSN', attributes: { prediction: 85 }, }, { - id: 3534, + id: '3534', from: 'LAX', to: 'MSO', attributes: { prediction: 903 }, }, { - id: 3535, + id: '3535', from: 'INL', to: 'MSP', attributes: { prediction: 1206 }, }, { - id: 3536, + id: '3536', from: 'MCO', to: 'MSP', attributes: { prediction: 17919 }, }, { - id: 3537, + id: '3537', from: 'IST', to: 'MSQ', attributes: { prediction: 2008 }, }, { - id: 3538, + id: '3538', from: 'JNB', to: 'MTS', attributes: { prediction: 2002 }, }, { - id: 3539, + id: '3539', from: 'DOH', to: 'MUC', attributes: { prediction: 6836 }, }, { - id: 3540, + id: '3540', from: 'LCY', to: 'MUC', attributes: { prediction: 3982 }, }, { - id: 3541, + id: '3541', from: 'OSL', to: 'MUC', attributes: { prediction: 7573 }, }, { - id: 3542, + id: '3542', from: 'TLV', to: 'MUC', attributes: { prediction: 5332 }, }, { - id: 3543, + id: '3543', from: 'TRS', to: 'MUC', attributes: { prediction: 4439 }, }, { - id: 3544, + id: '3544', from: 'CUL', to: 'MXL', attributes: { prediction: 1767 }, }, { - id: 3545, + id: '3545', from: 'BUD', to: 'MXP', attributes: { prediction: 7466 }, }, { - id: 3546, + id: '3546', from: 'WAW', to: 'MXP', attributes: { prediction: 4558 }, }, { - id: 3547, + id: '3547', from: 'MIM', to: 'MYA', attributes: { prediction: 607 }, }, { - id: 3548, + id: '3548', from: 'SHJ', to: 'NAG', attributes: { prediction: 1305 }, }, { - id: 3549, + id: '3549', from: 'SYD', to: 'NAN', attributes: { prediction: 19113 }, }, { - id: 3550, + id: '3550', from: 'NLA', to: 'NBO', attributes: { prediction: 473 }, }, { - id: 3551, + id: '3551', from: 'ABZ', to: 'NCL', attributes: { prediction: 2488 }, }, { - id: 3552, + id: '3552', from: 'SJC', to: 'OGG', attributes: { prediction: 1550 }, }, { - id: 3553, + id: '3553', from: 'HIJ', to: 'OKA', attributes: { prediction: 5806 }, }, { - id: 3554, + id: '3554', from: 'MAN', to: 'SPC', attributes: { prediction: 494 }, }, { - id: 3555, + id: '3555', from: 'FLL', to: 'STL', attributes: { prediction: 4696 }, }, { - id: 3556, + id: '3556', from: 'OPO', to: 'STN', attributes: { prediction: 10759 }, }, { - id: 3557, + id: '3557', from: 'ATL', to: 'STR', attributes: { prediction: 5236 }, }, { - id: 3558, + id: '3558', from: 'CEB', to: 'SUG', attributes: { prediction: 854 }, }, { - id: 3559, + id: '3559', from: 'XKS', to: 'SUR', attributes: { prediction: 66 }, }, { - id: 3560, + id: '3560', from: 'ANU', to: 'SVD', attributes: { prediction: 1343 }, }, { - id: 3561, + id: '3561', from: 'NUX', to: 'SVX', attributes: { prediction: 298 }, }, { - id: 3562, + id: '3562', from: 'ATL', to: 'SWF', attributes: { prediction: 1448 }, }, { - id: 3563, + id: '3563', from: 'SKB', to: 'SXM', attributes: { prediction: 2542 }, }, { - id: 3564, + id: '3564', from: 'VIE', to: 'SZG', attributes: { prediction: 5388 }, }, { - id: 3565, + id: '3565', from: 'RMQ', to: 'SZX', attributes: { prediction: 505 }, }, { - id: 3566, + id: '3566', from: 'APW', to: 'TBU', attributes: { prediction: 1071 }, }, { - id: 3567, + id: '3567', from: 'BDO', to: 'PDG', attributes: { prediction: 679 }, }, { - id: 3568, + id: '3568', from: 'BOI', to: 'PDX', attributes: { prediction: 10798 }, }, { - id: 3569, + id: '3569', from: 'EGX', to: 'PIP', attributes: { prediction: 16 }, }, { - id: 3570, + id: '3570', from: 'DFW', to: 'PIT', attributes: { prediction: 11268 }, }, { - id: 3571, + id: '3571', from: 'PUJ', to: 'PIT', attributes: { prediction: 1091 }, }, { - id: 3572, + id: '3572', from: 'NRN', to: 'PMI', attributes: { prediction: 2487 }, }, { - id: 3573, + id: '3573', from: 'SJO', to: 'PMZ', attributes: { prediction: 249 }, }, { - id: 3574, + id: '3574', from: 'FMN', to: 'PRC', attributes: { prediction: 323 }, }, { - id: 3575, + id: '3575', from: 'BRQ', to: 'PRG', attributes: { prediction: 2476 }, }, { - id: 3576, + id: '3576', from: 'LPL', to: 'TFS', attributes: { prediction: 2457 }, }, { - id: 3577, + id: '3577', from: 'SYZ', to: 'THR', attributes: { prediction: 3653 }, }, { - id: 3578, + id: '3578', from: 'STL', to: 'UIN', attributes: { prediction: 562 }, }, { - id: 3579, + id: '3579', from: 'CTS', to: 'UKB', attributes: { prediction: 11271 }, }, { - id: 3580, + id: '3580', from: 'BYN', to: 'ULN', attributes: { prediction: 131 }, }, { - id: 3581, + id: '3581', from: 'CNX', to: 'USM', attributes: { prediction: 3390 }, }, { - id: 3582, + id: '3582', from: 'VIE', to: 'VAR', attributes: { prediction: 2638 }, }, { - id: 3583, + id: '3583', from: 'MXP', to: 'TIA', attributes: { prediction: 3979 }, }, { - id: 3584, + id: '3584', from: 'DJJ', to: 'TIM', attributes: { prediction: 6082 }, }, { - id: 3585, + id: '3585', from: 'OAK', to: 'TLC', attributes: { prediction: 2011 }, }, { - id: 3586, + id: '3586', from: 'GOT', to: 'TLL', attributes: { prediction: 99 }, }, { - id: 3587, + id: '3587', from: 'EDI', to: 'TMP', attributes: { prediction: 1510 }, }, { - id: 3588, + id: '3588', from: 'CGQ', to: 'TNA', attributes: { prediction: 1855 }, }, { - id: 3589, + id: '3589', from: 'ONT', to: 'SFO', attributes: { prediction: 4824 }, }, { - id: 3590, + id: '3590', from: 'TWB', to: 'SGO', attributes: { prediction: 169 }, }, { - id: 3591, + id: '3591', from: 'HYN', to: 'SHA', attributes: { prediction: 2397 }, }, { - id: 3592, + id: '3592', from: 'OME', to: 'SHH', attributes: { prediction: 339 }, }, { - id: 3593, + id: '3593', from: 'KTM', to: 'SIN', attributes: { prediction: 2364 }, }, { - id: 3594, + id: '3594', from: 'DRK', to: 'SJO', attributes: { prediction: 124 }, }, { - id: 3595, + id: '3595', from: 'TMU', to: 'SJO', attributes: { prediction: 806 }, }, { - id: 3596, + id: '3596', from: 'KTW', to: 'TRN', attributes: { prediction: 653 }, }, { - id: 3597, + id: '3597', from: 'FCO', to: 'TRS', attributes: { prediction: 2268 }, }, { - id: 3598, + id: '3598', from: 'ALA', to: 'TSE', attributes: { prediction: 136 }, }, { - id: 3599, + id: '3599', from: 'MEM', to: 'STL', attributes: { prediction: 3601 }, }, { - id: 3600, + id: '3600', from: 'BDO', to: 'SUB', attributes: { prediction: 6388 }, }, { - id: 3601, + id: '3601', from: 'IXJ', to: 'SXR', attributes: { prediction: 9174 }, }, { - id: 3602, + id: '3602', from: 'NAA', to: 'SYD', attributes: { prediction: 615 }, }, { - id: 3603, + id: '3603', from: 'CLT', to: 'SYR', attributes: { prediction: 5011 }, }, { - id: 3604, + id: '3604', from: 'CAI', to: 'TAI', attributes: { prediction: 476 }, }, { - id: 3605, + id: '3605', from: 'RIX', to: 'TAS', attributes: { prediction: 992 }, }, { - id: 3606, + id: '3606', from: 'TZX', to: 'TBS', attributes: { prediction: 503 }, }, { - id: 3607, + id: '3607', from: 'NGO', to: 'TSN', attributes: { prediction: 4941 }, }, { - id: 3608, + id: '3608', from: 'VRN', to: 'TSR', attributes: { prediction: 936 }, }, { - id: 3609, + id: '3609', from: 'DUS', to: 'TXL', attributes: { prediction: 53030 }, }, { - id: 3610, + id: '3610', from: 'ULG', to: 'ULN', attributes: { prediction: 175 }, }, { - id: 3611, + id: '3611', from: 'YWB', to: 'YQC', attributes: { prediction: 338 }, }, { - id: 3612, + id: '3612', from: 'YYZ', to: 'YTS', attributes: { prediction: 4747 }, }, { - id: 3613, + id: '3613', from: 'YZF', to: 'YVR', attributes: { prediction: 955 }, }, { - id: 3614, + id: '3614', from: 'LIS', to: 'VCE', attributes: { prediction: 3675 }, }, { - id: 3615, + id: '3615', from: 'DEN', to: 'VEL', attributes: { prediction: 457 }, }, { - id: 3616, + id: '3616', from: 'ASU', to: 'VVI', attributes: { prediction: 2605 }, }, { - id: 3617, + id: '3617', from: 'CDG', to: 'WAW', attributes: { prediction: 19511 }, }, { - id: 3618, + id: '3618', from: 'MAD', to: 'WAW', attributes: { prediction: 3282 }, }, { - id: 3619, + id: '3619', from: 'ICN', to: 'WEH', attributes: { prediction: 2897 }, }, { - id: 3620, + id: '3620', from: 'RYG', to: 'WRO', attributes: { prediction: 1515 }, }, { - id: 3621, + id: '3621', from: 'NKG', to: 'XIY', attributes: { prediction: 4554 }, }, { - id: 3622, + id: '3622', from: 'YTL', to: 'XKS', attributes: { prediction: 79 }, }, { - id: 3623, + id: '3623', from: 'TPE', to: 'XMN', attributes: { prediction: 7084 }, }, { - id: 3624, + id: '3624', from: 'YVR', to: 'YBL', attributes: { prediction: 993 }, }, { - id: 3625, + id: '3625', from: 'YVR', to: 'YDQ', attributes: { prediction: 461 }, }, { - id: 3626, + id: '3626', from: 'LAX', to: 'YEG', attributes: { prediction: 1921 }, }, { - id: 3627, + id: '3627', from: 'HKG', to: 'YNZ', attributes: { prediction: 1049 }, }, { - id: 3628, + id: '3628', from: 'YQT', to: 'YOW', attributes: { prediction: 1039 }, }, { - id: 3629, + id: '3629', from: 'LAX', to: 'YYC', attributes: { prediction: 5879 }, }, { - id: 3630, + id: '3630', from: 'YQR', to: 'YYC', attributes: { prediction: 4033 }, }, { - id: 3631, + id: '3631', from: 'ACA', to: 'YYZ', attributes: { prediction: 882 }, }, { - id: 3632, + id: '3632', from: 'ANU', to: 'ATL', attributes: { prediction: 401 }, }, { - id: 3633, + id: '3633', from: 'FWA', to: 'ATL', attributes: { prediction: 2098 }, }, { - id: 3634, + id: '3634', from: 'ATL', to: 'ABE', attributes: { prediction: 3186 }, }, { - id: 3635, + id: '3635', from: 'DTW', to: 'ABE', attributes: { prediction: 4439 }, }, { - id: 3636, + id: '3636', from: 'OUA', to: 'ABJ', attributes: { prediction: 849 }, }, { - id: 3637, + id: '3637', from: 'PIK', to: 'AGP', attributes: { prediction: 2905 }, }, { - id: 3638, + id: '3638', from: 'CLT', to: 'AGS', attributes: { prediction: 4169 }, }, { - id: 3639, + id: '3639', from: 'VBS', to: 'AHO', attributes: { prediction: 2124 }, }, { - id: 3640, + id: '3640', from: 'ADD', to: 'AMM', attributes: { prediction: 4441 }, }, { - id: 3641, + id: '3641', from: 'DME', to: 'AMM', attributes: { prediction: 2184 }, }, { - id: 3642, + id: '3642', from: 'AYT', to: 'AMS', attributes: { prediction: 2653 }, }, { - id: 3643, + id: '3643', from: 'TRF', to: 'AMS', attributes: { prediction: 3062 }, }, { - id: 3644, + id: '3644', from: 'NEV', to: 'ANU', attributes: { prediction: 1072 }, }, { - id: 3645, + id: '3645', from: 'KEF', to: 'ARN', attributes: { prediction: 3868 }, }, { - id: 3646, + id: '3646', from: 'MJV', to: 'BHX', attributes: { prediction: 1738 }, }, { - id: 3647, + id: '3647', from: 'FLL', to: 'BIM', attributes: { prediction: 220 }, }, { - id: 3648, + id: '3648', from: 'KGL', to: 'BJM', attributes: { prediction: 2052 }, }, { - id: 3649, + id: '3649', from: 'AKL', to: 'BNE', attributes: { prediction: 37394 }, }, { - id: 3650, + id: '3650', from: 'DXB', to: 'BNE', attributes: { prediction: 6851 }, }, { - id: 3651, + id: '3651', from: 'GRO', to: 'BOH', attributes: { prediction: 2073 }, }, { - id: 3652, + id: '3652', from: 'JED', to: 'BOM', attributes: { prediction: 8414 }, }, { - id: 3653, + id: '3653', from: 'HYA', to: 'BOS', attributes: { prediction: 517 }, }, { - id: 3654, + id: '3654', from: 'IAH', to: 'BOS', attributes: { prediction: 22507 }, }, { - id: 3655, + id: '3655', from: 'EWR', to: 'BQN', attributes: { prediction: 2719 }, }, { - id: 3656, + id: '3656', from: 'AEP', to: 'BRC', attributes: { prediction: 13643 }, }, { - id: 3657, + id: '3657', from: 'SFB', to: 'CID', attributes: { prediction: 1139 }, }, { - id: 3658, + id: '3658', from: 'HKG', to: 'CLE', attributes: { prediction: 237 }, }, { - id: 3659, + id: '3659', from: 'SXM', to: 'CLT', attributes: { prediction: 5680 }, }, { - id: 3660, + id: '3660', from: 'DEN', to: 'CMH', attributes: { prediction: 6166 }, }, { - id: 3661, + id: '3661', from: 'CRL', to: 'CMN', attributes: { prediction: 4765 }, }, { - id: 3662, + id: '3662', from: 'BKO', to: 'COO', attributes: { prediction: 540 }, }, { - id: 3663, + id: '3663', from: 'PMI', to: 'CPH', attributes: { prediction: 965 }, }, { - id: 3664, + id: '3664', from: 'BOM', to: 'DXB', attributes: { prediction: 60277 }, }, { - id: 3665, + id: '3665', from: 'PEW', to: 'DXB', attributes: { prediction: 5894 }, }, { - id: 3666, + id: '3666', from: 'MAN', to: 'EDI', attributes: { prediction: 4712 }, }, { - id: 3667, + id: '3667', from: 'PRG', to: 'EDI', attributes: { prediction: 2389 }, }, { - id: 3668, + id: '3668', from: 'CAN', to: 'DAC', attributes: { prediction: 2171 }, }, { - id: 3669, + id: '3669', from: 'SGN', to: 'DAD', attributes: { prediction: 32295 }, }, { - id: 3670, + id: '3670', from: 'IXU', to: 'DEL', attributes: { prediction: 1221 }, }, { - id: 3671, + id: '3671', from: 'IAD', to: 'DFW', attributes: { prediction: 26287 }, }, { - id: 3672, + id: '3672', from: 'ONT', to: 'DFW', attributes: { prediction: 17616 }, }, { - id: 3673, + id: '3673', from: 'ORF', to: 'DFW', attributes: { prediction: 7486 }, }, { - id: 3674, + id: '3674', from: 'NRT', to: 'DLC', attributes: { prediction: 16594 }, }, { - id: 3675, + id: '3675', from: 'WUH', to: 'DLC', attributes: { prediction: 3614 }, }, { - id: 3676, + id: '3676', from: 'ISA', to: 'DMD', attributes: { prediction: 666 }, }, { - id: 3677, + id: '3677', from: 'HKT', to: 'DMK', attributes: { prediction: 4603 }, }, { - id: 3678, + id: '3678', from: 'MUC', to: 'DOH', attributes: { prediction: 8432 }, }, { - id: 3679, + id: '3679', from: 'PER', to: 'DRW', attributes: { prediction: 8287 }, }, { - id: 3680, + id: '3680', from: 'SSH', to: 'DSA', attributes: { prediction: 1352 }, }, { - id: 3681, + id: '3681', from: 'ATL', to: 'ELP', attributes: { prediction: 6942 }, }, { - id: 3682, + id: '3682', from: 'TPE', to: 'EWR', attributes: { prediction: 4920 }, }, { - id: 3683, + id: '3683', from: 'YYC', to: 'EWR', attributes: { prediction: 1819 }, }, { - id: 3684, + id: '3684', from: 'PVD', to: 'GRR', attributes: { prediction: 93 }, }, { - id: 3685, + id: '3685', from: 'ORD', to: 'GRU', attributes: { prediction: 5511 }, }, { - id: 3686, + id: '3686', from: 'ATL', to: 'GTR', attributes: { prediction: 1519 }, }, { - id: 3687, + id: '3687', from: 'CPH', to: 'GVA', attributes: { prediction: 5632 }, }, { - id: 3688, + id: '3688', from: 'BOG', to: 'GYE', attributes: { prediction: 7073 }, }, { - id: 3689, + id: '3689', from: 'AMM', to: 'FCO', attributes: { prediction: 3027 }, }, { - id: 3690, + id: '3690', from: 'DUJ', to: 'FKL', attributes: { prediction: 432 }, }, { - id: 3691, + id: '3691', from: 'FLL', to: 'FPO', attributes: { prediction: 2454 }, }, { - id: 3692, + id: '3692', from: 'KRK', to: 'FRA', attributes: { prediction: 3481 }, }, { - id: 3693, + id: '3693', from: 'LNK', to: 'GEG', attributes: { prediction: 28 }, }, { - id: 3694, + id: '3694', from: 'ORD', to: 'GLA', attributes: { prediction: 146 }, }, { - id: 3695, + id: '3695', from: 'ALC', to: 'GOT', attributes: { prediction: 930 }, }, { - id: 3696, + id: '3696', from: 'HND', to: 'HAC', attributes: { prediction: 4801 }, }, { - id: 3697, + id: '3697', from: 'ANI', to: 'HCR', attributes: { prediction: 106 }, }, { - id: 3698, + id: '3698', from: 'CDG', to: 'HEL', attributes: { prediction: 18513 }, }, { - id: 3699, + id: '3699', from: 'JFK', to: 'HEL', attributes: { prediction: 5578 }, }, { - id: 3700, + id: '3700', from: 'ATH', to: 'HER', attributes: { prediction: 39730 }, }, { - id: 3701, + id: '3701', from: 'KUL', to: 'HGH', attributes: { prediction: 8855 }, }, { - id: 3702, + id: '3702', from: 'WNZ', to: 'HGH', attributes: { prediction: 3917 }, }, { - id: 3703, + id: '3703', from: 'MEX', to: 'JAL', attributes: { prediction: 1389 }, }, { - id: 3704, + id: '3704', from: 'ALF', to: 'HVG', attributes: { prediction: 33 }, }, { - id: 3705, + id: '3705', from: 'AMA', to: 'IAH', attributes: { prediction: 5713 }, }, { - id: 3706, + id: '3706', from: 'GOT', to: 'IKA', attributes: { prediction: 678 }, }, { - id: 3707, + id: '3707', from: 'GRU', to: 'IPN', attributes: { prediction: 467 }, }, { - id: 3708, + id: '3708', from: 'MSQ', to: 'IST', attributes: { prediction: 1985 }, }, { - id: 3709, + id: '3709', from: 'MXP', to: 'IST', attributes: { prediction: 12676 }, }, { - id: 3710, + id: '3710', from: 'NBO', to: 'IST', attributes: { prediction: 3242 }, }, { - id: 3711, + id: '3711', from: 'CHC', to: 'IVC', attributes: { prediction: 10577 }, }, { - id: 3712, + id: '3712', from: 'DEL', to: 'IXB', attributes: { prediction: 10721 }, }, { - id: 3713, + id: '3713', from: 'BDA', to: 'JFK', attributes: { prediction: 3256 }, }, { - id: 3714, + id: '3714', from: 'LKO', to: 'MCT', attributes: { prediction: 4126 }, }, { - id: 3715, + id: '3715', from: 'YYC', to: 'MDW', attributes: { prediction: 29 }, }, { - id: 3716, + id: '3716', from: 'KWI', to: 'MED', attributes: { prediction: 421 }, }, { - id: 3717, + id: '3717', from: 'BIR', to: 'KTM', attributes: { prediction: 6804 }, }, { - id: 3718, + id: '3718', from: 'KTM', to: 'KUL', attributes: { prediction: 4545 }, }, { - id: 3719, + id: '3719', from: 'KHZ', to: 'KXU', attributes: { prediction: 192 }, }, { - id: 3720, + id: '3720', from: 'CPR', to: 'LAS', attributes: { prediction: 977 }, }, { - id: 3721, + id: '3721', from: 'GTF', to: 'LAS', attributes: { prediction: 2215 }, }, { - id: 3722, + id: '3722', from: 'SJC', to: 'LAS', attributes: { prediction: 25188 }, }, { - id: 3723, + id: '3723', from: 'ZLO', to: 'LAX', attributes: { prediction: 1915 }, }, { - id: 3724, + id: '3724', from: 'CDG', to: 'LBV', attributes: { prediction: 4671 }, }, { - id: 3725, + id: '3725', from: 'GJA', to: 'LCE', attributes: { prediction: 300 }, }, { - id: 3726, + id: '3726', from: 'ACR', to: 'LCR', attributes: { prediction: 104 }, }, { - id: 3727, + id: '3727', from: 'KGD', to: 'LED', attributes: { prediction: 8662 }, }, { - id: 3728, + id: '3728', from: 'PEK', to: 'LED', attributes: { prediction: 2765 }, }, { - id: 3729, + id: '3729', from: 'ROV', to: 'LED', attributes: { prediction: 1348 }, }, { - id: 3730, + id: '3730', from: 'CVG', to: 'LEX', attributes: { prediction: 3156 }, }, { - id: 3731, + id: '3731', from: 'AZD', to: 'MHD', attributes: { prediction: 1962 }, }, { - id: 3732, + id: '3732', from: 'CCS', to: 'LIS', attributes: { prediction: 2770 }, }, { - id: 3733, + id: '3733', from: 'LAP', to: 'LMM', attributes: { prediction: 1166 }, }, { - id: 3734, + id: '3734', from: 'STN', to: 'LPA', attributes: { prediction: 3213 }, }, { - id: 3735, + id: '3735', from: 'VIE', to: 'LPA', attributes: { prediction: 736 }, }, { - id: 3736, + id: '3736', from: 'DTW', to: 'MAD', attributes: { prediction: 153 }, }, { - id: 3737, + id: '3737', from: 'ORD', to: 'MAD', attributes: { prediction: 5248 }, }, { - id: 3738, + id: '3738', from: 'LAS', to: 'MAF', attributes: { prediction: 3333 }, }, { - id: 3739, + id: '3739', from: 'LHR', to: 'MAN', attributes: { prediction: 39824 }, }, { - id: 3740, + id: '3740', from: 'MAN', to: 'MBJ', attributes: { prediction: 917 }, }, { - id: 3741, + id: '3741', from: 'MEM', to: 'MIA', attributes: { prediction: 6448 }, }, { - id: 3742, + id: '3742', from: 'BFS', to: 'MJV', attributes: { prediction: 1430 }, }, { - id: 3743, + id: '3743', from: 'FNT', to: 'MKE', attributes: { prediction: 2526 }, }, { - id: 3744, + id: '3744', from: 'VLI', to: 'NOU', attributes: { prediction: 1958 }, }, { - id: 3745, + id: '3745', from: 'WLS', to: 'NOU', attributes: { prediction: 584 }, }, { - id: 3746, + id: '3746', from: 'PMI', to: 'NRN', attributes: { prediction: 3238 }, }, { - id: 3747, + id: '3747', from: 'NOU', to: 'NRT', attributes: { prediction: 3006 }, }, { - id: 3748, + id: '3748', from: 'TLC', to: 'OAK', attributes: { prediction: 2179 }, }, { - id: 3749, + id: '3749', from: 'DTW', to: 'OMA', attributes: { prediction: 4407 }, }, { - id: 3750, + id: '3750', from: 'TVC', to: 'MSP', attributes: { prediction: 852 }, }, { - id: 3751, + id: '3751', from: 'RIX', to: 'MUC', attributes: { prediction: 3231 }, }, { - id: 3752, + id: '3752', from: 'NAT', to: 'MXP', attributes: { prediction: 1123 }, }, { - id: 3753, + id: '3753', from: 'SEZ', to: 'NBO', attributes: { prediction: 1044 }, }, { - id: 3754, + id: '3754', from: 'GUM', to: 'NGO', attributes: { prediction: 12174 }, }, { - id: 3755, + id: '3755', from: 'TUS', to: 'ORD', attributes: { prediction: 6339 }, }, { - id: 3756, + id: '3756', from: 'AES', to: 'OSL', attributes: { prediction: 11922 }, }, { - id: 3757, + id: '3757', from: 'KRS', to: 'OSL', attributes: { prediction: 8710 }, }, { - id: 3758, + id: '3758', from: 'IST', to: 'OTP', attributes: { prediction: 14043 }, }, { - id: 3759, + id: '3759', from: 'DRG', to: 'OTZ', attributes: { prediction: 274 }, }, { - id: 3760, + id: '3760', from: 'KAJ', to: 'OUL', attributes: { prediction: 85 }, }, { - id: 3761, + id: '3761', from: 'TKU', to: 'OUL', attributes: { prediction: 1120 }, }, { - id: 3762, + id: '3762', from: 'SCL', to: 'PUJ', attributes: { prediction: 825 }, }, { - id: 3763, + id: '3763', from: 'ABQ', to: 'PDX', attributes: { prediction: 2949 }, }, { - id: 3764, + id: '3764', from: 'JFK', to: 'PDX', attributes: { prediction: 4281 }, }, { - id: 3765, + id: '3765', from: 'FNJ', to: 'PEK', attributes: { prediction: 1246 }, }, { - id: 3766, + id: '3766', from: 'SOV', to: 'PGM', attributes: { prediction: 7 }, }, { - id: 3767, + id: '3767', from: 'EWR', to: 'PHL', attributes: { prediction: 4833 }, }, { - id: 3768, + id: '3768', from: 'MCI', to: 'PHL', attributes: { prediction: 3084 }, }, { - id: 3769, + id: '3769', from: 'GYM', to: 'PHX', attributes: { prediction: 465 }, }, { - id: 3770, + id: '3770', from: 'BGR', to: 'PIE', attributes: { prediction: 1350 }, }, { - id: 3771, + id: '3771', from: 'DOM', to: 'PMV', attributes: { prediction: 166 }, }, { - id: 3772, + id: '3772', from: 'VTE', to: 'PNH', attributes: { prediction: 3600 }, }, { - id: 3773, + id: '3773', from: 'KVG', to: 'POM', attributes: { prediction: 388 }, }, { - id: 3774, + id: '3774', from: 'TPI', to: 'POM', attributes: { prediction: 180 }, }, { - id: 3775, + id: '3775', from: 'AXR', to: 'PPT', attributes: { prediction: 192 }, }, { - id: 3776, + id: '3776', from: 'RFP', to: 'PPT', attributes: { prediction: 6828 }, }, { - id: 3777, + id: '3777', from: 'TIH', to: 'PPT', attributes: { prediction: 1087 }, }, { - id: 3778, + id: '3778', from: 'BHX', to: 'PSA', attributes: { prediction: 1239 }, }, { - id: 3779, + id: '3779', from: 'SRZ', to: 'PSZ', attributes: { prediction: 424 }, }, { - id: 3780, + id: '3780', from: 'GTA', to: 'RBV', attributes: { prediction: 50 }, }, { - id: 3781, + id: '3781', from: 'PHX', to: 'RDU', attributes: { prediction: 3190 }, }, { - id: 3782, + id: '3782', from: 'LPQ', to: 'REP', attributes: { prediction: 1082 }, }, { - id: 3783, + id: '3783', from: 'MEX', to: 'SCL', attributes: { prediction: 8822 }, }, { - id: 3784, + id: '3784', from: 'YKM', to: 'SEA', attributes: { prediction: 4388 }, }, { - id: 3785, + id: '3785', from: 'LAS', to: 'SFO', attributes: { prediction: 62881 }, }, { - id: 3786, + id: '3786', from: 'VKG', to: 'SGN', attributes: { prediction: 1248 }, }, { - id: 3787, + id: '3787', from: 'JJN', to: 'SHA', attributes: { prediction: 3616 }, }, { - id: 3788, + id: '3788', from: 'BAV', to: 'SHE', attributes: { prediction: 1187 }, }, { - id: 3789, + id: '3789', from: 'BNE', to: 'SIN', attributes: { prediction: 37681 }, }, { - id: 3790, + id: '3790', from: 'FUK', to: 'SIN', attributes: { prediction: 9219 }, }, { - id: 3791, + id: '3791', from: 'PNH', to: 'SIN', attributes: { prediction: 8072 }, }, { - id: 3792, + id: '3792', from: 'SMF', to: 'SJD', attributes: { prediction: 1920 }, }, { - id: 3793, + id: '3793', from: 'IAH', to: 'SJO', attributes: { prediction: 13188 }, }, { - id: 3794, + id: '3794', from: 'NOB', to: 'SJO', attributes: { prediction: 134 }, }, { - id: 3795, + id: '3795', from: 'SJD', to: 'SLC', attributes: { prediction: 496 }, }, { - id: 3796, + id: '3796', from: 'ATL', to: 'YUL', attributes: { prediction: 6133 }, }, { - id: 3797, + id: '3797', from: 'YSJ', to: 'YUL', attributes: { prediction: 2420 }, }, { - id: 3798, + id: '3798', from: 'YYZ', to: 'YUL', attributes: { prediction: 67535 }, }, { - id: 3799, + id: '3799', from: 'YEV', to: 'YVQ', attributes: { prediction: 694 }, }, { - id: 3800, + id: '3800', from: 'YRT', to: 'YWG', attributes: { prediction: 1402 }, }, { - id: 3801, + id: '3801', from: 'AGU', to: 'TIJ', attributes: { prediction: 1950 }, }, { - id: 3802, + id: '3802', from: 'DAM', to: 'TIP', attributes: { prediction: 2329 }, }, { - id: 3803, + id: '3803', from: 'EWR', to: 'TLV', attributes: { prediction: 25294 }, }, { - id: 3804, + id: '3804', from: 'IAH', to: 'TPA', attributes: { prediction: 24019 }, }, { - id: 3805, + id: '3805', from: 'CDG', to: 'TRN', attributes: { prediction: 15497 }, }, { - id: 3806, + id: '3806', from: 'AOI', to: 'TSR', attributes: { prediction: 1190 }, }, { - id: 3807, + id: '3807', from: 'MUC', to: 'TSR', attributes: { prediction: 7243 }, }, { - id: 3808, + id: '3808', from: 'TOG', to: 'TWA', attributes: { prediction: 36 }, }, { - id: 3809, + id: '3809', from: 'GRU', to: 'UBA', attributes: { prediction: 473 }, }, { - id: 3810, + id: '3810', from: 'YDQ', to: 'YYE', attributes: { prediction: 325 }, }, { - id: 3811, + id: '3811', from: 'GEO', to: 'YYZ', attributes: { prediction: 777 }, }, { - id: 3812, + id: '3812', from: 'GRR', to: 'YYZ', attributes: { prediction: 647 }, }, { - id: 3813, + id: '3813', from: 'LAS', to: 'YYZ', attributes: { prediction: 7856 }, }, { - id: 3814, + id: '3814', from: 'YQG', to: 'YYZ', attributes: { prediction: 3997 }, }, { - id: 3815, + id: '3815', from: 'YXU', to: 'YYZ', attributes: { prediction: 5558 }, }, { - id: 3816, + id: '3816', from: 'YCO', to: 'YZF', attributes: { prediction: 1363 }, }, { - id: 3817, + id: '3817', from: 'GRZ', to: 'VIE', attributes: { prediction: 5844 }, }, { - id: 3818, + id: '3818', from: 'KRR', to: 'VIE', attributes: { prediction: 2058 }, }, { - id: 3819, + id: '3819', from: 'VQS', to: 'VIJ', attributes: { prediction: 311 }, }, { - id: 3820, + id: '3820', from: 'TSV', to: 'WIN', attributes: { prediction: 302 }, }, { - id: 3821, + id: '3821', from: 'NPE', to: 'WLG', attributes: { prediction: 7849 }, }, { - id: 3822, + id: '3822', from: 'KTN', to: 'WMK', attributes: { prediction: 19 }, }, { - id: 3823, + id: '3823', from: 'GDT', to: 'XSC', attributes: { prediction: 312 }, }, { - id: 3824, + id: '3824', from: 'YQT', to: 'YFH', attributes: { prediction: 728 }, }, { - id: 3825, + id: '3825', from: 'YVO', to: 'YHU', attributes: { prediction: 138 }, }, { - id: 3826, + id: '3826', from: 'ICN', to: 'YNJ', attributes: { prediction: 8082 }, }, { - id: 3827, + id: '3827', from: 'YPX', to: 'YZG', attributes: { prediction: 798 }, }, { - id: 3828, + id: '3828', from: 'YNC', to: 'ZEM', attributes: { prediction: 375 }, }, { - id: 3829, + id: '3829', from: 'FRA', to: 'ZRH', attributes: { prediction: 27983 }, }, { - id: 3830, + id: '3830', from: 'OPO', to: 'ZRH', attributes: { prediction: 3531 }, diff --git a/libs/shared/lib/mock-data/query-result/index.ts b/libs/shared/lib/mock-data/query-result/index.ts index 33ea29d3a..a4b9292b5 100644 --- a/libs/shared/lib/mock-data/query-result/index.ts +++ b/libs/shared/lib/mock-data/query-result/index.ts @@ -1,5 +1,5 @@ -export * from './mockLargeQueryResults' -export * from './big2ndChamberQueryResult' -export * from './bigMockQueryResults' -export * from './mockQueryResults' -export * from './smallFlightsQueryResults' \ No newline at end of file +export * from './mockLargeQueryResults'; +export * from './big2ndChamberQueryResult'; +export * from './bigMockQueryResults'; +export * from './mockQueryResults'; +export * from './smallFlightsQueryResults'; diff --git a/libs/shared/lib/mock-data/query-result/mockLargeQueryResults.ts b/libs/shared/lib/mock-data/query-result/mockLargeQueryResults.ts index 34044c680..94b37d591 100644 --- a/libs/shared/lib/mock-data/query-result/mockLargeQueryResults.ts +++ b/libs/shared/lib/mock-data/query-result/mockLargeQueryResults.ts @@ -4,4757 +4,4253 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { GraphQueryResultFromBackend } from "../../data-access/store/graphQueryResultSlice"; +import { GraphQueryResultFromBackend } from '../../data-access/store/graphQueryResultSlice'; export const mockLargeQueryResults: GraphQueryResultFromBackend = { - "edges": [ + edges: [ { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14514", - "to": "855" + from: '219', + id: '14514', + to: '855', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14570", - "to": "883" + from: '219', + id: '14570', + to: '883', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14558", - "to": "877" + from: '219', + id: '14558', + to: '877', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14452", - "to": "824" + from: '219', + id: '14452', + to: '824', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14582", - "to": "889" + from: '219', + id: '14582', + to: '889', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14498", - "to": "847" + from: '219', + id: '14498', + to: '847', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14526", - "to": "861" + from: '219', + id: '14526', + to: '861', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14600", - "to": "898" + from: '219', + id: '14600', + to: '898', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14472", - "to": "834" + from: '219', + id: '14472', + to: '834', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14520", - "to": "858" + from: '219', + id: '14520', + to: '858', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14580", - "to": "888" + from: '219', + id: '14580', + to: '888', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14604", - "to": "900" + from: '219', + id: '14604', + to: '900', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14524", - "to": "860" + from: '219', + id: '14524', + to: '860', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14528", - "to": "862" + from: '219', + id: '14528', + to: '862', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14586", - "to": "891" + from: '219', + id: '14586', + to: '891', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14460", - "to": "828" + from: '219', + id: '14460', + to: '828', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14506", - "to": "851" + from: '219', + id: '14506', + to: '851', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14568", - "to": "882" + from: '219', + id: '14568', + to: '882', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14598", - "to": "897" + from: '219', + id: '14598', + to: '897', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14606", - "to": "901" + from: '219', + id: '14606', + to: '901', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14538", - "to": "867" + from: '219', + id: '14538', + to: '867', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14512", - "to": "854" + from: '219', + id: '14512', + to: '854', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14560", - "to": "878" + from: '219', + id: '14560', + to: '878', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14482", - "to": "839" + from: '219', + id: '14482', + to: '839', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14522", - "to": "859" + from: '219', + id: '14522', + to: '859', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14546", - "to": "871" + from: '219', + id: '14546', + to: '871', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14478", - "to": "837" + from: '219', + id: '14478', + to: '837', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14572", - "to": "884" + from: '219', + id: '14572', + to: '884', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14504", - "to": "850" + from: '219', + id: '14504', + to: '850', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14464", - "to": "830" + from: '219', + id: '14464', + to: '830', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14510", - "to": "853" + from: '219', + id: '14510', + to: '853', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14562", - "to": "879" + from: '219', + id: '14562', + to: '879', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14552", - "to": "874" + from: '219', + id: '14552', + to: '874', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14592", - "to": "894" + from: '219', + id: '14592', + to: '894', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14496", - "to": "846" + from: '219', + id: '14496', + to: '846', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14556", - "to": "876" + from: '219', + id: '14556', + to: '876', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14566", - "to": "881" + from: '219', + id: '14566', + to: '881', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14594", - "to": "895" + from: '219', + id: '14594', + to: '895', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14516", - "to": "856" + from: '219', + id: '14516', + to: '856', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14576", - "to": "886" + from: '219', + id: '14576', + to: '886', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14564", - "to": "880" + from: '219', + id: '14564', + to: '880', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14500", - "to": "848" + from: '219', + id: '14500', + to: '848', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14540", - "to": "868" + from: '219', + id: '14540', + to: '868', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14492", - "to": "844" + from: '219', + id: '14492', + to: '844', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14470", - "to": "833" + from: '219', + id: '14470', + to: '833', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14608", - "to": "902" + from: '219', + id: '14608', + to: '902', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14458", - "to": "827" + from: '219', + id: '14458', + to: '827', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14518", - "to": "857" + from: '219', + id: '14518', + to: '857', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14584", - "to": "890" + from: '219', + id: '14584', + to: '890', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14474", - "to": "835" + from: '219', + id: '14474', + to: '835', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14550", - "to": "873" + from: '219', + id: '14550', + to: '873', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14462", - "to": "829" + from: '219', + id: '14462', + to: '829', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14536", - "to": "866" + from: '219', + id: '14536', + to: '866', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14534", - "to": "865" + from: '219', + id: '14534', + to: '865', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14548", - "to": "872" + from: '219', + id: '14548', + to: '872', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14542", - "to": "869" + from: '219', + id: '14542', + to: '869', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14530", - "to": "863" + from: '219', + id: '14530', + to: '863', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14486", - "to": "841" + from: '219', + id: '14486', + to: '841', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14554", - "to": "875" + from: '219', + id: '14554', + to: '875', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14602", - "to": "899" + from: '219', + id: '14602', + to: '899', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14588", - "to": "892" + from: '219', + id: '14588', + to: '892', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14484", - "to": "840" + from: '219', + id: '14484', + to: '840', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14502", - "to": "849" + from: '219', + id: '14502', + to: '849', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14454", - "to": "825" + from: '219', + id: '14454', + to: '825', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14476", - "to": "836" + from: '219', + id: '14476', + to: '836', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14488", - "to": "842" + from: '219', + id: '14488', + to: '842', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14490", - "to": "843" + from: '219', + id: '14490', + to: '843', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14466", - "to": "831" + from: '219', + id: '14466', + to: '831', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14508", - "to": "852" + from: '219', + id: '14508', + to: '852', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14574", - "to": "885" + from: '219', + id: '14574', + to: '885', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14480", - "to": "838" + from: '219', + id: '14480', + to: '838', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14544", - "to": "870" + from: '219', + id: '14544', + to: '870', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14468", - "to": "832" + from: '219', + id: '14468', + to: '832', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14578", - "to": "887" + from: '219', + id: '14578', + to: '887', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14532", - "to": "864" + from: '219', + id: '14532', + to: '864', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14456", - "to": "826" + from: '219', + id: '14456', + to: '826', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14590", - "to": "893" + from: '219', + id: '14590', + to: '893', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14596", - "to": "896" + from: '219', + id: '14596', + to: '896', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "219", - "id": "14494", - "to": "845" + from: '219', + id: '14494', + to: '845', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14650", - "to": "923" + from: '220', + id: '14650', + to: '923', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14654", - "to": "925" + from: '220', + id: '14654', + to: '925', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14756", - "to": "976" + from: '220', + id: '14756', + to: '976', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14698", - "to": "947" + from: '220', + id: '14698', + to: '947', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14704", - "to": "950" + from: '220', + id: '14704', + to: '950', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14746", - "to": "971" + from: '220', + id: '14746', + to: '971', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14640", - "to": "918" + from: '220', + id: '14640', + to: '918', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14732", - "to": "964" + from: '220', + id: '14732', + to: '964', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14626", - "to": "911" + from: '220', + id: '14626', + to: '911', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14730", - "to": "963" + from: '220', + id: '14730', + to: '963', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14684", - "to": "940" + from: '220', + id: '14684', + to: '940', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14666", - "to": "931" + from: '220', + id: '14666', + to: '931', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14766", - "to": "981" + from: '220', + id: '14766', + to: '981', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14610", - "to": "903" + from: '220', + id: '14610', + to: '903', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14676", - "to": "936" + from: '220', + id: '14676', + to: '936', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14632", - "to": "914" + from: '220', + id: '14632', + to: '914', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14708", - "to": "952" + from: '220', + id: '14708', + to: '952', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14616", - "to": "906" + from: '220', + id: '14616', + to: '906', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14622", - "to": "909" + from: '220', + id: '14622', + to: '909', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14712", - "to": "954" + from: '220', + id: '14712', + to: '954', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14738", - "to": "967" + from: '220', + id: '14738', + to: '967', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14618", - "to": "907" + from: '220', + id: '14618', + to: '907', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14740", - "to": "968" + from: '220', + id: '14740', + to: '968', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14752", - "to": "974" + from: '220', + id: '14752', + to: '974', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14702", - "to": "949" + from: '220', + id: '14702', + to: '949', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14644", - "to": "920" + from: '220', + id: '14644', + to: '920', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14762", - "to": "979" + from: '220', + id: '14762', + to: '979', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14628", - "to": "912" + from: '220', + id: '14628', + to: '912', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14744", - "to": "970" + from: '220', + id: '14744', + to: '970', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14764", - "to": "980" + from: '220', + id: '14764', + to: '980', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14748", - "to": "972" + from: '220', + id: '14748', + to: '972', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14646", - "to": "921" + from: '220', + id: '14646', + to: '921', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14694", - "to": "945" + from: '220', + id: '14694', + to: '945', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14664", - "to": "930" + from: '220', + id: '14664', + to: '930', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14726", - "to": "961" + from: '220', + id: '14726', + to: '961', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14662", - "to": "929" + from: '220', + id: '14662', + to: '929', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14642", - "to": "919" + from: '220', + id: '14642', + to: '919', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14680", - "to": "938" + from: '220', + id: '14680', + to: '938', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14722", - "to": "959" + from: '220', + id: '14722', + to: '959', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14750", - "to": "973" + from: '220', + id: '14750', + to: '973', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14612", - "to": "904" + from: '220', + id: '14612', + to: '904', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14636", - "to": "916" + from: '220', + id: '14636', + to: '916', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14634", - "to": "915" + from: '220', + id: '14634', + to: '915', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14710", - "to": "953" + from: '220', + id: '14710', + to: '953', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14620", - "to": "908" + from: '220', + id: '14620', + to: '908', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14670", - "to": "933" + from: '220', + id: '14670', + to: '933', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14652", - "to": "924" + from: '220', + id: '14652', + to: '924', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14656", - "to": "926" + from: '220', + id: '14656', + to: '926', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14624", - "to": "910" + from: '220', + id: '14624', + to: '910', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14674", - "to": "935" + from: '220', + id: '14674', + to: '935', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14706", - "to": "951" + from: '220', + id: '14706', + to: '951', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14678", - "to": "937" + from: '220', + id: '14678', + to: '937', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14728", - "to": "962" + from: '220', + id: '14728', + to: '962', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14668", - "to": "932" + from: '220', + id: '14668', + to: '932', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14742", - "to": "969" + from: '220', + id: '14742', + to: '969', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14714", - "to": "955" + from: '220', + id: '14714', + to: '955', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14688", - "to": "942" + from: '220', + id: '14688', + to: '942', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14716", - "to": "956" + from: '220', + id: '14716', + to: '956', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14690", - "to": "943" + from: '220', + id: '14690', + to: '943', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14672", - "to": "934" + from: '220', + id: '14672', + to: '934', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14734", - "to": "965" + from: '220', + id: '14734', + to: '965', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14724", - "to": "960" + from: '220', + id: '14724', + to: '960', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14760", - "to": "978" + from: '220', + id: '14760', + to: '978', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14630", - "to": "913" + from: '220', + id: '14630', + to: '913', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14736", - "to": "966" + from: '220', + id: '14736', + to: '966', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14686", - "to": "941" + from: '220', + id: '14686', + to: '941', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14682", - "to": "939" + from: '220', + id: '14682', + to: '939', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14768", - "to": "982" + from: '220', + id: '14768', + to: '982', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14758", - "to": "977" + from: '220', + id: '14758', + to: '977', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14700", - "to": "948" + from: '220', + id: '14700', + to: '948', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14720", - "to": "958" + from: '220', + id: '14720', + to: '958', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14718", - "to": "957" + from: '220', + id: '14718', + to: '957', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14692", - "to": "944" + from: '220', + id: '14692', + to: '944', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14614", - "to": "905" + from: '220', + id: '14614', + to: '905', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14658", - "to": "927" + from: '220', + id: '14658', + to: '927', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14660", - "to": "928" + from: '220', + id: '14660', + to: '928', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14696", - "to": "946" + from: '220', + id: '14696', + to: '946', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14638", - "to": "917" + from: '220', + id: '14638', + to: '917', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14754", - "to": "975" + from: '220', + id: '14754', + to: '975', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "220", - "id": "14648", - "to": "922" + from: '220', + id: '14648', + to: '922', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14784", - "to": "990" + from: '221', + id: '14784', + to: '990', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14770", - "to": "983" + from: '221', + id: '14770', + to: '983', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14786", - "to": "991" + from: '221', + id: '14786', + to: '991', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14780", - "to": "988" + from: '221', + id: '14780', + to: '988', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14776", - "to": "986" + from: '221', + id: '14776', + to: '986', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14774", - "to": "985" + from: '221', + id: '14774', + to: '985', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14782", - "to": "989" + from: '221', + id: '14782', + to: '989', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14790", - "to": "993" + from: '221', + id: '14790', + to: '993', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14772", - "to": "984" + from: '221', + id: '14772', + to: '984', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14778", - "to": "987" + from: '221', + id: '14778', + to: '987', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "221", - "id": "14788", - "to": "992" + from: '221', + id: '14788', + to: '992', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14866", - "to": "1031" + from: '223', + id: '14866', + to: '1031', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14900", - "to": "1048" + from: '223', + id: '14900', + to: '1048', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14856", - "to": "1026" + from: '223', + id: '14856', + to: '1026', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14840", - "to": "1018" + from: '223', + id: '14840', + to: '1018', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14942", - "to": "1069" + from: '223', + id: '14942', + to: '1069', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14904", - "to": "1050" + from: '223', + id: '14904', + to: '1050', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14836", - "to": "1016" + from: '223', + id: '14836', + to: '1016', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14874", - "to": "1035" + from: '223', + id: '14874', + to: '1035', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14810", - "to": "1003" + from: '223', + id: '14810', + to: '1003', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14858", - "to": "1027" + from: '223', + id: '14858', + to: '1027', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14872", - "to": "1034" + from: '223', + id: '14872', + to: '1034', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14846", - "to": "1021" + from: '223', + id: '14846', + to: '1021', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14808", - "to": "1002" + from: '223', + id: '14808', + to: '1002', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14860", - "to": "1028" + from: '223', + id: '14860', + to: '1028', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14804", - "to": "1000" + from: '223', + id: '14804', + to: '1000', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14820", - "to": "1008" + from: '223', + id: '14820', + to: '1008', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14884", - "to": "1040" + from: '223', + id: '14884', + to: '1040', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14896", - "to": "1046" + from: '223', + id: '14896', + to: '1046', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14882", - "to": "1039" + from: '223', + id: '14882', + to: '1039', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14818", - "to": "1007" + from: '223', + id: '14818', + to: '1007', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14794", - "to": "995" + from: '223', + id: '14794', + to: '995', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14830", - "to": "1013" + from: '223', + id: '14830', + to: '1013', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14902", - "to": "1049" + from: '223', + id: '14902', + to: '1049', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14908", - "to": "1052" + from: '223', + id: '14908', + to: '1052', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14792", - "to": "994" + from: '223', + id: '14792', + to: '994', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14834", - "to": "1015" + from: '223', + id: '14834', + to: '1015', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14898", - "to": "1047" + from: '223', + id: '14898', + to: '1047', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14924", - "to": "1060" + from: '223', + id: '14924', + to: '1060', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14864", - "to": "1030" + from: '223', + id: '14864', + to: '1030', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14892", - "to": "1044" + from: '223', + id: '14892', + to: '1044', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14850", - "to": "1023" + from: '223', + id: '14850', + to: '1023', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14852", - "to": "1024" + from: '223', + id: '14852', + to: '1024', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14848", - "to": "1022" + from: '223', + id: '14848', + to: '1022', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14844", - "to": "1020" + from: '223', + id: '14844', + to: '1020', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14842", - "to": "1019" + from: '223', + id: '14842', + to: '1019', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14862", - "to": "1029" + from: '223', + id: '14862', + to: '1029', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14894", - "to": "1045" + from: '223', + id: '14894', + to: '1045', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14906", - "to": "1051" + from: '223', + id: '14906', + to: '1051', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14936", - "to": "1066" + from: '223', + id: '14936', + to: '1066', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14868", - "to": "1032" + from: '223', + id: '14868', + to: '1032', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14822", - "to": "1009" + from: '223', + id: '14822', + to: '1009', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14932", - "to": "1064" + from: '223', + id: '14932', + to: '1064', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14824", - "to": "1010" + from: '223', + id: '14824', + to: '1010', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14912", - "to": "1054" + from: '223', + id: '14912', + to: '1054', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14930", - "to": "1063" + from: '223', + id: '14930', + to: '1063', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14802", - "to": "999" + from: '223', + id: '14802', + to: '999', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14886", - "to": "1041" + from: '223', + id: '14886', + to: '1041', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14814", - "to": "1005" + from: '223', + id: '14814', + to: '1005', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14934", - "to": "1065" + from: '223', + id: '14934', + to: '1065', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14806", - "to": "1001" + from: '223', + id: '14806', + to: '1001', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14800", - "to": "998" + from: '223', + id: '14800', + to: '998', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14838", - "to": "1017" + from: '223', + id: '14838', + to: '1017', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14878", - "to": "1037" + from: '223', + id: '14878', + to: '1037', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14914", - "to": "1055" + from: '223', + id: '14914', + to: '1055', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14928", - "to": "1062" + from: '223', + id: '14928', + to: '1062', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14938", - "to": "1067" + from: '223', + id: '14938', + to: '1067', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14854", - "to": "1025" + from: '223', + id: '14854', + to: '1025', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14944", - "to": "1070" + from: '223', + id: '14944', + to: '1070', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14918", - "to": "1057" + from: '223', + id: '14918', + to: '1057', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14796", - "to": "996" + from: '223', + id: '14796', + to: '996', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14812", - "to": "1004" + from: '223', + id: '14812', + to: '1004', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14876", - "to": "1036" + from: '223', + id: '14876', + to: '1036', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14910", - "to": "1053" + from: '223', + id: '14910', + to: '1053', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14888", - "to": "1042" + from: '223', + id: '14888', + to: '1042', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14926", - "to": "1061" + from: '223', + id: '14926', + to: '1061', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14828", - "to": "1012" + from: '223', + id: '14828', + to: '1012', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14832", - "to": "1014" + from: '223', + id: '14832', + to: '1014', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14870", - "to": "1033" + from: '223', + id: '14870', + to: '1033', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14798", - "to": "997" + from: '223', + id: '14798', + to: '997', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14922", - "to": "1059" + from: '223', + id: '14922', + to: '1059', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14940", - "to": "1068" + from: '223', + id: '14940', + to: '1068', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14880", - "to": "1038" + from: '223', + id: '14880', + to: '1038', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14890", - "to": "1043" + from: '223', + id: '14890', + to: '1043', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14826", - "to": "1011" + from: '223', + id: '14826', + to: '1011', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14920", - "to": "1058" + from: '223', + id: '14920', + to: '1058', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14916", - "to": "1056" + from: '223', + id: '14916', + to: '1056', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "223", - "id": "14816", - "to": "1006" + from: '223', + id: '14816', + to: '1006', }, { - "attributes": { - "Type": "IS_COUNTRY" + attributes: { + Type: 'IS_COUNTRY', }, - "from": "224", - "id": "15028", - "to": "1112" - } + from: '224', + id: '15028', + to: '1112', + }, ], - "nodes": [ + nodes: [ { - "attributes": { - "labels": [ - "COUNTRY" - ], - "name": "Afghanistan" + attributes: { + labels: ['COUNTRY'], + name: 'Afghanistan', }, - "id": "219" + id: '219', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "855" + id: '855', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "883" + id: '883', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "877" + id: '877', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "824" + id: '824', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "889" + id: '889', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "847" + id: '847', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "861" + id: '861', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "898" + id: '898', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "834" + id: '834', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "858" + id: '858', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "888" + id: '888', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "900" + id: '900', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "860" + id: '860', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "862" + id: '862', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "891" + id: '891', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "828" + id: '828', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "851" + id: '851', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "882" + id: '882', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "897" + id: '897', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -22.74920634920636, - "text": "\n\nThe life expectancy in Afghanistan was impacted by various factors during the period of 1980 to 1989. Here are some of the factors that played a significant role:\n\n1. Soviet-Afghan War: The Soviet-Afghan War lasted from 1979 to 1989 and resulted in significant casualties and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Refugee crisis: The conflict in Afghanistan caused a massive refugee crisis, with millions of Afghans fleeing to neighboring countries such as Pakistan and Iran. The conditions in refugee camps were often poor, with limited access to basic necessities such as food, water, and medical care.\n\n3. Economic instability: Afghanistan's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Political instability: Afghanistan was also facing political instability during this time, with various factions competing for power. The resulting violence and instability had a severe impact on public health and safety.\n\n5. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were widespread in Afghanistan during this period, partly due to the breakdown of health services and poor living conditions.\n\nOverall, the combination of war, refugee crisis, economic instability, political instability, and infectious diseases had a significant impact on life expectancy in Afghanistan during the period from 1980 to 1989. The situation was further exacerbated by limited access to healthcare and other basic services, as well as the disruption of infrastructure and social systems.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -22.74920634920636, + text: "\n\nThe life expectancy in Afghanistan was impacted by various factors during the period of 1980 to 1989. Here are some of the factors that played a significant role:\n\n1. Soviet-Afghan War: The Soviet-Afghan War lasted from 1979 to 1989 and resulted in significant casualties and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Refugee crisis: The conflict in Afghanistan caused a massive refugee crisis, with millions of Afghans fleeing to neighboring countries such as Pakistan and Iran. The conditions in refugee camps were often poor, with limited access to basic necessities such as food, water, and medical care.\n\n3. Economic instability: Afghanistan's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Political instability: Afghanistan was also facing political instability during this time, with various factions competing for power. The resulting violence and instability had a severe impact on public health and safety.\n\n5. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were widespread in Afghanistan during this period, partly due to the breakdown of health services and poor living conditions.\n\nOverall, the combination of war, refugee crisis, economic instability, political instability, and infectious diseases had a significant impact on life expectancy in Afghanistan during the period from 1980 to 1989. The situation was further exacerbated by limited access to healthcare and other basic services, as well as the disruption of infrastructure and social systems.", + type: 'valley', }, - "id": "901" + id: '901', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "867" + id: '867', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "854" + id: '854', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "878" + id: '878', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "839" + id: '839', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "859" + id: '859', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "871" + id: '871', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "837" + id: '837', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "884" + id: '884', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "850" + id: '850', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "830" + id: '830', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "853" + id: '853', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "879" + id: '879', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "874" + id: '874', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "894" + id: '894', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "846" + id: '846', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "876" + id: '876', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "881" + id: '881', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "895" + id: '895', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "856" + id: '856', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "886" + id: '886', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "880" + id: '880', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "848" + id: '848', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "868" + id: '868', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "844" + id: '844', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "833" + id: '833', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -22.74920634920636, - "text": "\n\nThe life expectancy in Afghanistan was impacted by various factors during the period of 1980 to 1989. Here are some of the factors that played a significant role:\n\n1. Soviet-Afghan War: The Soviet-Afghan War lasted from 1979 to 1989 and resulted in significant casualties and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Refugee crisis: The conflict in Afghanistan caused a massive refugee crisis, with millions of Afghans fleeing to neighboring countries such as Pakistan and Iran. The conditions in refugee camps were often poor, with limited access to basic necessities such as food, water, and medical care.\n\n3. Economic instability: Afghanistan's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Political instability: Afghanistan was also facing political instability during this time, with various factions competing for power. The resulting violence and instability had a severe impact on public health and safety.\n\n5. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were widespread in Afghanistan during this period, partly due to the breakdown of health services and poor living conditions.\n\nOverall, the combination of war, refugee crisis, economic instability, political instability, and infectious diseases had a significant impact on life expectancy in Afghanistan during the period from 1980 to 1989. The situation was further exacerbated by limited access to healthcare and other basic services, as well as the disruption of infrastructure and social systems.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -22.74920634920636, + text: "\n\nThe life expectancy in Afghanistan was impacted by various factors during the period of 1980 to 1989. Here are some of the factors that played a significant role:\n\n1. Soviet-Afghan War: The Soviet-Afghan War lasted from 1979 to 1989 and resulted in significant casualties and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Refugee crisis: The conflict in Afghanistan caused a massive refugee crisis, with millions of Afghans fleeing to neighboring countries such as Pakistan and Iran. The conditions in refugee camps were often poor, with limited access to basic necessities such as food, water, and medical care.\n\n3. Economic instability: Afghanistan's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Political instability: Afghanistan was also facing political instability during this time, with various factions competing for power. The resulting violence and instability had a severe impact on public health and safety.\n\n5. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were widespread in Afghanistan during this period, partly due to the breakdown of health services and poor living conditions.\n\nOverall, the combination of war, refugee crisis, economic instability, political instability, and infectious diseases had a significant impact on life expectancy in Afghanistan during the period from 1980 to 1989. The situation was further exacerbated by limited access to healthcare and other basic services, as well as the disruption of infrastructure and social systems.", + type: 'valley', }, - "id": "902" + id: '902', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "827" + id: '827', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "857" + id: '857', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "890" + id: '890', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "835" + id: '835', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "873" + id: '873', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "829" + id: '829', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "866" + id: '866', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "865" + id: '865', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "872" + id: '872', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "869" + id: '869', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "863" + id: '863', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "841" + id: '841', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "875" + id: '875', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "899" + id: '899', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "892" + id: '892', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "840" + id: '840', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "849" + id: '849', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "825" + id: '825', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "836" + id: '836', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "842" + id: '842', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "843" + id: '843', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "831" + id: '831', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "852" + id: '852', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "885" + id: '885', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "838" + id: '838', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "870" + id: '870', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "832" + id: '832', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "887" + id: '887', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "864" + id: '864', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "826" + id: '826', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "893" + id: '893', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "896" + id: '896', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -43.31641216392037, - "text": "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -43.31641216392037, + text: "\n\nUnfortunately, there is not much reliable data available on life expectancy in Afghanistan during the period from 1912 to 1924. However, it is known that Afghanistan was a relatively isolated country during this time, with a largely agrarian economy and limited access to modern medical technology.\n\nVarious factors likely influenced life expectancy in Afghanistan during this period, including:\n\n1. Health care: Access to modern medical care was limited, and traditional healing methods were often used instead. The country's infrastructure was also underdeveloped, with few hospitals or clinics outside of major urban centers.\n\n2. Nutrition: Malnutrition was common, particularly in rural areas where food shortages were common.\n\n3. Disease: Infectious diseases such as cholera, tuberculosis, and smallpox were widespread and often deadly.\n\n4. War and conflict: Afghanistan was frequently beset by internal conflicts and invasions by foreign powers during this period, which likely took a toll on public health and safety.\n\n5. Poverty: Afghanistan was one of the poorest countries in the world during this time, with a large portion of the population living in extreme poverty.\n\nOverall, it is likely that a combination of these factors contributed to the relatively low life expectancy in Afghanistan during the early 20th century. However, without more detailed data it is difficult to determine the specific impacts of each factor.", + type: 'valley', }, - "id": "845" + id: '845', }, { - "attributes": { - "labels": [ - "COUNTRY" - ], - "name": "Albania" + attributes: { + labels: ['COUNTRY'], + name: 'Albania', }, - "id": "220" + id: '220', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "923" + id: '923', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "925" + id: '925', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "976" + id: '976', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "947" + id: '947', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "950" + id: '950', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "971" + id: '971', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "918" + id: '918', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "964" + id: '964', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "911" + id: '911', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "963" + id: '963', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "940" + id: '940', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "931" + id: '931', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": 17.640000000000025, - "text": "\n\nAlbania underwent significant changes during the period from 1935 to 1949, which likely had an impact on life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. World War II: Albania was occupied by Italy and later Germany during World War II, which led to significant destruction of infrastructure and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Communist regime: After World War II, Albania came under communist rule, which led to significant changes in the country's social and economic systems. While the communist government invested in healthcare and education, the country also experienced political repression and economic isolation, which could have affected life expectancy.\n\n3. Economic instability: Albania's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were still widespread in Albania during this period, partly due to the breakdown of health services and poor living conditions.\n\n5. Health care: Albania's healthcare system improved during this period, with the communist government investing in healthcare infrastructure and training more healthcare professionals. However, access to healthcare was still limited in many rural areas, and healthcare quality varied widely.\n\nOverall, the combination of war, communist rule, economic instability, infectious diseases, and healthcare challenges likely had an impact on life expectancy in Albania during the period from 1935 to 1949. The specific impacts of each factor are difficult to determine without more detailed data.", - "type": "peak" + attributes: { + labels: ['FINDING'], + score: 17.640000000000025, + text: "\n\nAlbania underwent significant changes during the period from 1935 to 1949, which likely had an impact on life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. World War II: Albania was occupied by Italy and later Germany during World War II, which led to significant destruction of infrastructure and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Communist regime: After World War II, Albania came under communist rule, which led to significant changes in the country's social and economic systems. While the communist government invested in healthcare and education, the country also experienced political repression and economic isolation, which could have affected life expectancy.\n\n3. Economic instability: Albania's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were still widespread in Albania during this period, partly due to the breakdown of health services and poor living conditions.\n\n5. Health care: Albania's healthcare system improved during this period, with the communist government investing in healthcare infrastructure and training more healthcare professionals. However, access to healthcare was still limited in many rural areas, and healthcare quality varied widely.\n\nOverall, the combination of war, communist rule, economic instability, infectious diseases, and healthcare challenges likely had an impact on life expectancy in Albania during the period from 1935 to 1949. The specific impacts of each factor are difficult to determine without more detailed data.", + type: 'peak', }, - "id": "981" + id: '981', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "903" + id: '903', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "936" + id: '936', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "914" + id: '914', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "952" + id: '952', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "906" + id: '906', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "909" + id: '909', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "954" + id: '954', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "967" + id: '967', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "907" + id: '907', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "968" + id: '968', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "974" + id: '974', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "949" + id: '949', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "920" + id: '920', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "979" + id: '979', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "912" + id: '912', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "970" + id: '970', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": 17.640000000000025, - "text": "\n\nAlbania underwent significant changes during the period from 1935 to 1949, which likely had an impact on life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. World War II: Albania was occupied by Italy and later Germany during World War II, which led to significant destruction of infrastructure and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Communist regime: After World War II, Albania came under communist rule, which led to significant changes in the country's social and economic systems. While the communist government invested in healthcare and education, the country also experienced political repression and economic isolation, which could have affected life expectancy.\n\n3. Economic instability: Albania's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were still widespread in Albania during this period, partly due to the breakdown of health services and poor living conditions.\n\n5. Health care: Albania's healthcare system improved during this period, with the communist government investing in healthcare infrastructure and training more healthcare professionals. However, access to healthcare was still limited in many rural areas, and healthcare quality varied widely.\n\nOverall, the combination of war, communist rule, economic instability, infectious diseases, and healthcare challenges likely had an impact on life expectancy in Albania during the period from 1935 to 1949. The specific impacts of each factor are difficult to determine without more detailed data.", - "type": "peak" + attributes: { + labels: ['FINDING'], + score: 17.640000000000025, + text: "\n\nAlbania underwent significant changes during the period from 1935 to 1949, which likely had an impact on life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. World War II: Albania was occupied by Italy and later Germany during World War II, which led to significant destruction of infrastructure and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Communist regime: After World War II, Albania came under communist rule, which led to significant changes in the country's social and economic systems. While the communist government invested in healthcare and education, the country also experienced political repression and economic isolation, which could have affected life expectancy.\n\n3. Economic instability: Albania's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were still widespread in Albania during this period, partly due to the breakdown of health services and poor living conditions.\n\n5. Health care: Albania's healthcare system improved during this period, with the communist government investing in healthcare infrastructure and training more healthcare professionals. However, access to healthcare was still limited in many rural areas, and healthcare quality varied widely.\n\nOverall, the combination of war, communist rule, economic instability, infectious diseases, and healthcare challenges likely had an impact on life expectancy in Albania during the period from 1935 to 1949. The specific impacts of each factor are difficult to determine without more detailed data.", + type: 'peak', }, - "id": "980" + id: '980', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "972" + id: '972', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "921" + id: '921', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "945" + id: '945', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "930" + id: '930', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "961" + id: '961', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "929" + id: '929', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "919" + id: '919', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "938" + id: '938', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "959" + id: '959', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "973" + id: '973', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "904" + id: '904', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "916" + id: '916', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "915" + id: '915', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "953" + id: '953', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "908" + id: '908', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "933" + id: '933', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "924" + id: '924', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "926" + id: '926', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "910" + id: '910', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "935" + id: '935', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "951" + id: '951', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "937" + id: '937', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "962" + id: '962', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "932" + id: '932', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "969" + id: '969', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "955" + id: '955', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "942" + id: '942', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "956" + id: '956', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "943" + id: '943', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "934" + id: '934', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "965" + id: '965', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "960" + id: '960', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "978" + id: '978', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "913" + id: '913', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "966" + id: '966', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "941" + id: '941', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "939" + id: '939', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -14.76923076923077, - "text": "\n\nAlbania underwent significant changes during the period from 1942 to 1949, which likely had an impact on life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. World War II: Albania was occupied by Italy and later Germany during World War II, which led to significant destruction of infrastructure and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Communist regime: After World War II, Albania came under communist rule, which led to significant changes in the country's social and economic systems. While the communist government invested in healthcare and education, the country also experienced political repression and economic isolation, which could have affected life expectancy.\n\n3. Economic instability: Albania's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were still widespread in Albania during this period, partly due to the breakdown of health services and poor living conditions.\n\n5. Health care: Albania's healthcare system improved during this period, with the communist government investing in healthcare infrastructure and training more healthcare professionals. However, access to healthcare was still limited in many rural areas, and healthcare quality varied widely.\n\n6. Repression and political violence: The communist government of Albania engaged in political repression, including the execution of political opponents and imprisonment of those deemed to be dissidents. This repression could have led to increased mortality and reduced life expectancy.\n\nOverall, the combination of war, communist rule, economic instability, infectious diseases, healthcare challenges, and political repression likely had an impact on life expectancy in Albania during the period from 1942 to 1949. The specific impacts of each factor are difficult to determine without more detailed data.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -14.76923076923077, + text: "\n\nAlbania underwent significant changes during the period from 1942 to 1949, which likely had an impact on life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. World War II: Albania was occupied by Italy and later Germany during World War II, which led to significant destruction of infrastructure and displacement of civilians. The war had a severe impact on public health, with widespread disruption of health services, lack of access to clean water and sanitation facilities, and increased exposure to infectious diseases.\n\n2. Communist regime: After World War II, Albania came under communist rule, which led to significant changes in the country's social and economic systems. While the communist government invested in healthcare and education, the country also experienced political repression and economic isolation, which could have affected life expectancy.\n\n3. Economic instability: Albania's economy was unstable during this period, with frequent droughts, low agricultural yields, and limited access to trade and investment. Poverty was widespread, and many people lacked access to basic necessities such as food, shelter, and healthcare.\n\n4. Infectious diseases: Infectious diseases such as tuberculosis, cholera, and malaria were still widespread in Albania during this period, partly due to the breakdown of health services and poor living conditions.\n\n5. Health care: Albania's healthcare system improved during this period, with the communist government investing in healthcare infrastructure and training more healthcare professionals. However, access to healthcare was still limited in many rural areas, and healthcare quality varied widely.\n\n6. Repression and political violence: The communist government of Albania engaged in political repression, including the execution of political opponents and imprisonment of those deemed to be dissidents. This repression could have led to increased mortality and reduced life expectancy.\n\nOverall, the combination of war, communist rule, economic instability, infectious diseases, healthcare challenges, and political repression likely had an impact on life expectancy in Albania during the period from 1942 to 1949. The specific impacts of each factor are difficult to determine without more detailed data.", + type: 'valley', }, - "id": "982" + id: '982', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "977" + id: '977', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "948" + id: '948', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "958" + id: '958', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "957" + id: '957', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "944" + id: '944', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "905" + id: '905', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "927" + id: '927', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "928" + id: '928', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "946" + id: '946', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "917" + id: '917', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "975" + id: '975', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -32, - "text": "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -32, + text: "\n\nAlbania underwent significant changes and faced several challenges during the period from 1912 to 1924, which likely impacted life expectancy in the country. Here are some of the factors that could have affected life expectancy in Albania during this period:\n\n1. War and conflict: Albania was embroiled in a series of conflicts during this period, including the Balkan Wars, World War I, and internal political struggles. These conflicts led to displacement, disruption of infrastructure, and increased mortality due to injuries and illnesses.\n\n2. Disease and epidemics: Albania experienced several epidemics during this period, including outbreaks of cholera and typhus. These diseases were difficult to control, as public health measures were underdeveloped and the country lacked access to modern medical technology.\n\n3. Poverty and malnutrition: Albania was one of the poorest countries in Europe during this period, with widespread poverty and malnutrition. These factors contributed to poor health outcomes and increased mortality, particularly among vulnerable populations such as children and the elderly.\n\n4. Access to healthcare: Albania's healthcare system was underdeveloped during this period, with limited access to medical facilities and trained healthcare professionals. This lack of access to healthcare likely contributed to increased morbidity and mortality from preventable illnesses and injuries.\n\n5. Environmental factors: Albania's mountainous terrain and rural areas presented challenges for infrastructure development and access to basic necessities such as clean water and sanitation facilities. These environmental factors may have contributed to the spread of disease and increased mortality rates.\n\nOverall, the combination of war, disease, poverty, limited access to healthcare, and environmental factors likely had a significant impact on life expectancy in Albania during the period from 1912 to 1924. However, due to the lack of reliable data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "922" + id: '922', }, { - "attributes": { - "labels": [ - "COUNTRY" - ], - "name": "Algeria" + attributes: { + labels: ['COUNTRY'], + name: 'Algeria', }, - "id": "221" + id: '221', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -23.348484848484848, - "text": "Data on life expectancy in Algeria during the period from 1912 to 1927 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1912 to 1927. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -23.348484848484848, + text: "Data on life expectancy in Algeria during the period from 1912 to 1927 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1912 to 1927. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "990" + id: '990', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -15.590909090909093, - "text": "\n\nData on life expectancy in Algeria during the period from 1844 to 1855 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: In 1830, France invaded and occupied Algeria, which led to significant changes in the country's social and economic systems. The French colonization led to large-scale displacement of the local population, and many Algerians were forced to work in difficult and dangerous conditions. This could have had an impact on life expectancy.\n\n2. War and conflict: Algeria experienced several periods of conflict during this period, including the French conquest of Algeria, which lasted until 1847. These conflicts led to significant loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, plague, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\nOverall, the combination of French colonization, war and conflict, infectious diseases, limited access to healthcare, and poor nutrition could have impacted life expectancy in Algeria during the period from 1844 to 1855. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -15.590909090909093, + text: "\n\nData on life expectancy in Algeria during the period from 1844 to 1855 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: In 1830, France invaded and occupied Algeria, which led to significant changes in the country's social and economic systems. The French colonization led to large-scale displacement of the local population, and many Algerians were forced to work in difficult and dangerous conditions. This could have had an impact on life expectancy.\n\n2. War and conflict: Algeria experienced several periods of conflict during this period, including the French conquest of Algeria, which lasted until 1847. These conflicts led to significant loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, plague, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\nOverall, the combination of French colonization, war and conflict, infectious diseases, limited access to healthcare, and poor nutrition could have impacted life expectancy in Algeria during the period from 1844 to 1855. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "983" + id: '983', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": 23.68621794871796, - "text": "Data on life expectancy in Algeria during the period from 1920 to 1933 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I aftermath: The aftermath of World War I, including the Treaty of Versailles, led to significant changes in the political and economic systems of Europe, which could have impacted Algeria and its population.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I aftermath, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1920 to 1933. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "peak" + attributes: { + labels: ['FINDING'], + score: 23.68621794871796, + text: "Data on life expectancy in Algeria during the period from 1920 to 1933 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I aftermath: The aftermath of World War I, including the Treaty of Versailles, led to significant changes in the political and economic systems of Europe, which could have impacted Algeria and its population.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I aftermath, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1920 to 1933. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'peak', }, - "id": "991" + id: '991', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": 13.225000000000014, - "text": "Data on life expectancy in Algeria during the period from 1907 to 1919 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1907 to 1919. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "peak" + attributes: { + labels: ['FINDING'], + score: 13.225000000000014, + text: "Data on life expectancy in Algeria during the period from 1907 to 1919 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1907 to 1919. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'peak', }, - "id": "988" + id: '988', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": 13.225000000000014, - "text": "Data on life expectancy in Algeria during the period from 1907 to 1919 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1907 to 1919. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "peak" + attributes: { + labels: ['FINDING'], + score: 13.225000000000014, + text: "Data on life expectancy in Algeria during the period from 1907 to 1919 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1907 to 1919. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'peak', }, - "id": "986" + id: '986', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -10.173333333333332, - "text": "Data on life expectancy in Algeria during the period from 1902 to 1910 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. War and conflict: Algeria experienced several periods of conflict during this period, including the Second Moroccan Crisis in 1905, which led to significant loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Urbanization and industrialization: Algeria underwent significant urbanization and industrialization during this period, which could have both positive and negative impacts on life expectancy. While the growth of urban areas could have led to improvements in healthcare and access to resources, it could also have increased exposure to environmental hazards and contributed to poor living conditions.\n\nOverall, the combination of French colonization, war and conflict, infectious diseases, limited access to healthcare, poor nutrition, and urbanization and industrialization could have impacted life expectancy in Algeria during the period from 1902 to 1910. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -10.173333333333332, + text: "Data on life expectancy in Algeria during the period from 1902 to 1910 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. War and conflict: Algeria experienced several periods of conflict during this period, including the Second Moroccan Crisis in 1905, which led to significant loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Urbanization and industrialization: Algeria underwent significant urbanization and industrialization during this period, which could have both positive and negative impacts on life expectancy. While the growth of urban areas could have led to improvements in healthcare and access to resources, it could also have increased exposure to environmental hazards and contributed to poor living conditions.\n\nOverall, the combination of French colonization, war and conflict, infectious diseases, limited access to healthcare, poor nutrition, and urbanization and industrialization could have impacted life expectancy in Algeria during the period from 1902 to 1910. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "985" + id: '985', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -23.348484848484848, - "text": "Data on life expectancy in Algeria during the period from 1912 to 1927 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1912 to 1927. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -23.348484848484848, + text: "Data on life expectancy in Algeria during the period from 1912 to 1927 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1912 to 1927. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "989" + id: '989', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": 10.681776133210011, - "text": "Data on life expectancy in Algeria during the period from 1937 to 1948 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. World War II: Algeria, as a French colony, was involved in World War II, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n2. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of World War II, French colonization, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1937 to 1948. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "peak" + attributes: { + labels: ['FINDING'], + score: 10.681776133210011, + text: "Data on life expectancy in Algeria during the period from 1937 to 1948 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. World War II: Algeria, as a French colony, was involved in World War II, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n2. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of World War II, French colonization, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1937 to 1948. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'peak', }, - "id": "993" + id: '993', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -34.41025641025641, - "text": "Data on life expectancy in Algeria during the period from 1863 to 1874 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was under French colonial rule during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. War and conflict: Algeria experienced several periods of conflict during this period, including the Mokrani Revolt in 1871, which led to significant loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, war and conflict, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1863 to 1874. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -34.41025641025641, + text: "Data on life expectancy in Algeria during the period from 1863 to 1874 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was under French colonial rule during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. War and conflict: Algeria experienced several periods of conflict during this period, including the Mokrani Revolt in 1871, which led to significant loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, war and conflict, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1863 to 1874. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "984" + id: '984', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": 13.225000000000014, - "text": "Data on life expectancy in Algeria during the period from 1907 to 1919 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1907 to 1919. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "peak" + attributes: { + labels: ['FINDING'], + score: 13.225000000000014, + text: "Data on life expectancy in Algeria during the period from 1907 to 1919 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. World War I: Algeria, as a French colony, was involved in World War I, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of French colonization, World War I, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1907 to 1919. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'peak', }, - "id": "987" + id: '987', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": 10.681776133210011, - "text": "Data on life expectancy in Algeria during the period from 1937 to 1948 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. World War II: Algeria, as a French colony, was involved in World War II, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n2. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of World War II, French colonization, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1937 to 1948. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "peak" + attributes: { + labels: ['FINDING'], + score: 10.681776133210011, + text: "Data on life expectancy in Algeria during the period from 1937 to 1948 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. World War II: Algeria, as a French colony, was involved in World War II, and the conflict likely led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\n2. French colonization: Algeria was still under French colonial rule during this period, and the colonial authorities continued to implement significant changes to the country's social and economic systems. This included the introduction of new infrastructure and economic development projects, but also included forced labor, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n3. Infectious diseases: Algeria was still affected by a range of infectious diseases during this period, including cholera, typhoid fever, and tuberculosis, which could have impacted life expectancy.\n\n4. Limited access to healthcare: Healthcare services were limited in Algeria during this period, and many people lacked access to basic healthcare services.\n\n5. Nutrition: Poor nutrition and food insecurity were also common in Algeria during this period, which could have contributed to lower life expectancy.\n\n6. Economic changes: The introduction of new economic policies and infrastructure projects under French colonial rule could have had both positive and negative impacts on life expectancy, depending on the specific context and implementation of these changes.\n\nOverall, the combination of World War II, French colonization, infectious diseases, limited access to healthcare, poor nutrition, and economic changes could have impacted life expectancy in Algeria during the period from 1937 to 1948. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'peak', }, - "id": "992" + id: '992', }, { - "attributes": { - "labels": [ - "COUNTRY" - ], - "name": "Angola" + attributes: { + labels: ['COUNTRY'], + name: 'Angola', }, - "id": "223" + id: '223', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1031" + id: '1031', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1048" + id: '1048', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1026" + id: '1026', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1018" + id: '1018', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1069" + id: '1069', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1050" + id: '1050', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1016" + id: '1016', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1035" + id: '1035', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1003" + id: '1003', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1027" + id: '1027', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1034" + id: '1034', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1021" + id: '1021', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1002" + id: '1002', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1028" + id: '1028', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1000" + id: '1000', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1008" + id: '1008', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1040" + id: '1040', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1046" + id: '1046', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1039" + id: '1039', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1007" + id: '1007', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "995" + id: '995', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1013" + id: '1013', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1049" + id: '1049', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1052" + id: '1052', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "994" + id: '994', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1015" + id: '1015', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1047" + id: '1047', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1060" + id: '1060', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1030" + id: '1030', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1044" + id: '1044', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1023" + id: '1023', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1024" + id: '1024', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1022" + id: '1022', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1020" + id: '1020', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1019" + id: '1019', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1029" + id: '1029', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1045" + id: '1045', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1051" + id: '1051', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1066" + id: '1066', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1032" + id: '1032', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1009" + id: '1009', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1064" + id: '1064', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1010" + id: '1010', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1054" + id: '1054', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1063" + id: '1063', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "999" + id: '999', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1041" + id: '1041', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1005" + id: '1005', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1065" + id: '1065', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1001" + id: '1001', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "998" + id: '998', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1017" + id: '1017', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1037" + id: '1037', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1055" + id: '1055', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1062" + id: '1062', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1067" + id: '1067', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1025" + id: '1025', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1070" + id: '1070', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1057" + id: '1057', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "996" + id: '996', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1004" + id: '1004', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1036" + id: '1036', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1053" + id: '1053', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1042" + id: '1042', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1061" + id: '1061', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1012" + id: '1012', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1014" + id: '1014', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1033" + id: '1033', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "997" + id: '997', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1059" + id: '1059', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1068" + id: '1068', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1038" + id: '1038', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1043" + id: '1043', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1011" + id: '1011', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1058" + id: '1058', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1056" + id: '1056', }, { - "attributes": { - "labels": [ - "FINDING" - ], - "score": -36.19366228070176, - "text": "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", - "type": "valley" + attributes: { + labels: ['FINDING'], + score: -36.19366228070176, + text: "Data on life expectancy in Angola during the period from 1912 to 1924 is limited, but there were several factors that could have impacted life expectancy during this time:\n\n1. Portuguese colonization: Angola was a colony of Portugal during this period, and the colonial authorities implemented significant changes to the country's social and economic systems. This included the forced labor of Angolan people, displacement of local populations, and political repression, which could have impacted life expectancy.\n\n2. Infectious diseases: Angola was affected by a range of infectious diseases during this period, including malaria, yellow fever, and smallpox, which could have impacted life expectancy.\n\n3. Limited access to healthcare: Healthcare services were limited in Angola during this period, and many people lacked access to basic healthcare services.\n\n4. Nutrition: Poor nutrition and food insecurity were also common in Angola during this period, which could have contributed to lower life expectancy.\n\n5. Violence and conflicts: Angola experienced several conflicts and wars during this period, including the first and second Chimurenga, which could have led to loss of life and displacement of civilians, which could have impacted life expectancy.\n\nOverall, the combination of Portuguese colonization, infectious diseases, limited access to healthcare, poor nutrition, and conflicts and wars could have impacted life expectancy in Angola during the period from 1912 to 1924. However, due to limited data, it is difficult to determine the specific impacts of each factor on life expectancy.", + type: 'valley', }, - "id": "1006" + id: '1006', }, { - "attributes": { - "labels": [ - "COUNTRY" - ], - "name": "Antigua and Barbuda" + attributes: { + labels: ['COUNTRY'], + name: 'Antigua and Barbuda', }, - "id": "224" - } - ] + id: '224', + }, + ], }; diff --git a/libs/shared/lib/mock-data/query-result/mockQueryResults.ts b/libs/shared/lib/mock-data/query-result/mockQueryResults.ts index b60190fe1..ab4ca988e 100644 --- a/libs/shared/lib/mock-data/query-result/mockQueryResults.ts +++ b/libs/shared/lib/mock-data/query-result/mockQueryResults.ts @@ -135,8 +135,6 @@ export const mockQueryResults = { }, }, ], - newYorkToSanFranciscoExpectedLinks: [ - { source: 'airports/JFK', target: 'airports/SFO', value: 18 }, - ], + newYorkToSanFranciscoExpectedLinks: [{ source: 'airports/JFK', target: 'airports/SFO', value: 18 }], badQueryResponse: `{"bad":"response"}`, }; diff --git a/libs/shared/lib/mock-data/query-result/smallFlightsQueryResults.ts b/libs/shared/lib/mock-data/query-result/smallFlightsQueryResults.ts index 0d58608d9..431c9e833 100644 --- a/libs/shared/lib/mock-data/query-result/smallFlightsQueryResults.ts +++ b/libs/shared/lib/mock-data/query-result/smallFlightsQueryResults.ts @@ -14,6 +14,7 @@ export const smallFlightsQueryResults = { { from: 'airports/JFK', to: 'airports/SFO', + id: 'flights/1', value: 18, attributes: { diff --git a/libs/shared/lib/mock-data/schema/index.ts b/libs/shared/lib/mock-data/schema/index.ts index cc281685c..91b46a3f0 100644 --- a/libs/shared/lib/mock-data/schema/index.ts +++ b/libs/shared/lib/mock-data/schema/index.ts @@ -1,4 +1,4 @@ export * from './simpleRaw'; export * from './moviesSchemaRaw'; export * from './northwindSchemaRaw'; -export * from './twitterSchemaRaw'; \ No newline at end of file +export * from './twitterSchemaRaw'; diff --git a/libs/shared/lib/mock-data/schema/mock-data.spec.ts b/libs/shared/lib/mock-data/schema/mock-data.spec.ts index 85d7a294d..3f041e343 100644 --- a/libs/shared/lib/mock-data/schema/mock-data.spec.ts +++ b/libs/shared/lib/mock-data/schema/mock-data.spec.ts @@ -1,4 +1,4 @@ -import { assert, describe, expect, it, test } from "vitest"; +import { assert, describe, expect, it, test } from 'vitest'; import Graph from 'graphology'; import { movieSchemaRaw, movieSchema } from '..'; @@ -31,35 +31,27 @@ describe('MockData Tests', () => { const graph = movieSchema; expect(graph); - expect( - graph.constructor.name.toLowerCase().indexOf('graph') != -1 - ).toBeTruthy(); + expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy(); }); it('should have data available as graphology model northwind', () => { const graph = northWindSchema; expect(graph); - expect( - graph.constructor.name.toLowerCase().indexOf('graph') != -1 - ).toBeTruthy(); + expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy(); }); it('should have data available as graphology model simpleSchemaRaw', () => { const graph = simpleSchema; expect(graph); - expect( - graph.constructor.name.toLowerCase().indexOf('graph') != -1 - ).toBeTruthy(); + expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy(); }); it('should have data available as graphology model twitterSchemaRaw', () => { const graph = twitterSchema; expect(graph); - expect( - graph.constructor.name.toLowerCase().indexOf('graph') != -1 - ).toBeTruthy(); + expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy(); }); }); diff --git a/libs/shared/lib/mock-data/schema/moviesSchemaRaw.ts b/libs/shared/lib/mock-data/schema/moviesSchemaRaw.ts index 7857f98c0..8a69595d8 100644 --- a/libs/shared/lib/mock-data/schema/moviesSchemaRaw.ts +++ b/libs/shared/lib/mock-data/schema/moviesSchemaRaw.ts @@ -99,4 +99,3 @@ export const movieSchemaRaw: SchemaFromBackend = { }; export const movieSchema = SchemaUtils.schemaBackend2Graphology(movieSchemaRaw); - diff --git a/libs/shared/lib/mock-data/schema/northwindSchemaRaw.ts b/libs/shared/lib/mock-data/schema/northwindSchemaRaw.ts index 529dbfa04..7b665842a 100644 --- a/libs/shared/lib/mock-data/schema/northwindSchemaRaw.ts +++ b/libs/shared/lib/mock-data/schema/northwindSchemaRaw.ts @@ -286,5 +286,4 @@ export const northwindSchemaRaw: SchemaFromBackend = { ], }; -export const northWindSchema = - SchemaUtils.schemaBackend2Graphology(northwindSchemaRaw); +export const northWindSchema = SchemaUtils.schemaBackend2Graphology(northwindSchemaRaw); diff --git a/libs/shared/lib/mock-data/schema/simpleAirportRaw.ts b/libs/shared/lib/mock-data/schema/simpleAirportRaw.ts index a8524c549..7a877d85f 100644 --- a/libs/shared/lib/mock-data/schema/simpleAirportRaw.ts +++ b/libs/shared/lib/mock-data/schema/simpleAirportRaw.ts @@ -14,7 +14,7 @@ export const simpleSchemaAirportRaw: SchemaFromBackend = { { name: 'state', type: 'string' }, { name: 'vip', type: 'bool' }, ], - } + }, ], edges: [ { @@ -36,7 +36,7 @@ export const simpleSchemaAirportRaw: SchemaFromBackend = { { name: 'UniqueCarrier', type: 'string' }, { name: 'Year', type: 'int' }, ], - } + }, ], }; diff --git a/libs/shared/lib/mock-data/schema/simpleRaw.ts b/libs/shared/lib/mock-data/schema/simpleRaw.ts index 5444e1f57..9ccfa362f 100644 --- a/libs/shared/lib/mock-data/schema/simpleRaw.ts +++ b/libs/shared/lib/mock-data/schema/simpleRaw.ts @@ -1,5 +1,5 @@ -import { SchemaFromBackend } from "../../schema"; -import { SchemaUtils } from "../../schema/schema-utils"; +import { SchemaFromBackend } from '../../schema'; +import { SchemaUtils } from '../../schema/schema-utils'; export const simpleSchemaRaw: SchemaFromBackend = { nodes: [ @@ -103,4 +103,4 @@ export const simpleSchemaRaw: SchemaFromBackend = { ], }; -export const simpleSchema = SchemaUtils.schemaBackend2Graphology(simpleSchemaRaw); \ No newline at end of file +export const simpleSchema = SchemaUtils.schemaBackend2Graphology(simpleSchemaRaw); diff --git a/libs/shared/lib/mock-data/schema/twitterSchemaRaw.ts b/libs/shared/lib/mock-data/schema/twitterSchemaRaw.ts index 1ce12b664..7637bdb26 100644 --- a/libs/shared/lib/mock-data/schema/twitterSchemaRaw.ts +++ b/libs/shared/lib/mock-data/schema/twitterSchemaRaw.ts @@ -1,5 +1,5 @@ import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils'; -import { SchemaFromBackend } from '@graphpolaris/shared/lib/model/backend'; +import { SchemaFromBackend } from '../../schema'; export const twitterSchemaRaw: SchemaFromBackend = { nodes: [ @@ -315,5 +315,4 @@ export const twitterSchemaRaw: SchemaFromBackend = { ], }; -export const twitterSchema = - SchemaUtils.schemaBackend2Graphology(twitterSchemaRaw); +export const twitterSchema = SchemaUtils.schemaBackend2Graphology(twitterSchemaRaw); diff --git a/libs/shared/lib/querybuilder/index.ts b/libs/shared/lib/querybuilder/index.ts index f0c630f2d..698ce9f89 100644 --- a/libs/shared/lib/querybuilder/index.ts +++ b/libs/shared/lib/querybuilder/index.ts @@ -1,4 +1,4 @@ export * from './panel'; export * from './pills'; export * from './query-utils'; -export * from './model'; \ No newline at end of file +export * from './model'; diff --git a/libs/shared/lib/querybuilder/model/BackendQueryFormat.tsx b/libs/shared/lib/querybuilder/model/BackendQueryFormat.tsx index d13b4b6f7..06a266125 100644 --- a/libs/shared/lib/querybuilder/model/BackendQueryFormat.tsx +++ b/libs/shared/lib/querybuilder/model/BackendQueryFormat.tsx @@ -4,6 +4,9 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ +import { type } from 'os'; +import { AllLogicStatement, AnyStatement, InputNodeType } from './logic/general'; + /** JSON query format used to send a query to the backend. */ export interface BackendQueryResultFormat { databaseName: string; @@ -14,8 +17,8 @@ export interface BackendQueryResultFormat { }; entities: Entity[]; relations: Relation[]; - groupBys: GroupBy[]; - machineLearning: MachineLearning[]; + // groupBys: GroupBy[]; + // machineLearning: MachineLearning[]; limit: number; // modifiers: ModifierStruct[]; // prefix: string; @@ -24,20 +27,67 @@ export interface BackendQueryResultFormat { /** JSON query format used to send a query to the backend. */ export interface BackendQueryFormat { databaseName: string; - return: { - entities: number[]; - relations: number[]; - groupBys: number[]; - }; - entities: Entity[]; - relations: Relation[]; - groupBys: GroupBy[]; - machineLearning: MachineLearning[]; limit: number; + return: string[]; + query: QueryStruct[]; + logic?: AnyStatement; + // entities: Entity[]; + // relations: Relation[]; + // groupBys: GroupBy[]; + // machineLearning: MachineLearning[]; // modifiers: ModifierStruct[]; // prefix: string; } +/** Interface for an entity in the JSON for the query. */ +export interface QueryStruct { + ID: string; + node: NodeStruct; +} + +export interface NodeStruct { + label?: string; + ID?: string; + logic?: AllLogicStatement; + filter?: FilterStruct[]; + relation?: RelationStruct; + subQuery?: QueryStruct; + export?: ExportNodeStruct[]; +} + +export interface ExportNodeStruct { + ID: string; + attribute: string; +} + +export interface LogicStruct { + ID: string; + attribute: string; + operation: LogicOperationType; +} + +export interface FilterStruct { + attribute: string; + operation: FilterOperationType; + value: string; +} + +export interface RelationStruct { + ID?: string; + label?: string; + depth?: QuerySearchDepthStruct; + direction: 'TO' | 'FROM'; + node?: NodeStruct; +} + +export interface QuerySearchDepthStruct { + min: number; + max: number; +} + +export type FilterOperationType = 'EQ' | 'NEQ' | 'GT' | 'LT' | 'GTE' | 'LTE' | 'LIKE' | 'NOT LIKE' | 'IN' | 'NOT IN'; +export type LogicOperationType = 'AND' | 'OR'; + /** Interface for an entity in the JSON for the query. */ export interface Entity { name: string; @@ -57,6 +107,34 @@ export interface Relation { constraints: Constraint[]; } +// /** Interface for an relation in the JSON for the query. */ +// export interface Relation { +// name: string; +// ID: number; +// fromType: string; +// fromID: number; +// toType: string; +// toID: number; +// depth: { min: number; max: number }; +// constraints: Constraint[]; +// } + +// /** JSON query format used to send a query to the backend. */ +// export interface TranslatedJSONQuery { +// return: { +// entities: number[]; +// relations: number[]; +// groupBys: number[]; +// }; +// entities: Entity[]; +// relations: Relation[]; +// groupBys: GroupBy[]; +// machineLearning: MachineLearning[]; +// limit: number; +// } + +// //////////////////// + /** * Constraint datatypes created from the attributes of a relation or entity. * @@ -72,57 +150,42 @@ export interface Constraint { value: string; } -/** Interface for a function in the JSON for the query. */ -export interface GroupBy { - ID: number; - - groupType: string; - groupID: number[]; - groupAttribute: string; +// /** Interface for a function in the JSON for the query. */ +// export interface GroupBy { +// ID: number; - byType: string; - byID: number[]; - byAttribute: string; - - appliedModifier: string; - relationID: number; - constraints: Constraint[]; -} +// groupType: string; +// groupID: number[]; +// groupAttribute: string; -/** Interface for Machine Learning algorithm */ -export interface MachineLearning { - ID?: number; - queuename: string; - parameters: string[]; -} +// byType: string; +// byID: number[]; +// byAttribute: string; -/** Interface for what the JSON needs for link predicition */ -export interface LinkPrediction { - queuename: string; - parameters: { - key: string; - value: string; - }[]; -} +// appliedModifier: string; +// relationID: number; +// constraints: Constraint[]; +// } -export interface ModifierStruct { - type: string; - selectedType: string; - selectedTypeID: number; - attributeIndex: number; -} +// /** Interface for Machine Learning algorithm */ +// export interface MachineLearning { +// ID?: number; +// queuename: string; +// parameters: string[]; +// } +// /** Interface for what the JSON needs for link predicition */ +// export interface LinkPrediction { +// queuename: string; +// parameters: { +// key: string; +// value: string; +// }[]; +// } -/** JSON query format used to send a query to the backend. */ -export interface TranslatedJSONQuery { - return: { - entities: number[]; - relations: number[]; - groupBys: number[]; - }; - entities: Entity[]; - relations: Relation[]; - groupBys: GroupBy[]; - machineLearning: MachineLearning[]; - limit: number; -} \ No newline at end of file +// export interface ModifierStruct { +// type: string; +// selectedType: string; +// selectedTypeID: number; +// attributeIndex: number; +// } diff --git a/libs/shared/lib/querybuilder/model/graphology/index.ts b/libs/shared/lib/querybuilder/model/graphology/index.ts index 50589a9b7..860ef5a01 100644 --- a/libs/shared/lib/querybuilder/model/graphology/index.ts +++ b/libs/shared/lib/querybuilder/model/graphology/index.ts @@ -1,2 +1,2 @@ -export * from './model' -export * from './utils' \ No newline at end of file +export * from './model'; +export * from './utils'; diff --git a/libs/shared/lib/querybuilder/model/graphology/model.ts b/libs/shared/lib/querybuilder/model/graphology/model.ts index de7c52ad5..09832ac55 100644 --- a/libs/shared/lib/querybuilder/model/graphology/model.ts +++ b/libs/shared/lib/querybuilder/model/graphology/model.ts @@ -1,49 +1,82 @@ import { Attributes as GAttributes } from 'graphology-types'; -import { - AttributeData, - AttributeNode, - EntityData, - EntityNode, - FunctionNode, - ModifierNode, - RelationData, - RelationNode, -} from '../reactflow/model'; import { XYPosition } from 'reactflow'; -import { - setQuerybuilderNodes, - store, -} from '@graphpolaris/shared/lib/data-access'; import { MultiGraph } from 'graphology'; -import { calcWidthHeightOfPill } from '@graphpolaris/shared/lib/querybuilder/model/graphology/utils'; import './utils'; +import { GeneralDescription, InputNodeType, InputNodeTypeTypes } from '../logic/general'; +import { AllLogicTypes } from '../logic'; +import { QueryElementTypes } from '../reactflow'; // export interface Attributes extends EntityNode | RelationNode | AttributeNode | FunctionNode | ModifierNode { // } -export type NodeAttributes = XYPosition & - AttributeData & { - type: string; - width?: number; - height?: number; - }; - -export type EntityNodeAttributes = XYPosition & - EntityData & { - type: string; - width?: number; - height?: number; - }; - -export type RelationNodeAttributes = XYPosition & - RelationData & { - type: string; - width?: number; - height?: number; - }; - -export type QueryGraph = MultiGraph< - NodeAttributes | EntityNodeAttributes | RelationNodeAttributes, - GAttributes, - GAttributes ->; +export interface NodeAttribute { + handleData: QueryGraphEdgeHandle; + // nodeCount: number; + // summedNullAmount: number; + // connectedRatio: number; + // handles: string[]; +} + +export type NodeDefaults = { + id?: string; + type: QueryElementTypes; + width?: number; + height?: number; + attributes?: NodeAttribute[]; +}; + +/** Interface for the data in an entity node. */ +export interface EntityData { + name?: string; + leftRelationHandleId?: QueryGraphEdgeHandle; + rightRelationHandleId?: QueryGraphEdgeHandle; +} + +/** Interface for the data in an relation node. */ +export interface RelationData { + name?: string; + collection: string; + depth: { min: number; max: number }; + leftEntityHandleId?: QueryGraphEdgeHandle; + rightEntityHandleId?: QueryGraphEdgeHandle; +} + +export interface LogicData { + name: string; + // inputType: InputNodeType; + // logicType: AllLogicTypes; + // key: string; + logic: GeneralDescription<AllLogicTypes>; + inputs: Record<string, InputNodeTypeTypes>; // name from InputNode -> InputNodeTypeTypes +} + +export type EntityNodeAttributes = XYPosition & EntityData & NodeDefaults; +export type RelationNodeAttributes = XYPosition & RelationData & NodeDefaults; +export type LogicNodeAttributes = XYPosition & LogicData & NodeDefaults; + +export type QueryGraphNodes = EntityNodeAttributes | RelationNodeAttributes | LogicNodeAttributes; + +export type QueryGraphEdgeAttribute = { + attributeName?: string; + attributeType?: InputNodeType; +}; + +export type QueryGraphEdgeHandle = { + nodeId: string; + nodeName: string; + nodeType: QueryElementTypes; +} & QueryGraphEdgeAttribute; + +export type QueryGraphEdges = { + type: string; + sourceHandleData: QueryGraphEdgeHandle; + targetHandleData: QueryGraphEdgeHandle; +}; + +export type QueryGraphEdgesOpt = { + type?: string; + sourceHandleData?: QueryGraphEdgeHandle; + targetHandleData?: QueryGraphEdgeHandle; +}; + +// export class QueryGraph extends Graph<QueryGraphNodes, GAttributes, GAttributes>; // is in utils.ts diff --git a/libs/shared/lib/querybuilder/model/graphology/utils.ts b/libs/shared/lib/querybuilder/model/graphology/utils.ts index d31faed13..5da3f1cd3 100644 --- a/libs/shared/lib/querybuilder/model/graphology/utils.ts +++ b/libs/shared/lib/querybuilder/model/graphology/utils.ts @@ -1,20 +1,22 @@ -import { - setQuerybuilderNodes, - store, -} from '@graphpolaris/shared/lib/data-access/store'; +// import { setQuerybuilderNodes, store } from '@graphpolaris/shared/lib/data-access/store'; import Graph, { MultiGraph } from 'graphology'; -import { - Attributes as GAttributes, - Attributes, - SerializedGraph, -} from 'graphology-types'; +import { Attributes as GAttributes, Attributes, SerializedGraph } from 'graphology-types'; import { EntityNodeAttributes, - NodeAttributes, - QueryGraph, + LogicNodeAttributes, + QueryGraphEdgeAttribute, + QueryGraphEdgeHandle, + QueryGraphEdges, + QueryGraphEdgesOpt, + QueryGraphNodes, RelationNodeAttributes, } from './model'; import { XYPosition } from 'reactflow'; +import { Handles, QueryElementTypes } from '../reactflow'; +import { toHandleId } from '..'; +import { s } from 'vitest/dist/env-afee91f0'; +import { SchemaAttribute, SchemaAttributeTypes } from '@graphpolaris/shared/lib/schema'; +import { InputNodeType, InputNodeTypeTypes } from '../logic/general'; /** monospace fontsize table */ const widthPerFontsize = { @@ -23,23 +25,20 @@ const widthPerFontsize = { 10: 6.0167, }; -export type QueryMultiGraph = SerializedGraph< - NodeAttributes | EntityNodeAttributes | RelationNodeAttributes, - GAttributes, - GAttributes ->; - -export class QueryMultiGraphology extends MultiGraph< - NodeAttributes | EntityNodeAttributes | RelationNodeAttributes, - GAttributes, - GAttributes -> { - public addPill2Graphology( - attributes: NodeAttributes | EntityNodeAttributes | RelationNodeAttributes, - id: string = (Date.now() + Math.floor(Math.random() * 1000)).toString() - ): string | null { +export type AddEdge2GraphologyOptions = { + // attributeSourceHandle?: string; + sourceHandleName?: string; + targetHandleName?: string; + attributeTargetHandleName?: string; + attributeTargetHandleType?: string; +}; + +export type QueryMultiGraph = SerializedGraph<QueryGraphNodes, QueryGraphEdges, GAttributes>; + +export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges, GAttributes> { + public configureDefaults(attributes: QueryGraphNodes): QueryGraphNodes { const { type, name } = attributes; - if (!type || !name) return null; + if (!type || !name) throw Error('type or name is not defined'); let { x, y } = attributes; // Check if x and y are present, otherwise set them to 0 @@ -49,13 +48,163 @@ export class QueryMultiGraphology extends MultiGraph< // Get the width and height of a node const { width, height } = calcWidthHeightOfPill(attributes); + attributes.x = x; + attributes.y = y; + attributes.width = width; + attributes.height = height; + + if (!attributes.id) attributes.id = 'id_' + (Date.now() + Math.floor(Math.random() * 1000)).toString(); + + return attributes; + } + + public addPill2Graphology(attributes: QueryGraphNodes, optAttributes: SchemaAttribute[] | undefined = undefined): QueryGraphNodes { + attributes = this.configureDefaults(attributes); + if (!attributes.type || !attributes.name || !attributes.id) throw Error('type or name is not defined'); + + // fix handles + const defaultHandleData: QueryGraphEdgeHandle = { + nodeId: attributes.id, + nodeName: attributes.name, + nodeType: attributes.type, + }; + if (attributes.type === QueryElementTypes.Entity) { + var entityAttributes = attributes as EntityNodeAttributes; + entityAttributes.leftRelationHandleId = { ...defaultHandleData, attributeName: Handles.EntityLeft }; + entityAttributes.rightRelationHandleId = { ...defaultHandleData, attributeName: Handles.EntityRight }; + optAttributes?.forEach((optAttribute) => { + if (!entityAttributes.attributes) entityAttributes.attributes = []; + entityAttributes.attributes.push({ + handleData: { ...defaultHandleData, attributeName: optAttribute.name, attributeType: optAttribute.type }, + }); + }); + } else if (attributes.type === QueryElementTypes.Relation) { + var relationAttributes = attributes as RelationNodeAttributes; + relationAttributes.leftEntityHandleId = { ...defaultHandleData, attributeName: Handles.RelationLeft }; + relationAttributes.rightEntityHandleId = { ...defaultHandleData, attributeName: Handles.RelationRight }; + optAttributes?.forEach((optAttribute) => { + if (!relationAttributes.attributes) relationAttributes.attributes = []; + relationAttributes.attributes.push({ + handleData: { ...defaultHandleData, attributeName: optAttribute.name, attributeType: optAttribute.type }, + }); + }); + } else if (attributes.type === QueryElementTypes.Logic) { + throw Error('using wrong function! use addLogicPill2Graphology instead'); + } + + // Add a node to the graphology object + const nodeId = this.addNode(attributes.id, { ...attributes }); + + // Set the new nodes in the query builder slice TODO: maybe remove for efficiency + // dispatch(setQuerybuilderNodes(this.export())); // Can't do this due to loop import + + return attributes; + } + + public addLogicPill2Graphology(attributes: QueryGraphNodes, inputValues: Record<string, InputNodeTypeTypes> = {}): QueryGraphNodes { + attributes = this.configureDefaults(attributes); + if (!attributes.type) attributes.type = QueryElementTypes.Logic; + if (!attributes.name || !attributes.id) throw Error('type or name is not defined'); + + if (attributes.type === QueryElementTypes.Logic) { + attributes = attributes as LogicNodeAttributes; + (attributes as LogicNodeAttributes).logic.inputs.forEach((input, i) => { + // Setup default non-linked inputs as regular values matching the input expected type + if (!(attributes as LogicNodeAttributes).inputs) (attributes as LogicNodeAttributes).inputs = {}; + (attributes as LogicNodeAttributes).inputs[input.name] = inputValues?.[input.name] || input.default; + }); + // (attributes as LogicNodeAttributes).leftEntityHandleId = getHandleId(attributes.id, name, type, Handles.RelationLeft, ''); + // (attributes as LogicNodeAttributes).rightEntityHandleId = getHandleId(attributes.id, name, type, Handles.RelationRight, ''); + } else throw Error('using wrong function! use addPill2Graphology instead'); + // Add a node to the graphology object - const nodeId = this.addNode(id, { ...attributes, x, y, width, height }); + const nodeId = this.addNode(attributes.id, { ...attributes }); + + // Set the new nodes in the query builder slice TODO: maybe remove for efficiency + // dispatch(setQuerybuilderNodes(this.export())); // Can't do this due to loop import + + return attributes; + } + + public addEdge2Graphology( + source: QueryGraphNodes, + target: QueryGraphNodes, + attributes: QueryGraphEdgesOpt = {}, + options: AddEdge2GraphologyOptions = {} + ): string | null { + let sourceAttributeName = ''; + let sourceAttributeType: InputNodeType | undefined; + let targetAttributeName = ''; + let targetAttributeType: InputNodeType | undefined; + + if (source.type === QueryElementTypes.Entity) { + source = source as EntityNodeAttributes; + if (!!options?.sourceHandleName) { + sourceAttributeName = options?.sourceHandleName; + sourceAttributeType = source?.attributes?.find((a) => a.handleData.attributeName === sourceAttributeName)?.handleData.attributeType; + } else { + sourceAttributeName = Handles.EntityRight; + } + } else if (source.type === QueryElementTypes.Relation) { + sourceAttributeName = Handles.RelationRight; + // } else if (source.type === QueryElementTypes.Logic && !!options?.attributeSourceHandle) { + // sourceAttributeName = Handles.LogicRight; + // sourceAttributeType = options.attributeSourceHandle; + } else if (source.type === QueryElementTypes.Logic) { + if (!options.sourceHandleName) throw Error('sourceHandleName is not defined'); + sourceAttributeName = options.sourceHandleName; + sourceAttributeType = (source as LogicNodeAttributes).logic.output.type; + if (!sourceAttributeType) throw Error(`sourceHandleName ${sourceAttributeName} does not exist!`); + } else { + throw Error('source.type is not correctly defined'); + } + + if (target.type === QueryElementTypes.Entity) { + // if (!!options?.attributeTargetHandle) { + // targetAttributeName = Handles.EntityAttribute; + // targetAttributeType = options.attributeTargetHandle; + // } else { + targetAttributeName = Handles.EntityRight; + // } + } else if (target.type === QueryElementTypes.Relation) { + targetAttributeName = Handles.RelationRight; + } else if (target.type === QueryElementTypes.Logic) { + if (!options.targetHandleName) throw Error('targetHandleName is not defined'); + targetAttributeName = options.targetHandleName; + targetAttributeType = (target as LogicNodeAttributes).logic.inputs.find((i) => i.name === targetAttributeName)?.type; + if (!targetAttributeType) throw Error(`targetHandleName ${targetAttributeName} does not exist!`); + } else { + throw Error('target.type is not correctly defined'); + } + + if (!source.id) throw Error('source.id is not defined'); + if (!target.id) throw Error('target.id is not defined'); + if (!attributes.type) attributes.type = 'connection'; + // fix handles + attributes.sourceHandleData = { + nodeId: source.id, + nodeName: source.name || '', + nodeType: source.type, + attributeName: sourceAttributeName, + attributeType: sourceAttributeType, + }; + attributes.targetHandleData = { + nodeId: target.id, + nodeName: target.name || '', + nodeType: target.type, + attributeName: targetAttributeName, + attributeType: targetAttributeType, + }; + + // console.log('newEdge', attributes, source, target); + + // Add an edge to the graphology object + const edgeId = this.addEdge(source.id, target.id, attributes as QueryGraphEdges); // Set the new nodes in the query builder slice TODO: maybe remove for efficiency - store.dispatch(setQuerybuilderNodes(this.export())); + // store.dispatch(setQuerybuilderNodes(this.export())); - return nodeId; + return edgeId; } } @@ -115,7 +264,7 @@ export function calcWidthHeightOfPill(attributes: Attributes): { } /** Interface for x and y position of node */ -export interface NodePosition extends XYPosition { } +export interface NodePosition extends XYPosition {} /** Returns from-position of relation node */ export function RelationPosToFromEntityPos(position: XYPosition): NodePosition { diff --git a/libs/shared/lib/querybuilder/model/index.ts b/libs/shared/lib/querybuilder/model/index.ts index fdf75046a..b1b8ea45a 100644 --- a/libs/shared/lib/querybuilder/model/index.ts +++ b/libs/shared/lib/querybuilder/model/index.ts @@ -1,4 +1,34 @@ +import { NodeAttribute, QueryGraphEdgeHandle, QueryGraphNodes } from './graphology'; +import { InputNodeType } from './logic/general'; +import { QueryElementTypes, SchemaReactflowNode } from './reactflow'; + export * from './BackendQueryFormat'; export * from './graphology'; export * from './logic'; -export * from './reactflow'; \ No newline at end of file +export * from './reactflow'; + +type ExtraProps = { extra?: string; separator?: string }; +export function toHandleId(handleData: QueryGraphEdgeHandle, separator: string = '__'): string { + // if (!extra) extra = ''; + if (!separator) separator = '__'; + return [handleData.nodeId, handleData.nodeType, handleData.nodeName, handleData.attributeName, handleData.attributeType].join(separator); +} +// export function getHandleIdFromGraphology(node: QueryGraphNodes, attribute: NodeAttribute, options: ExtraProps = {}): string { +// return toHandleId(node.id || '', node.name, node.type, attribute.name, attribute.type, options); +// } +export function handleDataFromReactflowToId(node: SchemaReactflowNode, attribute: NodeAttribute, options: ExtraProps = {}): string { + if (!node.data.name) throw Error('node.data is not defined'); + return toHandleId({ + nodeId: node.id, + nodeName: node.data.name, + nodeType: node.type as QueryElementTypes, + attributeName: attribute.handleData.attributeName, + attributeType: attribute.handleData.attributeType, + }); +} +export function toHandleData(handleId: string, separator: string = '__'): QueryGraphEdgeHandle { + let [nodeId, nodeType, nodeName, attributeName, attributeType] = handleId.split(separator); + const _nodeType = nodeType as QueryElementTypes; + const _attributeType = attributeType as InputNodeType; + return { nodeId, nodeType: _nodeType, nodeName, attributeName, attributeType: _attributeType }; +} diff --git a/libs/shared/lib/querybuilder/model/logic/general.ts b/libs/shared/lib/querybuilder/model/logic/general.ts new file mode 100644 index 000000000..7963d68ac --- /dev/null +++ b/libs/shared/lib/querybuilder/model/logic/general.ts @@ -0,0 +1,205 @@ +export type InputNodeTypeTypes = string | number | boolean; +export type InputNodeType = 'string' | 'float' | 'int' | 'bool' | 'date' | 'time' | 'datetime' | 'duration'; + +export enum MathFilterTypes { + EQUAL = '==', + NOT_EQUAL = '!=', + GREATER_THAN = '>', + LESS_THAN = '<', + GREATER_THAN_EQUAL = '>=', + LESS_THAN_EQUAL = '<=', +} + +export enum MathAggregationTypes { + AVG = 'Avg', + COUNT = 'Count', + MAX = 'Max', + MIN = 'Min', + SUM = 'Sum', +} + +export enum MathFunctionTypes { + ADD = '+', + SUBTRACT = '-', + MULTIPLY = '*', + DIVIDE = '/', + // MODULO = '%', + // POWER = '^', + // SQRT = 'Sqrt', + // ABS = 'Abs', + // LOG = 'Log', + // EXP = 'Exp', + // ROUND = 'Round', + // CEIL = 'Ceil', + // FLOOR = 'Floor', +} + +export enum StringFunctionTypes { + LOWER = 'Lower', + UPPER = 'Upper', + // CONCAT = 'Concat', + // SUBSTRING = 'Substring', + // TRIM = 'Trim', +} + +export enum StringFilterTypes { + EQUAL = '==', + NOT_EQUAL = '!=', + LIKE = 'Like', + // NOT_LIKE = 'Not Like', + // IN = 'In', + // NOT_IN = 'Not In', +} + +export enum LogicFunctionTypes { + AND = 'And', + OR = 'Or', + NOT = 'Not', +} + +// Logic +export type AndLogicStatement = ['And', AnyStatement, AnyStatement]; +export type OrLogicStatement = ['Or', AnyStatement, AnyStatement]; +export type NotLogicStatement = ['Not', AnyStatement]; +export type LogicStatement = AndLogicStatement | OrLogicStatement | NotLogicStatement; + +// Numbers +export type PlusStatement = ['+', AnyStatement, AnyStatement]; +export type MinusStatement = ['-', AnyStatement, AnyStatement]; +export type MultiplyStatement = ['*', AnyStatement, AnyStatement]; +export type DivideStatement = ['/', AnyStatement, AnyStatement]; +export type ModuloStatement = ['%', AnyStatement, AnyStatement]; +export type PowerStatement = ['^', AnyStatement, AnyStatement]; +export type SqrtStatement = ['Sqrt', AnyStatement]; +export type AbsStatement = ['Abs', AnyStatement]; +export type LogStatement = ['Log', AnyStatement]; +export type ExpStatement = ['Exp', AnyStatement]; +export type AllMathStatement = + | PlusStatement + | MinusStatement + | MultiplyStatement + | DivideStatement + | ModuloStatement + | PowerStatement + | SqrtStatement + | AbsStatement + | LogStatement + | ExpStatement; + +// Comparisons +export type EqualStatement = ['==', AnyStatement, AnyStatement]; +export type NotEqualStatement = ['!=', AnyStatement, AnyStatement]; +export type GreaterThanStatement = ['>', AnyStatement, AnyStatement]; +export type LessThanStatement = ['<', AnyStatement, AnyStatement]; +export type GreaterThanEqualStatement = ['>=', AnyStatement, AnyStatement]; +export type LessThanEqualStatement = ['<=', AnyStatement, AnyStatement]; +export type AllComparisonStatement = + | EqualStatement + | NotEqualStatement + | GreaterThanStatement + | LessThanStatement + | GreaterThanEqualStatement + | LessThanEqualStatement; + +// Strings +export type ConcatStatement = ['Concat', AnyStatement, AnyStatement]; +export type LowerStatement = ['Lower', AnyStatement]; +export type UpperStatement = ['Upper', AnyStatement]; +export type SubstringStatement = ['Substring', AnyStatement, AnyStatement, AnyStatement]; +export type TrimStatement = ['Trim', AnyStatement]; +export type AllStringStatement = ConcatStatement | LowerStatement | UpperStatement | SubstringStatement | TrimStatement; + +// Dates +export type DateStatement = ['Date', AnyStatement]; +export type YearStatement = ['Year', AnyStatement]; +export type MonthStatement = ['Month', AnyStatement]; +export type DayStatement = ['Day', AnyStatement]; +export type HourStatement = ['Hour', AnyStatement]; +export type MinuteStatement = ['Minute', AnyStatement]; +export type SecondStatement = ['Second', AnyStatement]; +export type AllDateStatement = + | DateStatement + | YearStatement + | MonthStatement + | DayStatement + | HourStatement + | MinuteStatement + | SecondStatement; + +// Aggregations +export type AvgStatement = ['Avg', AnyStatement]; +export type CountStatement = ['Count', AnyStatement]; +export type MaxStatement = ['Max', AnyStatement]; +export type MinStatement = ['Min', AnyStatement]; +export type SumStatement = ['Sum', AnyStatement]; +export type RoundStatement = ['Round', AnyStatement]; +export type CeilStatement = ['Ceil', AnyStatement]; +export type FloorStatement = ['Floor', AnyStatement]; +export type AllAggregationStatement = + | AvgStatement + | CountStatement + | MaxStatement + | MinStatement + | SumStatement + | RoundStatement + | CeilStatement + | FloorStatement; + +// Filters +export type EqualFilterStatement = ['==', AnyStatement, AnyStatement]; +export type NotEqualFilterStatement = ['!=', AnyStatement, AnyStatement]; +export type LikeFilterStatement = ['Like', AnyStatement, AnyStatement]; +export type NotLikeFilterStatement = ['Not Like', AnyStatement, AnyStatement]; +export type InFilterStatement = ['In', AnyStatement, AnyStatement]; +export type NotInFilterStatement = ['Not In', AnyStatement, AnyStatement]; +export type FilterStatement = + | EqualFilterStatement + | NotEqualFilterStatement + | LikeFilterStatement + | NotLikeFilterStatement + | InFilterStatement + | NotInFilterStatement; + +// Regular +type WithPrefix<T extends string> = `${T}${string}`; + +export type NumberStatement = number; +export type StringStatement = string; +export type BooleanStatement = boolean; +export type DateStringStatement = string; +export type ReferenceStatement = WithPrefix<'@'>; +export type RegularStatement = NumberStatement | StringStatement | BooleanStatement | DateStringStatement; +export type AllRegularStatement = RegularStatement | ReferenceStatement; + +export type AllLogicStatement = + | LogicStatement + | AllMathStatement + | AllComparisonStatement + | AllStringStatement + | AllDateStatement + | AllAggregationStatement + | FilterStatement; + +export type AnyStatement = AllLogicStatement | AllRegularStatement; + +export interface OutputNode { + name: string; + type: InputNodeType; +} + +export interface InputNode { + name: string; + type: InputNodeType; + default: number | string | boolean; +} + +export interface GeneralDescription<T> { + name: string; + type: T; + description: string; + numInputs?: number; + inputs: InputNode[]; + output: OutputNode; + key: string; + logic: AllLogicStatement; +} diff --git a/libs/shared/lib/querybuilder/model/logic/graphFunctions.tsx b/libs/shared/lib/querybuilder/model/logic/graphFunctions.tsx new file mode 100644 index 000000000..9ba0343f8 --- /dev/null +++ b/libs/shared/lib/querybuilder/model/logic/graphFunctions.tsx @@ -0,0 +1,146 @@ +// TODO; move each to its own pill & logic (allowing for more complex and modular functions) +// /** +// * This program has been developed by students from the bachelor Computer Science at +// * Utrecht University within the Software Project course. +// * © Copyright Utrecht University (Department of Information and Computing Sciences) +// */ + +// /** What functions exist +// * Default is for the functions in the function bar that don't exist yet. +// */ +export enum GraphFunctionTypes { + // GroupBy = 'groupBy', + link = 'linkPrediction', + communityDetection = 'communityDetection', + centrality = 'centrality', + shortestPath = 'shortestPath', + default = 'default', +} + +// export enum FunctionArgTypes { +// group = 'group', +// by = 'by', +// relation = 'relation', +// modifier = 'modifier', +// constraints = 'constraints', +// result = 'result', +// ID1 = 'ID1', +// ID2 = 'ID2', +// } +// /** All arguments that groupby pill needs */ +// export const DefaultGroupByArgs: FunctionArgs = { +// group: { displayName: 'Group', connectable: true, value: '', visible: true }, +// by: { displayName: 'By', connectable: true, value: '_id', visible: true }, +// relation: { +// displayName: 'On', +// connectable: true, +// value: undefined, +// visible: true, +// }, +// modifier: { +// displayName: 'Modifier: ', +// connectable: false, +// value: '', +// visible: true, +// }, +// constraints: { +// displayName: 'Constraints: ', +// connectable: true, +// value: undefined, +// visible: true, +// }, +// result: { +// displayName: 'Result: ', +// connectable: true, +// value: undefined, +// visible: true, +// }, +// }; +// /** All arguments that linkprediction pill needs */ +// export const DefaultLinkPredictionArgs: FunctionArgs = { +// linkprediction: { +// //currently the querybuilder shows this name instead of the display name that needs to be changed. +// displayName: 'linkprediction', +// connectable: false, +// value: undefined, +// visible: true, +// }, +// }; + +// /** All arguments that CommunictyDetection pill needs */ +// export const DefaultCommunictyDetectionArgs: FunctionArgs = { +// CommunityDetection: { +// displayName: 'CommunityDetection', +// connectable: false, +// value: undefined, +// visible: true, +// }, +// }; + +// /** All arguments that centrality pill needs */ +// export const DefaultCentralityArgs: FunctionArgs = { +// centrality: { +// displayName: 'centrality', +// connectable: false, +// value: undefined, +// visible: true, +// }, +// }; + +// /** All arguments that centrality pill needs */ +// export const DefaultShortestPathArgs: FunctionArgs = { +// shortestPath: { +// displayName: 'shortestPath', +// connectable: false, +// value: undefined, +// visible: true, +// }, +// }; + +// // TODO: fix this to somehow make use of the enum +// /** Returns the correct arguments depending on the type */ +// export const DefaultFunctionArgs: { [type: string]: FunctionArgs } = { +// groupBy: DefaultGroupByArgs, +// linkPrediction: DefaultLinkPredictionArgs, +// communityDetection: DefaultCommunictyDetectionArgs, +// centrality: DefaultCentralityArgs, +// shortestPath: DefaultShortestPathArgs, +// }; + +/** Interface for what function descriptions need */ +export interface GraphFunctionDescription { + name: string; + type: GraphFunctionTypes; + description: string; +} + +/** All available functions in the function bar. */ +export const GraphFunctions: Record<string, GraphFunctionDescription> = { + centrality: { + name: 'centrality', + type: GraphFunctionTypes.centrality, + description: 'W.I.P. Shows the importance of nodes', + }, + communityDetection: { + name: 'Community Detection', + type: GraphFunctionTypes.communityDetection, + description: 'Group entities connected by a relation based on how interconnected they are.', + }, + // groupBy: { + // name: 'Group By', + // type: GraphFunctionTypes.GroupBy, + // description: + // 'W.I.P. Per entity of type A, generate aggregate statistics of an attribute of either all links of a relation, or all nodes of an entity of type B connected to the type A entity by a relation.', + // }, + link: { + name: 'Link Prediction', + type: GraphFunctionTypes.link, + description: + 'For each pair of entities from a given type, determine the overlap between nodes they are connect to by a given relation.', + }, + shortestPath: { + name: 'shortestPath', + type: GraphFunctionTypes.shortestPath, + description: 'W.I.P. shortest path. Shows the shortest path between nodes', + }, +}; diff --git a/libs/shared/lib/querybuilder/model/logic/index.ts b/libs/shared/lib/querybuilder/model/logic/index.ts index 465510a48..00f4dfeff 100644 --- a/libs/shared/lib/querybuilder/model/logic/index.ts +++ b/libs/shared/lib/querybuilder/model/logic/index.ts @@ -1 +1,31 @@ -export * from './queryFunctions' \ No newline at end of file +import { + GeneralDescription, + InputNodeType, + MathFunctionTypes, + MathFilterTypes, + StringFilterTypes, + StringFunctionTypes, + MathAggregationTypes, +} from './general'; +import { MathAggregations } from './mathAggregations'; +import { MathFilters } from './mathFilters'; +import { MathFunctions } from './mathFunctions'; +import { StringFilters } from './stringFilters'; +import { StringFunctions } from './stringFunctions'; + +export type AllLogicTypes = MathFilterTypes | MathFunctionTypes | MathAggregationTypes | StringFilterTypes | StringFunctionTypes; +export type AllLogicDescriptions = GeneralDescription<AllLogicTypes>; + +export const AllLogicMap: Record<string, AllLogicDescriptions> = { + ...Object.fromEntries(Object.values(MathFilters).map((x) => [x.key, x])), + ...Object.fromEntries(Object.values(MathFunctions).map((x) => [x.key, x])), + ...Object.fromEntries(Object.values(MathAggregations).map((x) => [x.key, x])), + ...Object.fromEntries(Object.values(StringFilters).map((x) => [x.key, x])), + ...Object.fromEntries(Object.values(StringFunctions).map((x) => [x.key, x])), +}; + +export * from './graphFunctions'; +export * from './mathFunctions'; +export * from './mathFilters'; +export * from './stringFunctions'; +export * from './stringFilters'; diff --git a/libs/shared/lib/querybuilder/model/logic/mathAggregations.tsx b/libs/shared/lib/querybuilder/model/logic/mathAggregations.tsx new file mode 100644 index 000000000..5baebb3b6 --- /dev/null +++ b/libs/shared/lib/querybuilder/model/logic/mathAggregations.tsx @@ -0,0 +1,745 @@ +/** + * This program has been developed by students from the bachelor Computer Science at + * Utrecht University within the Software Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ + +import { Position } from 'reactflow'; +import { GeneralDescription, MathAggregationTypes } from './general'; + +export const MathAggregations: Record<MathAggregationTypes, GeneralDescription<MathAggregationTypes>> = { + [MathAggregationTypes.AVG]: { + key: 'mathFunctionAvg', + name: 'Average', + type: MathAggregationTypes.AVG, + description: 'Average of all values', + numInputs: 1, + inputs: [{ name: '1', type: 'float', default: 0 }], + output: { name: 'avg', type: 'float' }, + logic: ['Avg', '@1'], + }, + [MathAggregationTypes.COUNT]: { + key: 'mathFunctionCount', + name: 'Count', + type: MathAggregationTypes.COUNT, + description: 'Count the number of values', + numInputs: 1, + inputs: [{ name: '1', type: 'float', default: 0 }], + + output: { name: 'count', type: 'float' }, + logic: ['Count', '@1'], + }, + [MathAggregationTypes.MAX]: { + key: 'mathFunctionMax', + name: 'Maximum', + type: MathAggregationTypes.MAX, + description: 'Maximum of all values', + numInputs: 1, + inputs: [{ name: '1', type: 'float', default: 0 }], + output: { name: 'max', type: 'float' }, + logic: ['Max', '@1'], + }, + [MathAggregationTypes.MIN]: { + key: 'mathFunctionMin', + name: 'Minimum', + type: MathAggregationTypes.MIN, + description: 'Minimum of all values', + numInputs: 1, + inputs: [{ name: '1', type: 'float', default: 0 }], + output: { name: 'min', type: 'float' }, + logic: ['Min', '@1'], + }, + [MathAggregationTypes.SUM]: { + key: 'mathFunctionSum', + name: 'Sum', + type: MathAggregationTypes.SUM, + description: 'Sum of all values', + numInputs: 1, + inputs: [{ name: '1', type: 'float', default: 0 }], + output: { name: 'sum', type: 'float' }, + logic: ['Sum', '@1'], + }, + // [MathAggregationTypes.STD]: { + // key: 'mathFunctionStd', + // name: 'Standard Deviation', + // type: MathAggregationTypes.STD, + // description: 'Standard deviation of all values', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'std', type: 'float' }, + // logic: ['Std', '@1'], + // }, + // [MathAggregationTypes.ADD]: { + // key: 'mathFunctionAdd', + // name: 'Add', + // type: MathAggregationTypes.ADD, + // description: 'Add two values', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'float', default: 0 }, + // { name: '2', type: 'float', default: 0 }, + // ], + // output: { name: '+', type: 'float' }, + // logic: ['+', '@1', '@2'], + // }, + // [MathAggregationTypes.SUBTRACT]: { + // key: 'mathFunctionSubtract', + // name: 'Subtract', + // type: MathAggregationTypes.SUBTRACT, + // description: 'Subtract two values', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'float' }, + // { name: '2', type: 'float' }, + // ], + // output: { name: '-', type: 'float' }, + // logic: ['-', '@1', '@2'], + // }, + // [MathAggregationTypes.MULTIPLY]: { + // key: 'mathFunctionMultiply', + // name: 'Multiply', + // type: MathAggregationTypes.MULTIPLY, + // description: 'Multiply two values', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'float' }, + // { name: '2', type: 'float' }, + // ], + // output: { name: '*', type: 'float' }, + // logic: ['*', '@1', '@2'], + // }, + // [MathAggregationTypes.DIVIDE]: { + // key: 'mathFunctionDivide', + // name: 'Divide', + // type: MathAggregationTypes.DIVIDE, + // description: 'Divide two values', + + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'float' }, + // { name: '2', type: 'float' }, + // ], + // output: { name: '/', type: 'float' }, + // logic: ['/', '@1', '@2'], + // }, + // [MathAggregationTypes.POWER]: { + // key: 'mathFunctionPower', + // name: 'Power', + // type: MathAggregationTypes.POWER, + // description: 'Raise a value to the power of another value', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'float' }, + // { name: '2', type: 'float' }, + // ], + // output: { name: '^', type: 'float' }, + // logic: ['^', '@1', '@2'], + // }, + // [MathAggregationTypes.SQRT]: { + // key: 'mathFunctionSqrt', + // name: 'Square Root', + // type: MathAggregationTypes.SQRT, + // description: 'Square root of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'sqrt', type: 'float' }, + // logic: ['Sqrt', '@1'], + // }, + // [MathAggregationTypes.LOG]: { + // key: 'mathFunctionLog', + // name: 'Logarithm', + // type: MathAggregationTypes.LOG, + // description: 'Logarithm of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'log', type: 'float' }, + // logic: ['Log', '@1'], + // }, + // [MathAggregationTypes.EXP]: { + // key: 'mathFunctionExp', + // name: 'Exponential', + // type: MathAggregationTypes.EXP, + // description: 'Exponential of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'exp', type: 'float' }, + // logic: ['Exp', '@1'], + // }, + // [MathAggregationTypes.ABS]: { + // key: 'mathFunctionAbs', + // name: 'Absolute Value', + // type: MathAggregationTypes.ABS, + // description: 'Absolute value of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'abs', type: 'float' }, + // logic: ['Abs', '@1'], + // }, + // [MathAggregationTypes.CEIL]: { + // key: 'mathFunctionCeil', + // name: 'Ceiling', + // type: MathAggregationTypes.CEIL, + // description: 'Ceiling of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'ceil', type: 'float' }, + // logic: ['Ceil', '@1'], + // }, + // [MathAggregationTypes.FLOOR]: { + // key: 'mathFunctionFloor', + // name: 'Floor', + // type: MathAggregationTypes.FLOOR, + // description: 'Floor of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'floor', type: 'float' }, + // logic: ['Floor', '@1'], + // }, + // [MathAggregationTypes.ROUND]: { + // key: 'mathFunctionRound', + // name: 'Round', + // type: MathAggregationTypes.ROUND, + // description: 'Round a value to the nearest integer', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'round', type: 'float' }, + // logic: ['Round', '@1'], + // }, + // [MathAggregationTypes.TRUNC]: { + // key: 'mathFunctionTrunc', + // name: 'Truncate', + // type: MathAggregationTypes.TRUNC, + // description: 'Truncate a value to the nearest integer towards zero', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + + // output: { name: 'trunc', type: 'float' }, + // logic: ['Trunc', '@1'], + // }, + // [MathAggregationTypes.RANDOM]: { + // key: 'mathFunctionRandom', + // name: 'Random', + // type: MathAggregationTypes.RANDOM, + // description: 'Random value between 0 and 1', + // numInputs: 0, + // inputs: [], + // output: { name: 'random', type: 'float' }, + // logic: ['Random'], + // }, + // [MathAggregationTypes.RANDOMINT]: { + // key: 'mathFunctionRandomInt', + // name: 'Random Integer', + // type: MathAggregationTypes.RANDOMINT, + // description: 'Random integer between two values', + // numInputs: 2, + // inputs: [ + // { name: 'min', type: 'float' }, + // { name: 'max', type: 'float' }, + // ], + // output: { name: 'randomInt', type: 'float' }, + // logic: ['RandomInt', '@1', '@2'], + // }, + // [MathAggregationTypes.SIN]: { + // key: 'mathFunctionSin', + // name: 'Sine', + // type: MathAggregationTypes.SIN, + // description: 'Sine of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'sin', type: 'float' }, + // logic: ['Sin', '@1'], + // }, + // [MathAggregationTypes.COS]: { + // key: 'mathFunctionCos', + // name: 'Cosine', + // type: MathAggregationTypes.COS, + // description: 'Cosine of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'cos', type: 'float' }, + // logic: ['Cos', '@1'], + // }, + // [MathAggregationTypes.TAN]: { + // key: 'mathFunctionTan', + // name: 'Tangent', + // type: MathAggregationTypes.TAN, + // description: 'Tangent of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'tan', type: 'float' }, + // logic: ['Tan', '@1'], + // }, + // [MathAggregationTypes.ASIN]: { + // key: 'mathFunctionAsin', + // name: 'Arcsine', + // type: MathAggregationTypes.ASIN, + // description: 'Arcsine of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'asin', type: 'float' }, + // logic: ['Asin', '@1'], + // }, + // [MathAggregationTypes.ACOS]: { + // key: 'mathFunctionAcos', + // name: 'Arccosine', + // type: MathAggregationTypes.ACOS, + // description: 'Arccosine of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'acos', type: 'float' }, + // logic: ['Acos', '@1'], + // }, + // [MathAggregationTypes.ATAN]: { + // key: 'mathFunctionAtan', + // name: 'Arctangent', + // type: MathAggregationTypes.ATAN, + // description: 'Arctangent of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'atan', type: 'float' }, + // logic: ['Atan', '@1'], + // }, + // [MathAggregationTypes.SINH]: { + // key: 'mathFunctionSinh', + // name: 'Hyperbolic Sine', + // type: MathAggregationTypes.SINH, + // description: 'Hyperbolic sine of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'sinh', type: 'float' }, + // logic: ['Sinh', '@1'], + // }, + // [MathAggregationTypes.COSH]: { + // key: 'mathFunctionCosh', + // name: 'Hyperbolic Cosine', + // type: MathAggregationTypes.COSH, + // description: 'Hyperbolic cosine of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'cosh', type: 'float' }, + // logic: ['Cosh', '@1'], + // }, + // [MathAggregationTypes.TANH]: { + // key: 'mathFunctionTanh', + // name: 'Hyperbolic Tangent', + // type: MathAggregationTypes.TANH, + // description: 'Hyperbolic tangent of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'tanh', type: 'float' }, + // logic: ['Tanh', '@1'], + // }, + // [MathAggregationTypes.ASINH]: { + // key: 'mathFunctionAsinh', + // name: 'Hyperbolic Arcsine', + // type: MathAggregationTypes.ASINH, + // description: 'Hyperbolic arcsine of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'asinh', type: 'float' }, + // logic: ['Asinh', '@1'], + // }, + // [MathAggregationTypes.ACOSH]: { + // key: 'mathFunctionAcosh', + // name: 'Hyperbolic Arccosine', + // type: MathAggregationTypes.ACOSH, + // description: 'Hyperbolic arccosine of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'acosh', type: 'float' }, + // logic: ['Acosh', '@1'], + // }, + // [MathAggregationTypes.ATANH]: { + // key: 'mathFunctionAtanh', + // name: 'Hyperbolic Arctangent', + // type: MathAggregationTypes.ATANH, + // description: 'Hyperbolic arctangent of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'atanh', type: 'float' }, + // logic: ['Atanh', '@1'], + // }, + // [MathAggregationTypes.DEGREES]: { + // key: 'mathFunctionDegrees', + // name: 'Degrees', + // type: MathAggregationTypes.DEGREES, + // description: 'Convert a value from radians to degrees', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'degrees', type: 'float' }, + // logic: ['Degrees', '@1'], + // }, + // [MathAggregationTypes.RADIANS]: { + // key: 'mathFunctionRadians', + // name: 'Radians', + // type: MathAggregationTypes.RADIANS, + // description: 'Convert a value from degrees to radians', + // numInputs: 1, + + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'radians', type: 'float' }, + // logic: ['Radians', '@1'], + // }, + // [MathAggregationTypes.SIGN]: { + // key: 'mathFunctionSign', + // name: 'Sign', + // type: MathAggregationTypes.SIGN, + // description: 'Sign of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'sign', type: 'float' }, + // logic: ['Sign', '@1'], + // }, + // [MathAggregationTypes.RANDOMNORMAL]: { + // key: 'mathFunctionRandomNormal', + // name: 'Random Normal', + // type: MathAggregationTypes.RANDOMNORMAL, + // description: 'Random value from a normal distribution', + // numInputs: 2, + // inputs: [ + // { name: 'mean', type: 'float' }, + // { name: 'std', type: 'float' }, + // ], + // output: { name: 'randomNormal', type: 'float' }, + // logic: ['RandomNormal', '@1', '@2'], + // }, + // [MathAggregationTypes.RANDOMLOGNORMAL]: { + // key: 'mathFunctionRandomLogNormal', + // name: 'Random Log Normal', + // type: MathAggregationTypes.RANDOMLOGNORMAL, + // description: 'Random value from a log normal distribution', + // numInputs: 2, + // inputs: [ + // { name: 'mean', type: 'float' }, + // { name: 'std', type: 'float' }, + // ], + // output: { name: 'randomLogNormal', type: 'float' }, + // logic: ['RandomLogNormal', '@1', '@2'], + // }, + // [MathAggregationTypes.RANDOMEXPONENTIAL]: { + // key: 'mathFunctionRandomExponential', + // name: 'Random Exponential', + // type: MathAggregationTypes.RANDOMEXPONENTIAL, + // description: 'Random value from an exponential distribution', + // numInputs: 1, + // inputs: [{ name: 'lambda', type: 'float' }], + // output: { name: 'randomExponential', type: 'float' }, + // logic: ['RandomExponential', '@1'], + // }, + // [MathAggregationTypes.RANDOMGAMMA]: { + // key: 'mathFunctionRandomGamma', + // name: 'Random Gamma', + // type: MathAggregationTypes.RANDOMGAMMA, + // description: 'Random value from a gamma distribution', + // numInputs: 2, + // inputs: [ + // { name: 'alpha', type: 'float' }, + // { name: 'beta', type: 'float' }, + // ], + // output: { name: 'randomGamma', type: 'float' }, + // logic: ['RandomGamma', '@1', '@2'], + // }, + // [MathAggregationTypes.RANDOMBETA]: { + // key: 'mathFunctionRandomBeta', + // name: 'Random Beta', + // type: MathAggregationTypes.RANDOMBETA, + // description: 'Random value from a beta distribution', + // numInputs: 2, + // inputs: [ + // { name: 'alpha', type: 'float' }, + // { name: 'beta', type: 'float' }, + // ], + // output: { name: 'randomBeta', type: 'float' }, + // logic: ['RandomBeta', '@1', '@2'], + // }, + // [MathAggregationTypes.RANDOMCHISQUARE]: { + // key: 'mathFunctionRandomChiSquare', + // name: 'Random Chi Square', + // type: MathAggregationTypes.RANDOMCHISQUARE, + // description: 'Random value from a chi square distribution', + // numInputs: 1, + // inputs: [{ name: 'k', type: 'float' }], + // output: { name: 'randomChiSquare', type: 'float' }, + // logic: ['RandomChiSquare', '@1'], + // }, + // [MathAggregationTypes.RANDOMWEIBULL]: { + // key: 'mathFunctionRandomWeibull', + // name: 'Random Weibull', + // type: MathAggregationTypes.RANDOMWEIBULL, + // description: 'Random value from a Weibull distribution', + // numInputs: 2, + // inputs: [ + // { name: 'k', type: 'float' }, + // { name: 'lambda', type: 'float' }, + // ], + // output: { name: 'randomWeibull', type: 'float' }, + // logic: ['RandomWeibull', '@1', '@2'], + // }, + // [MathAggregationTypes.RANDOMCAUCHY]: { + // key: 'mathFunctionRandomCauchy', + // name: 'Random Cauchy', + // type: MathAggregationTypes.RANDOMCAUCHY, + // description: 'Random value from a Cauchy distribution', + // numInputs: 2, + // inputs: [ + // { name: 'x0', type: 'float' }, + // { name: 'gamma', type: 'float' }, + // ], + // output: { name: 'randomCauchy', type: 'float' }, + // logic: ['RandomCauchy', '@1', '@2'], + // }, + // [MathAggregationTypes.RANDOMPOISSON]: { + // key: 'mathFunctionRandomPoisson', + // name: 'Random Poisson', + // type: MathAggregationTypes.RANDOMPOISSON, + // description: 'Random value from a Poisson distribution', + // numInputs: 1, + // inputs: [{ name: 'lambda', type: 'float' }], + // output: { name: 'randomPoisson', type: 'float' }, + // logic: ['RandomPoisson', '@1'], + // }, + // [MathAggregationTypes.RANDOMIRWINHALL]: { + // key: 'mathFunctionRandomIrwinHall', + // name: 'Random Irwin Hall', + // type: MathAggregationTypes.RANDOMIRWINHALL, + // description: 'Random value from an Irwin Hall distribution', + // numInputs: 2, + // inputs: [ + // { name: 'n', type: 'float' }, + // { name: 'scale', type: 'float' }, + // ], + // output: { name: 'randomIrwinHall', type: 'float' }, + // logic: ['RandomIrwinHall', '@1', '@2'], + // }, + // [MathAggregationTypes.CHIQUARETEST]: { + // key: 'mathFunctionChiSquareTest', + // name: 'Chi Square Test', + // type: MathAggregationTypes.CHIQUARETEST, + // description: 'Chi square test', + // numInputs: 2, + // inputs: [ + // { name: 'observed', type: 'float' }, + // { name: 'expected', type: 'float' }, + // ], + // output: { name: 'chiSquareTest', type: 'float' }, + // logic: ['ChiSquareTest', '@1', '@2'], + // }, + // [MathAggregationTypes.CORRELATION]: { + // key: 'mathFunctionCorrelation', + // name: 'Correlation', + // type: MathAggregationTypes.CORRELATION, + // description: 'Correlation between two values', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'float' }, + // { name: '2', type: 'float' }, + // ], + // output: { name: 'correlation', type: 'float' }, + // logic: ['Correlation', '@1', '@2'], + // }, + // [MathAggregationTypes.COVARIANCE]: { + // key: 'mathFunctionCovariance', + // name: 'Covariance', + // type: MathAggregationTypes.COVARIANCE, + // description: 'Covariance between two values', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'float' }, + // { name: '2', type: 'float' }, + // ], + // output: { name: 'covariance', type: 'float' }, + // logic: ['Covariance', '@1', '@2'], + // }, + // [MathAggregationTypes.FREQUENCY]: { + // key: 'mathFunctionFrequency', + // name: 'Frequency', + // type: MathAggregationTypes.FREQUENCY, + // description: 'Frequency of a value in a dataset', + // numInputs: 2, + // inputs: [ + // { name: 'value', type: 'float' }, + // { name: 'dataset', type: 'float' }, + // ], + // output: { name: 'frequency', type: 'float' }, + // logic: ['Frequency', '@1', '@2'], + // }, + // [MathAggregationTypes.MEAN]: { + // key: 'mathFunctionMean', + // name: 'Mean', + // type: MathAggregationTypes.MEAN, + // description: 'Mean of a dataset', + // numInputs: 1, + // inputs: [{ name: 'dataset', type: 'float' }], + // output: { name: 'mean', type: 'float' }, + // logic: ['Mean', '@1'], + // }, + // [MathAggregationTypes.MEDIAN]: { + // key: 'mathFunctionMedian', + // name: 'Median', + // type: MathAggregationTypes.MEDIAN, + // description: 'Median of a dataset', + // numInputs: 1, + // inputs: [{ name: 'dataset', type: 'float' }], + // output: { name: 'median', type: 'float' }, + // logic: ['Median', '@1'], + // }, + // [MathAggregationTypes.MODE]: { + // key: 'mathFunctionMode', + // name: 'Mode', + // type: MathAggregationTypes.MODE, + // description: 'Mode of a dataset', + // numInputs: 1, + // inputs: [{ name: 'dataset', type: 'float' }], + // output: { name: 'mode', type: 'float' }, + // logic: ['Mode', '@1'], + // }, + // [MathAggregationTypes.RANK]: { + // key: 'mathFunctionRank', + // name: 'Rank', + // type: MathAggregationTypes.RANK, + // description: 'Rank of a value in a dataset', + // numInputs: 2, + // inputs: [ + // { name: 'value', type: 'float' }, + // { name: 'dataset', type: 'float' }, + // ], + // output: { name: 'rank', type: 'float' }, + // logic: ['Rank', '@1', '@2'], + // }, + // [MathAggregationTypes.STDEV]: { + // key: 'mathFunctionStdev', + // name: 'Standard Deviation', + // type: MathAggregationTypes.STDEV, + // description: 'Standard deviation of a dataset', + // numInputs: 1, + // inputs: [{ name: 'dataset', type: 'float' }], + // output: { name: 'stdev', type: 'float' }, + // logic: ['Stdev', '@1'], + // }, + // [MathAggregationTypes.VARIANCE]: { + // key: 'mathFunctionVariance', + // name: 'Variance', + // type: MathAggregationTypes.VARIANCE, + // description: 'Variance of a dataset', + // numInputs: 1, + // inputs: [{ name: 'dataset', type: 'float' }], + // output: { name: 'variance', type: 'float' }, + // logic: ['Variance', '@1'], + // }, + // [MathAggregationTypes.ZSCORE]: { + // key: 'mathFunctionZscore', + // name: 'Z-Score', + // type: MathAggregationTypes.ZSCORE, + // description: 'Z-score of a value in a dataset', + // numInputs: 2, + // inputs: [ + // { name: 'value', type: 'float' }, + // { name: 'dataset', type: 'float' }, + // ], + // output: { name: 'zscore', type: 'float' }, + // logic: ['Zscore', '@1', '@2'], + // }, + // [MathAggregationTypes.AND]: { + // key: 'mathFunctionAnd', + // name: 'And', + // type: MathAggregationTypes.AND, + // description: 'Logical AND of two values', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'boolean' }, + // { name: '2', type: 'boolean' }, + // ], + // output: { name: 'and', type: 'boolean' }, + // logic: ['And', '@1', '@2'], + // }, + // [MathAggregationTypes.OR]: { + // key: 'mathFunctionOr', + // name: 'Or', + // type: MathAggregationTypes.OR, + // description: 'Logical OR of two values', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'boolean' }, + // { name: '2', type: 'boolean' }, + // ], + // output: { name: 'or', type: 'boolean' }, + // logic: ['Or', '@1', '@2'], + // }, + // [MathAggregationTypes.NOT]: { + // key: 'mathFunctionNot', + // name: 'Not', + // type: MathAggregationTypes.NOT, + // description: 'Logical NOT of a value', + // numInputs: 1, + // inputs: [{ name: '1', type: 'boolean' }], + // output: { name: 'not', type: 'boolean' }, + // logic: ['Not', '@1'], + // }, + + // { + // key: 'mathFunctionCount', + // name: 'Count', + // type: MathAggregationTypes.COUNT, + // description: 'Count the number of values', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'count', type: 'float' }, + // logic: ['Count', '@1'], + // }, + // { + // key: 'mathFunctionMax', + // name: 'Maximum', + // type: MathAggregationTypes.MAX, + // description: 'Maximum of all values', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'max', type: 'float' }, + // logic: ['Max', '@1'], + // }, + // { + // key: 'mathFunctionMin', + // name: 'Minimum', + // type: MathAggregationTypes.MIN, + // description: 'Minimum of all values', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'min', type: 'float' }, + // logic: ['Min', '@1'], + // }, + // { + // key: 'mathFunctionSum', + // name: 'Sum', + // type: MathAggregationTypes.SUM, + // description: 'Sum of all values', + // numInputs: 1, + // inputs: [{ name: '1', type: 'float' }], + // output: { name: 'sum', type: 'float' }, + // logic: ['Sum', '@1'], + // }, + // // { + // // key: 'mathFunctionStd', + // // name: 'Standard Deviation', + // // type: MathAggregationTypes.STD, + // // description: 'Standard deviation of all values', + // // numInputs: 1, + // // inputs: [{ name: '1', type: 'float' }], + // // output: { name: 'std', type: 'float' }, + // // logic: ['Std', '@1'], + // // }, + // { + // key: 'mathFunctionAdd', + // name: 'Add', + // type: MathAggregationTypes.ADD, + // description: 'Add two values', + // numInputs: 2, + // inputs: [ + // { name: '1', type: 'float' }, + // { name: '2', type: 'float' }, + // ], + // output: { name: '+', type: 'float' }, + // logic: ['+', '@1', '@2'], + // }, +}; + +/** All available functions in the function bar. */ +export const MathAggregationArray: Array<GeneralDescription<MathAggregationTypes>> = Object.values(MathAggregations); diff --git a/libs/shared/lib/querybuilder/model/logic/mathFilters.tsx b/libs/shared/lib/querybuilder/model/logic/mathFilters.tsx new file mode 100644 index 000000000..0c235dd78 --- /dev/null +++ b/libs/shared/lib/querybuilder/model/logic/mathFilters.tsx @@ -0,0 +1,93 @@ +/** + * This program has been developed by students from the bachelor Computer Science at + * Utrecht University within the Software Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ + +import { Position } from 'reactflow'; +import { GeneralDescription, InputNode, MathFilterTypes } from './general'; + +export const MathFilters: Record<MathFilterTypes, GeneralDescription<MathFilterTypes>> = { + [MathFilterTypes.EQUAL]: { + key: 'mathFilterEqual', + name: 'Equal', + type: MathFilterTypes.EQUAL, + description: 'Equal to another value', + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '==', type: 'float' }, + logic: ['==', '@1', '@2'], + }, + [MathFilterTypes.NOT_EQUAL]: { + key: 'mathFilterNotEqual', + name: 'Not Equal', + type: MathFilterTypes.NOT_EQUAL, + description: 'Not equal to another value', + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '!=', type: 'float' }, + logic: ['!=', '@1', '@2'], + }, + [MathFilterTypes.LESS_THAN]: { + key: 'mathFilterLessThan', + name: 'Less Than', + type: MathFilterTypes.LESS_THAN, + description: 'Less than another value', + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '<', type: 'float' }, + logic: ['<', '@1', '@2'], + }, + [MathFilterTypes.LESS_THAN_EQUAL]: { + key: 'mathFilterLessThanOrEqual', + name: 'Less Than or Equal', + type: MathFilterTypes.LESS_THAN_EQUAL, + description: 'Less than or equal to another value', + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '<=', type: 'float' }, + logic: ['<=', '@1', '@2'], + }, + [MathFilterTypes.GREATER_THAN]: { + key: 'mathFilterGreaterThan', + name: 'Greater Than', + type: MathFilterTypes.GREATER_THAN, + description: 'Greater than another value', + + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '>', type: 'float' }, + logic: ['>', '@1', '@2'], + }, + [MathFilterTypes.GREATER_THAN_EQUAL]: { + key: 'mathFilterGreaterThanOrEqual', + name: 'Greater Than or Equal', + type: MathFilterTypes.GREATER_THAN_EQUAL, + description: 'Greater than or equal to another value', + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '>=', type: 'float' }, + logic: ['>=', '@1', '@2'], + }, +}; + +/** All available functions in the function bar. */ +export const MathFilterArray: Array<GeneralDescription<MathFilterTypes>> = Object.values(MathFilters); diff --git a/libs/shared/lib/querybuilder/model/logic/mathFunctions.tsx b/libs/shared/lib/querybuilder/model/logic/mathFunctions.tsx new file mode 100644 index 000000000..5375664aa --- /dev/null +++ b/libs/shared/lib/querybuilder/model/logic/mathFunctions.tsx @@ -0,0 +1,67 @@ +/** + * This program has been developed by students from the bachelor Computer Science at + * Utrecht University within the Software Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ + +import { Position } from 'reactflow'; +import { GeneralDescription, MathFunctionTypes } from './general'; + +export const MathFunctions: Record<MathFunctionTypes, GeneralDescription<MathFunctionTypes>> = { + [MathFunctionTypes.ADD]: { + key: 'mathFunctionAdd', + name: 'Add', + type: MathFunctionTypes.ADD, + description: 'Add two values', + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '+', type: 'float' }, + logic: ['+', '@1', '@2'], + }, + [MathFunctionTypes.SUBTRACT]: { + key: 'mathFunctionSubtract', + name: 'Subtract', + type: MathFunctionTypes.SUBTRACT, + description: 'Subtract two values', + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '-', type: 'float' }, + logic: ['-', '@1', '@2'], + }, + [MathFunctionTypes.MULTIPLY]: { + key: 'mathFunctionMultiply', + name: 'Multiply', + type: MathFunctionTypes.MULTIPLY, + description: 'Multiply two values', + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '*', type: 'float' }, + logic: ['*', '@1', '@2'], + }, + [MathFunctionTypes.DIVIDE]: { + key: 'mathFunctionDivide', + name: 'Divide', + type: MathFunctionTypes.DIVIDE, + description: 'Divide two values', + + numInputs: 2, + inputs: [ + { name: '1', type: 'float', default: 0 }, + { name: '2', type: 'float', default: 0 }, + ], + output: { name: '/', type: 'float' }, + logic: ['/', '@1', '@2'], + }, +}; + +/** All available functions in the function bar. */ +export const MathFunctionArray: Array<GeneralDescription<MathFunctionTypes>> = Object.values(MathFunctions); diff --git a/libs/shared/lib/querybuilder/model/logic/queryFunctions.tsx b/libs/shared/lib/querybuilder/model/logic/queryFunctions.tsx deleted file mode 100644 index 404f2f3ea..000000000 --- a/libs/shared/lib/querybuilder/model/logic/queryFunctions.tsx +++ /dev/null @@ -1,148 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { AnyNode, FunctionArgs } from '../reactflow/model'; - -/** What functions exist - * Default is for the functions in the function bar that don't exist yet. - */ -export enum FunctionTypes { - GroupBy = 'groupBy', - link = 'linkPrediction', - communityDetection = 'communityDetection', - centrality = 'centrality', - shortestPath = 'shortestPath', - default = 'default', -} - -export enum FunctionArgTypes { - group = 'group', - by = 'by', - relation = 'relation', - modifier = 'modifier', - constraints = 'constraints', - result = 'result', - ID1 = 'ID1', - ID2 = 'ID2', -} -/** All arguments that groupby pill needs */ -export const DefaultGroupByArgs: FunctionArgs = { - group: { displayName: 'Group', connectable: true, value: '', visible: true }, - by: { displayName: 'By', connectable: true, value: '_id', visible: true }, - relation: { - displayName: 'On', - connectable: true, - value: undefined, - visible: true, - }, - modifier: { - displayName: 'Modifier: ', - connectable: false, - value: '', - visible: true, - }, - constraints: { - displayName: 'Constraints: ', - connectable: true, - value: undefined, - visible: true, - }, - result: { - displayName: 'Result: ', - connectable: true, - value: undefined, - visible: true, - }, -}; -/** All arguments that linkprediction pill needs */ -export const DefaultLinkPredictionArgs: FunctionArgs = { - linkprediction: { - //currently the querybuilder shows this name instead of the display name that needs to be changed. - displayName: 'linkprediction', - connectable: false, - value: undefined, - visible: true, - }, -}; - -/** All arguments that CommunictyDetection pill needs */ -export const DefaultCommunictyDetectionArgs: FunctionArgs = { - CommunityDetection: { - displayName: 'CommunityDetection', - connectable: false, - value: undefined, - visible: true, - }, -}; - -/** All arguments that centrality pill needs */ -export const DefaultCentralityArgs: FunctionArgs = { - centrality: { - displayName: 'centrality', - connectable: false, - value: undefined, - visible: true, - }, -}; - -/** All arguments that centrality pill needs */ -export const DefaultShortestPathArgs: FunctionArgs = { - shortestPath: { - displayName: 'shortestPath', - connectable: false, - value: undefined, - visible: true, - }, -}; - -// TODO: fix this to somehow make use of the enum -/** Returns the correct arguments depending on the type */ -export const DefaultFunctionArgs: { [type: string]: FunctionArgs } = { - groupBy: DefaultGroupByArgs, - linkPrediction: DefaultLinkPredictionArgs, - communityDetection: DefaultCommunictyDetectionArgs, - centrality: DefaultCentralityArgs, - shortestPath: DefaultShortestPathArgs, -}; - -/** Interface for what function descriptions need */ -export interface FunctionDescription { - name: string; - type: FunctionTypes; - description: string; -} - -/** All available functions in the function bar. */ -export const AvailableFunctions: Record<string, FunctionDescription> = { - centrality: { - name: 'centrality', - type: FunctionTypes.centrality, - description: 'W.I.P. Shows the importance of nodes', - }, - communityDetection: { - name: 'Community Detection', - type: FunctionTypes.communityDetection, - description: - 'Group entities connected by a relation based on how interconnected they are.', - }, - groupBy: { - name: 'Group By', - type: FunctionTypes.GroupBy, - description: - 'W.I.P. Per entity of type A, generate aggregate statistics of an attribute of either all links of a relation, or all nodes of an entity of type B connected to the type A entity by a relation.', - }, - link: { - name: 'Link Prediction', - type: FunctionTypes.link, - description: - 'For each pair of entities from a given type, determine the overlap between nodes they are connect to by a given relation.', - }, - shortestPath: { - name: 'shortestPath', - type: FunctionTypes.shortestPath, - description: 'W.I.P. shortest path. Shows the shortest path between nodes', - }, -}; diff --git a/libs/shared/lib/querybuilder/model/logic/stringFilters.tsx b/libs/shared/lib/querybuilder/model/logic/stringFilters.tsx new file mode 100644 index 000000000..5799d1804 --- /dev/null +++ b/libs/shared/lib/querybuilder/model/logic/stringFilters.tsx @@ -0,0 +1,66 @@ +/** + * This program has been developed by students from the bachelor Computer Science at + * Utrecht University within the Software Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ + +import { Position } from 'reactflow'; +import { GeneralDescription, StringFilterTypes } from './general'; + +export const StringFilters: Record<StringFilterTypes, GeneralDescription<StringFilterTypes>> = { + [StringFilterTypes.EQUAL]: { + key: 'stringFilterEqual', + name: 'Equal', + type: StringFilterTypes.EQUAL, + description: 'Equal to another value', + numInputs: 1, + inputs: [ + { name: '1', type: 'string', default: '' }, + { name: '2', type: 'string', default: '' }, + ], + output: { name: StringFilterTypes.EQUAL, type: 'bool' }, + logic: [StringFilterTypes.EQUAL, '@1', '@2'], + }, + [StringFilterTypes.NOT_EQUAL]: { + key: 'stringFilterNotEqual', + name: 'Not Equal', + type: StringFilterTypes.NOT_EQUAL, + description: 'Not equal to another value', + numInputs: 1, + inputs: [ + { name: '1', type: 'string', default: '' }, + { name: '2', type: 'string', default: '' }, + ], + output: { name: StringFilterTypes.NOT_EQUAL, type: 'bool' }, + logic: [StringFilterTypes.NOT_EQUAL, '@1', '@2'], + }, + // [StringFilterTypes.IN]: { + // key: 'stringFilterContains', + // name: 'Contains', + // type: StringFilterTypes.IN, + // description: 'Contains another value', + // numInputs: 1, + // inputs: [ + // { name: '1', type: 'string', default: '' }, + // { name: '2', type: 'string', default: '' }, + // ], + // output: { name: StringFilterTypes.IN, type: 'bool' }, + // logic: [StringFilterTypes.IN, '@1', '@2'], + // }, + [StringFilterTypes.LIKE]: { + key: 'stringFilterLike', + name: 'Like', + type: StringFilterTypes.LIKE, + description: 'Like another value', + numInputs: 1, + inputs: [ + { name: '1', type: 'string', default: '' }, + { name: '2', type: 'string', default: '' }, + ], + output: { name: StringFilterTypes.LIKE, type: 'bool' }, + logic: [StringFilterTypes.LIKE, '@1', '@2'], + }, +}; + +/** All available functions in the function bar. */ +export const StringFilterArray: Array<GeneralDescription<StringFilterTypes>> = Object.values(StringFilters); diff --git a/libs/shared/lib/querybuilder/model/logic/stringFunctions.tsx b/libs/shared/lib/querybuilder/model/logic/stringFunctions.tsx new file mode 100644 index 000000000..3ba9a3d1a --- /dev/null +++ b/libs/shared/lib/querybuilder/model/logic/stringFunctions.tsx @@ -0,0 +1,44 @@ +/** + * This program has been developed by students from the bachelor Computer Science at + * Utrecht University within the Software Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ + +import { Position } from 'reactflow'; +import { GeneralDescription, StringFunctionTypes } from './general'; + +export const StringFunctions: Record<StringFunctionTypes, GeneralDescription<StringFunctionTypes>> = { + // [StringFunctionTypes.CONCAT]: { + // key: 'stringFunctionConcat', + // name: 'Concat', + // type: StringFunctionTypes.CONCAT, + // description: 'Lowercase all characters', + // numInputs: 1, + // inputs: [{ name: '1', type: 'string', default: '' }], + // output: { name: 'lower_case', type: 'string' }, + // logic: ['Lower', '@1'], + // }, + [StringFunctionTypes.LOWER]: { + key: 'stringFunctionLowerCase', + name: 'Lower Case', + type: StringFunctionTypes.LOWER, + description: 'Lowercase all characters', + numInputs: 1, + inputs: [{ name: '1', type: 'string', default: '' }], + output: { name: 'lower_case', type: 'string' }, + logic: ['Lower', '@1'], + }, + [StringFunctionTypes.UPPER]: { + key: 'stringFunctionUpperCase', + name: 'Upper Case', + type: StringFunctionTypes.UPPER, + description: 'Uppercase all characters', + numInputs: 1, + inputs: [{ name: '1', type: 'string', default: '' }], + output: { name: 'upper_case', type: 'string' }, + logic: ['Upper', '@1'], + }, +}; + +/** All available functions in the function bar. */ +export const StringFunctionArray: Array<GeneralDescription<StringFunctionTypes>> = Object.values(StringFunctions); diff --git a/libs/shared/lib/querybuilder/model/logic/utils.ts b/libs/shared/lib/querybuilder/model/logic/utils.ts new file mode 100644 index 000000000..e69de29bb diff --git a/libs/shared/lib/querybuilder/model/reactflow/handles.tsx b/libs/shared/lib/querybuilder/model/reactflow/handles.tsx index 71a09d206..8c913b2c3 100644 --- a/libs/shared/lib/querybuilder/model/reactflow/handles.tsx +++ b/libs/shared/lib/querybuilder/model/reactflow/handles.tsx @@ -8,18 +8,23 @@ * Enums for possible values for handles of nodes in the query builder. * Possible handles for an entity node. */ -import { FunctionArgTypes } from '../logic/queryFunctions'; -import { AnyNode, QueryElementTypes } from './model'; +// import { FunctionArgTypes } from '../logic/graphFunctions'; +import { SchemaReactflowNode, QueryElementTypes } from './model'; /** Links need handles to what they are connected to (and which side) */ export enum Handles { - RelationLeft = 'leftEntityHandle', //target - RelationRight = 'rightEntityHandle', //target + RelationLeft = 'relationLeftHandle', //target + RelationRight = 'relationRightHandle', //source ToAttribute = 'attributesHandle', //target - ToRelation = 'relationsHandle', //source + EntityLeft = 'entityLeftHandle', //source + EntityRight = 'entityRightHandle', //target + EntityAttribute = 'entityAttributeHandle', //source OnAttribute = 'onAttributeHandle', //source ReceiveFunction = 'receiveFunctionHandle', //target FunctionBase = 'functionHandle_', // + name from FunctionTypes args //source + FromAttribute = 'fromAttributeHandle', //source + LogicLeft = 'leftLogicHandle', + LogicRight = 'rightLogicHandle', } /** returns a boolean that check whether the handle is a function handle */ @@ -27,36 +32,34 @@ export function isFunctionHandle(handle: string): boolean { return handle.startsWith(Handles.FunctionBase); } -/** - * returns the functionargumenttype - * Currently only working for groupby but made that in the future other functions can use this as well. - */ -export function functionHandleToType(handle: string): FunctionArgTypes { - if (isFunctionHandle(handle)) - return handle.slice(Handles.FunctionBase.length) as FunctionArgTypes; - else - throw new Error('Incorrectly trying to assert handle to function handle'); -} -/** Creates a handle from a functiontype */ -export function typeToFunctionHandle(type: FunctionArgTypes): string { - return Handles.FunctionBase + type; -} +// /** +// * returns the functionargumenttype +// * Currently only working for groupby but made that in the future other functions can use this as well. +// */ +// export function functionHandleToType(handle: string): FunctionArgTypes { +// if (isFunctionHandle(handle)) +// return handle.slice(Handles.FunctionBase.length) as FunctionArgTypes; +// else +// throw new Error('Incorrectly trying to assert handle to function handle'); +// } +// /** Creates a handle from a functiontype */ +// export function typeToFunctionHandle(type: FunctionArgTypes): string { +// return Handles.FunctionBase + type; +// } /** * Return a list of handles to which a connection can be made by dragging a node nearby */ -export function nodeToHandlesThatCanReceiveDragconnect( - node: AnyNode -): string[] { +export function nodeToHandlesThatCanReceiveDragconnect(node: SchemaReactflowNode): string[] { switch (node.type) { case QueryElementTypes.Entity: return [Handles.ToAttribute]; case QueryElementTypes.Relation: return [Handles.RelationLeft, Handles.RelationRight, Handles.ToAttribute]; - case QueryElementTypes.Function: - return [Handles.ToAttribute]; - case QueryElementTypes.Attribute: - return []; + // case QueryElementTypes.Function: + // return [Handles.ToAttribute]; + // case QueryElementTypes.Attribute: + // return []; default: throw new Error('Unsupported node'); } @@ -64,17 +67,18 @@ export function nodeToHandlesThatCanReceiveDragconnect( /** * Return a list of handles from which a connection can be made while dragging the node they are on + * @deprecated */ -export function nodeToHandlesThatCanSendDragconnect(node: AnyNode): string[] { +export function nodeToHandlesThatCanSendDragconnect(node: SchemaReactflowNode): string[] { switch (node.type) { - case QueryElementTypes.Entity: - return [Handles.ToRelation]; - case QueryElementTypes.Relation: - return []; - case QueryElementTypes.Function: - return []; - case QueryElementTypes.Attribute: - return [Handles.OnAttribute]; + // case QueryElementTypes.Entity: + // return [Handles.ToRelation]; + // case QueryElementTypes.Relation: + // return []; + // case QueryElementTypes.Function: + // return []; + // case QueryElementTypes.Attribute: + // return [Handles.OnAttribute]; default: throw new Error('Unsupported node'); } diff --git a/libs/shared/lib/querybuilder/model/reactflow/index.ts b/libs/shared/lib/querybuilder/model/reactflow/index.ts index 7d808fb6e..a65fadcdf 100644 --- a/libs/shared/lib/querybuilder/model/reactflow/index.ts +++ b/libs/shared/lib/querybuilder/model/reactflow/index.ts @@ -1,4 +1,4 @@ -export * from './handles' -export * from './model' -export * from './pillHandles' -export * from './utils' \ No newline at end of file +export * from './handles'; +export * from './model'; +export * from './pillHandles'; +export * from './utils'; diff --git a/libs/shared/lib/querybuilder/model/reactflow/model.tsx b/libs/shared/lib/querybuilder/model/reactflow/model.tsx index fddca14b8..ed03cfb64 100644 --- a/libs/shared/lib/querybuilder/model/reactflow/model.tsx +++ b/libs/shared/lib/querybuilder/model/reactflow/model.tsx @@ -4,127 +4,66 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ import { Edge as ReactEdge, NodeProps } from 'reactflow'; +import { EntityData, EntityNodeAttributes, LogicData, LogicNodeAttributes, RelationData, RelationNodeAttributes } from '../graphology'; +import { Schema } from 'inspector'; /** Enums for the possible types of query elements */ export enum QueryElementTypes { Entity = 'entity', Relation = 'relation', - Attribute = 'attribute', - Function = 'function', + // Attribute = 'attribute', + // Function = 'function', + Logic = 'logic', } /** List of possible query element types */ export const possibleTypes: string[] = [ QueryElementTypes.Entity, QueryElementTypes.Relation, - QueryElementTypes.Attribute, - QueryElementTypes.Function, + QueryElementTypes.Logic, + // QueryElementTypes.Attribute, + // QueryElementTypes.Function, ]; /** All the possible react flow nodes. */ -export type AnyNode = EntityNode | RelationNode | AttributeNode | FunctionNode; -export type Edge = ReactEdge<any>; -export type AnyElement = AnyNode | Edge; -export type AnyNodeData = - | EntityData - | RelationData - | AttributeData - | FunctionData; +export type SchemaReactflowEntityNode = NodeProps<EntityNodeAttributes>; +export type SchemaReactflowRelationNode = NodeProps<RelationNodeAttributes>; +export type SchemaReactflowLogicNode = NodeProps<LogicNodeAttributes>; -export type EntityNode = NodeProps<EntityData>; -export type RelationNode = NodeProps<RelationData>; -export type AttributeNode = NodeProps<AttributeData>; -export type FunctionNode = NodeProps<FunctionData>; -export type ModifierNode = NodeProps<ModifierData>; - -export interface NodeData { - fadeIn: boolean; -} - -export interface ModifierData { - type: string; -} - -/** Interface for the data in an entity node. */ -export interface EntityData extends NodeData { - name: string; -} - -/** Interface for the data in an relation node. */ -export interface RelationData extends NodeData { - name: string; - collection: string; - depth: { min: number; max: number }; -} - -/** Interface for the data in an attribute node. - * Can have multiple constraint datatypes. - * string MatchTypes: exact/contains/startswith/endswith. - * int MatchTypes: GT/LT/EQ. - * bool MatchTypes: EQ/NEQ. - */ -export interface AttributeData extends NodeData { - name: string; - value: string; - dataType: string; - matchType: string; -} - -export interface FunctionArg { - displayName: string; - connectable: boolean; // does this arg have a connectable handle? - value: string | undefined; // undefined means no text input - visible: boolean; // for implicit connections -} - -export interface FunctionArgs { - [name: string]: FunctionArg; -} - -/** Interface for the data in a function node. */ -export interface FunctionData extends NodeData { - functionType: string; - args: FunctionArgs; -} +export type SchemaReactflowNode = SchemaReactflowEntityNode | SchemaReactflowRelationNode | SchemaReactflowLogicNode; +export type SchemaReactflowEdge = ReactEdge<any>; +export type SchemaReactflowElement = SchemaReactflowNode | SchemaReactflowEdge; +export type SchemaReactflowNodeData = EntityData | RelationData | LogicData; /** Interface for updating the edges. */ export interface updateEdges { - newEdge: Edge | undefined; - removeEdge: Edge | undefined; + newEdge: SchemaReactflowEdge | undefined; + removeEdge: SchemaReactflowEdge | undefined; } /** * Checks if a node is an entityNode. - * @param {AnyNode} node The node that has to checked. + * @param {SchemaReactflowNode} node The node that has to checked. * @returns True and casts if the node is an EntityNode. */ -export function isEntityNode(node: AnyNode): node is EntityNode { +export function isEntityNode(node: SchemaReactflowNode): node is SchemaReactflowEntityNode { return node.type == QueryElementTypes.Entity; } /** * Checks if a node is a RelationNode. - * @param {AnyNode} node The node that has to checked. + * @param {SchemaReactflowNode} node The node that has to checked. * @returns True and casts if the node is an RelationNode. */ -export function isRelationNode(node: AnyNode): node is RelationNode { +export function isRelationNode(node: SchemaReactflowNode): node is SchemaReactflowRelationNode { return node.type == QueryElementTypes.Relation; } /** - * Checks if a node is an AttributeNode. - * @param {AnyNode} node The node that has to checked. - * @returns True and casts if the node is an AttributeNode. - */ -export function isAttributeNode(node: AnyNode): node is AttributeNode { - return node.type == QueryElementTypes.Attribute; -} - -/** - * Checks if a node is an FunctionNode. - * @param {AnyNode} node The node that has to checked. - * @returns True and casts if the node is an FunctionNode. + * Checks if a node is a LogicNode. + * @param {SchemaReactflowNode} node The node that has to checked. + * @returns True and casts if the node is an LogicNode. */ -export function isFunctionNode(node: AnyNode): node is FunctionNode { - return node.type == QueryElementTypes.Function; +export function isLogicNode(node: SchemaReactflowNode): node is SchemaReactflowLogicNode { + return node.type == QueryElementTypes.Logic; } diff --git a/libs/shared/lib/querybuilder/model/reactflow/utils.ts b/libs/shared/lib/querybuilder/model/reactflow/utils.ts index 2bdf0acdb..b8a0dd791 100644 --- a/libs/shared/lib/querybuilder/model/reactflow/utils.ts +++ b/libs/shared/lib/querybuilder/model/reactflow/utils.ts @@ -1,68 +1,26 @@ -import Graph, { MultiGraph } from 'graphology'; -import { Attributes } from 'graphology-types'; -import { Node, Edge, XYPosition } from 'reactflow'; +import Graph from 'graphology'; +import { Node, Edge } from 'reactflow'; +import { toHandleId } from '..'; // Takes the querybuilder graph as an input and creates react flow elements for them. -export function createReactFlowElements(graph: Graph): { +export function createReactFlowElements<T extends Graph>( + graph: T +): { nodes: Array<Node>; edges: Array<Edge>; } { const nodes: Array<Node> = []; const edges: Array<Edge> = []; - graph.forEachNode((node: string, attributes: Attributes): void => { - let data; - let position = { x: attributes?.x || 0, y: attributes?.y || 0 }; - - switch (attributes.type) { - case 'entity': - data = { - isConnected: graph - .neighbors(node) - .some((nb) => graph.getNodeAttribute(nb, 'type') == 'relation'), - }; - break; - case 'relation': - data = { - isFromEntityConnected: graph - .inNeighbors(node) - .some((nb) => graph.getNodeAttribute(nb, 'type') == 'entity'), - isToEntityConnected: graph - .outNeighbors(node) - .some((nb) => graph.getNodeAttribute(nb, 'type') == 'entity'), - }; - break; - case 'attribute': { - const ERNeighbors = graph.outNeighbors(node).filter((nb) => { - const type = graph.getNodeAttribute(nb, 'type'); - return type == 'entity' || type == 'relation'; - }); - let attributeOfA = ''; - if (ERNeighbors.length > 0) - attributeOfA = graph.getNodeAttribute(ERNeighbors[0], 'type'); - data = { - datatype: attributes.datatype, - operator: attributes.operator, - value: attributes.value, - attributeOfA: attributeOfA, - }; - // Get the position of the attribute, based on the connection to entity or relation - const p = getAttributePosition(node, graph); - if (p) position = p; - break; - } - } - // Each pill should have a name and type - data = { - ...attributes, - ...data, - }; + graph.forEachNode((node, attributes): void => { + // console.log('attributes', attributes); - const RFNode: Node = { + let position = { x: attributes?.x || 0, y: attributes?.y || 0 }; + const RFNode: Node<typeof attributes> = { id: node, type: attributes.type, position: position, - data: data, + data: attributes, }; nodes.push(RFNode); }); @@ -72,62 +30,20 @@ export function createReactFlowElements(graph: Graph): { // connection from attributes don't have visible connection lines // if (attributes.type == 'attribute_connection') return; - const RFEdge: Edge = { + const RFEdge: Edge<typeof attributes> = { id: edge, source: source, target: target, type: 'connection', - sourceHandle: attributes.sourceHandle, - targetHandle: attributes.targetHandle, + sourceHandle: toHandleId(attributes.sourceHandleData), + targetHandle: toHandleId(attributes.targetHandleData), + data: attributes, + zIndex: 1, }; edges.push(RFEdge); }); - return { nodes, edges }; -} - -/** Gets the position of an attribute based on the connection to an entity or relation. - * It uses the position of the parent pill and what the index is of this attribute in all - * the connected attributes to the parent. - */ -function getAttributePosition( - id: string, - nodes: MultiGraph -): XYPosition | undefined { - const nbs = nodes.filterOutNeighbors(id, (_, { type }) => - ['entity', 'relation'].includes(type) - ); + // console.log('nodes', nodes, 'edges', edges); - if (nbs.length > 1) - console.log( - 'WARNING: attribute connected to more than one entity or relation' - ); - else if (nbs.length == 1) { - const nb = nbs[0]; - const connectedAttributes = nodes.filterInNeighbors( - nb, - (_, { type }) => type == 'attribute' - ); - - // An entity can have more attributes, what is the attributes index in the attributes array of that entity? - let nthAttibute = -1; - for (let i = 0; i < connectedAttributes.length; i++) { - if (connectedAttributes[i] == id) { - nthAttibute = i; - break; - } - } - - const nbAttr = nodes.getNodeAttributes(nb); - - const pos = { x: nbAttr.x + 30, y: nbAttr.y + nbAttr.h }; - // ASSUMES THAT EACH ATTRIBUTE HAS THE SAME HEIGHT - const heightOfAttributes = nodes.getNodeAttribute(id, 'h') - 1; - pos.y += nthAttibute * heightOfAttributes; - - return pos; - } - - // If the attribute has no (attribute_)connection, don't position it. - return undefined; + return { nodes, edges }; } diff --git a/libs/shared/lib/querybuilder/panel/index.ts b/libs/shared/lib/querybuilder/panel/index.ts index e9d6c870c..561b9d241 100644 --- a/libs/shared/lib/querybuilder/panel/index.ts +++ b/libs/shared/lib/querybuilder/panel/index.ts @@ -1 +1 @@ -export * from './querybuilder'; \ No newline at end of file +export * from './querybuilder'; diff --git a/libs/shared/lib/querybuilder/panel/querybuilder.module.scss b/libs/shared/lib/querybuilder/panel/querybuilder.module.scss index 40d1908c9..aa0cb3737 100644 --- a/libs/shared/lib/querybuilder/panel/querybuilder.module.scss +++ b/libs/shared/lib/querybuilder/panel/querybuilder.module.scss @@ -1,31 +1,39 @@ -.reactflow { - width: 100%; - // height: 500px; - - // :global(.react-flow__edges) { - // z-index: 4 !important; - // } -} - -//controls -.controls { - left: auto !important; - bottom: auto !important; - top: 10px; - right: 20px; - width: auto !important; -} -.buttons { - left: auto !important; - bottom: auto !important; - top: 10px; - right: 20px; - & svg { - transform: scale(1.4); - } -} - -.menuText { - font-size: small; - font-family: Poppins, sans-serif; -} +.reactflow { + width: 100%; + // height: 500px; + + // :global(.react-flow__edges) { + // z-index: 4 !important; + // } +} + +//controls +.controls { + left: auto !important; + bottom: auto !important; + top: 10px; + right: 20px; + width: auto !important; +} +.buttons { + left: auto !important; + bottom: auto !important; + top: 10px; + right: 20px; + & svg { + transform: scale(1.4); + } +} + +.dndnode { +} + +.menuText { + font-size: small; + font-family: Poppins, sans-serif; +} + +.full { + height: 100%; + width: 100%; +} diff --git a/libs/shared/lib/querybuilder/panel/querybuilder.module.scss.d.ts b/libs/shared/lib/querybuilder/panel/querybuilder.module.scss.d.ts index 8d93e9952..719453220 100644 --- a/libs/shared/lib/querybuilder/panel/querybuilder.module.scss.d.ts +++ b/libs/shared/lib/querybuilder/panel/querybuilder.module.scss.d.ts @@ -3,5 +3,6 @@ declare const classNames: { readonly controls: 'controls'; readonly buttons: 'buttons'; readonly menuText: 'menuText'; + readonly full: 'full'; }; export = classNames; diff --git a/libs/shared/lib/querybuilder/panel/querybuilder.stories.tsx b/libs/shared/lib/querybuilder/panel/querybuilder.stories.tsx deleted file mode 100644 index 0d45f3ba5..000000000 --- a/libs/shared/lib/querybuilder/panel/querybuilder.stories.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import React from 'react'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - setQuerybuilderNodes, - store, -} from '@graphpolaris/shared/lib/data-access/store'; -import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { configureStore } from '@reduxjs/toolkit'; -import { Meta } from '@storybook/react'; -import { Provider } from 'react-redux'; -import QueryBuilder from './querybuilder'; -import { Handles, QueryMultiGraphology } from '../model'; - -const Component: Meta<typeof QueryBuilder> = { - component: QueryBuilder, - title: 'QueryBuilder/Panel', - decorators: [ - (story) => ( - <Provider store={store}> - <GraphPolarisThemeProvider> - <div - style={{ - width: '100%', - height: '95vh', - }} - > - {story()} - </div> - </GraphPolarisThemeProvider> - </Provider> - ), - ], -}; - -const graph = new QueryMultiGraphology(); -graph.addPill2Graphology( - { type: 'entity', x: 100, y: 100, name: 'Entity Pill', fadeIn: false }, - '0' -); -graph.addPill2Graphology( - { type: 'entity', x: 200, y: 200, name: 'Entity Pill 2', fadeIn: false }, - '10' -); -// graph.addNode('0', { type: 'entity', x: 100, y: 100, name: 'Entity Pill' }); -graph.addPill2Graphology( - { - type: 'relation', - x: 140, - y: 140, - name: 'Relation Pill', - depth: { min: 0, max: 1 }, - fadeIn: false, - }, - '1' -); -// addPill2Graphology( -// '2', -// { -// type: 'attribute', -// x: 170, -// y: 160, -// name: 'Attr string', -// dataType: 'string', -// matchType: 'EQ', -// value: 'mark', -// depth: { min: 0, max: 1 }, -// fadeIn: false, -// }, -// graph -// ); -// addPill2Graphology( -// '3', -// { -// type: 'attribute', -// x: 170, -// y: 170, -// name: 'Attr number', -// dataType: 'float', -// matchType: 'EQ', -// depth: { min: 0, max: 1 }, -// fadeIn: false, -// }, -// graph -// ); -// addPill2Graphology( -// '4', -// { -// type: 'attribute', -// x: 130, -// y: 120, -// name: 'Attr bool', -// dataType: 'bool', -// matchType: 'EQ', -// value: 'true', -// depth: { min: 0, max: 1 }, -// fadeIn: false, -// }, -// graph -// ); -graph.addEdge('0', '1', { - type: 'connection', - sourceHandle: Handles.ToRelation, - targetHandle: Handles.RelationLeft, -}); -graph.addEdge('10', '1', { - type: 'connection', - sourceHandle: Handles.ToRelation, - targetHandle: Handles.RelationRight, -}); -// console.log(graph.getNodeAttributes('2')); -// graph.addEdge('2', '1', { type: 'attribute_connection' }); -// graph.addEdge('3', '1', { type: 'attribute_connection' }); -// graph.addEdge('4', '0', { type: 'attribute_connection' }); -// graph.addEdge('0', '1', { -// type: 'entity_relation', -// targetHandle: handles.relation.fromEntity, -// }); -// graph.addEdge('1', '0', { -// type: 'entity_relation', -// sourceHandle: handles.relation.entity, -// }); - -export const Simple = { - args: {}, - play: async () => { - store.dispatch(setQuerybuilderNodes(graph.export())); - }, -}; - -export default Component; diff --git a/libs/shared/lib/querybuilder/panel/querybuilder.tsx b/libs/shared/lib/querybuilder/panel/querybuilder.tsx index bcfcf7408..73bc730e7 100644 --- a/libs/shared/lib/querybuilder/panel/querybuilder.tsx +++ b/libs/shared/lib/querybuilder/panel/querybuilder.tsx @@ -4,6 +4,8 @@ import { useConfig, useQuerybuilderGraph, useQuerybuilderGraphology, + useSchemaGraph, + useSchemaGraphology, } from '@graphpolaris/shared/lib/data-access/store'; import ReactFlow, { ReactFlowProvider, @@ -15,44 +17,70 @@ import ReactFlow, { ControlButton, NodeChange, ConnectionMode, + NodeMouseHandler, + OnConnectStartParams, + useReactFlow, + Edge, + Connection, + HandleType, + XYPosition, + Position, + OnEdgesChange, + NodePositionChange, } from 'reactflow'; import styles from './querybuilder.module.scss'; +import CachedIcon from '@mui/icons-material/Cached'; -import React, { - ReactComponentElement, - useMemo, - useRef, - useEffect, -} from 'react'; -import { - AttributePill, - ConnectionDragLine, - ConnectionLine, - EntityFlowElement, - RelationPill, -} from '../pills'; -import { - dragPillStarted, - dragPillStopped, - movePillTo, -} from '../pills/dragging/dragPill'; -import { - Settings as SettingsIcon, - Delete as DeleteIcon, - ImportExport as ExportIcon, -} from '@mui/icons-material'; +import React, { ReactComponentElement, useMemo, useRef, useEffect, useCallback, useState, DragEventHandler } from 'react'; +import { AttributePill, ConnectionDragLine, ConnectionLine, EntityFlowElement, RelationPill } from '../pills'; +import { dragPillStarted, dragPillStopped, movePillTo } from '../pills/dragging/dragPill'; +import { Settings as SettingsIcon, Delete as DeleteIcon, ImportExport as ExportIcon } from '@mui/icons-material'; import { clearQB } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; -import { - RelationPosToFromEntityPos, - RelationPosToToEntityPos, -} from '@graphpolaris/shared/lib/querybuilder/model/graphology/utils'; +import { RelationPosToFromEntityPos, RelationPosToToEntityPos } from '@graphpolaris/shared/lib/querybuilder/model/graphology/utils'; import { useDispatch } from 'react-redux'; -import { Card, CardContent, Typography } from '@mui/material'; -import { Handles, QueryElementTypes, createReactFlowElements } from '../model'; +import { + Box, + Card, + CardContent, + Dialog, + DialogTitle, + Grid, + List, + ListItem, + ListItemButton, + ListItemText, + Paper, + PaperProps, + Tab, + Tabs, + Typography, +} from '@mui/material'; +import { + AllLogicDescriptions, + AllLogicMap, + Handles, + MathFilters, + StringFilters, + MathFunctions, + NodeAttribute, + QueryElementTypes, + QueryGraphNodes, + StringFunctions, + createReactFlowElements, + toHandleData, + QueryGraphEdgeHandle, +} from '../model'; +import Draggable from 'react-draggable'; +import { GeneralDescription, InputNodeType } from '../model/logic/general'; +import LogicPill from '../pills/customFlowPills/logicpill/logicpill'; +import { current } from '@reduxjs/toolkit'; +import { SchemaAttributeTypes } from '../../schema'; +import { MathAggregations } from '../model/logic/mathAggregations'; const nodeTypes = { entity: EntityFlowElement, relation: RelationPill, + logic: LogicPill, attribute: AttributePill, }; const edgeTypes = { @@ -63,54 +91,54 @@ const edgeTypes = { /** * This is the main querybuilder component. It is responsible for holding all pills and fire off the visual part of the querybuilder panel logic */ -export const QueryBuilder: React.FC = () => { +export const QueryBuilderInner = (props: QueryBuilderProps) => { + const [openPopup, setOpenPopup] = useState(false); + const reactFlowWrapper = useRef<HTMLDivElement>(null); + + const schema = useSchemaGraphology(); const graphologyGraph = useQuerybuilderGraphology(); const graph = useQuerybuilderGraph(); const config = useConfig(); const dispatch = useDispatch(); const isDraggingPill = useRef(false); - // console.log('inputnodes', nodes); - const reactFlowInstanceRef = useRef<ReactFlowInstance>(); - const divRef = useRef<HTMLDivElement>(null); + const connectingNodeId = useRef<{ + params: OnConnectStartParams; + position: XYPosition; + node: QueryGraphNodes; + attribute: NodeAttribute; + } | null>(null); + const reactFlow = useReactFlow(); + const isEdgeUpdating = useRef(false); + const isOnConnect = useRef(false); const onInit = (reactFlowInstance: ReactFlowInstance) => { - reactFlowInstanceRef.current = reactFlowInstance; - setTimeout(() => reactFlowInstance.fitView(), 0); + setTimeout(() => reactFlow.fitView(), 0); }; - const elements = useMemo( - () => createReactFlowElements(graphologyGraph), - [graphologyGraph] - ); + const elements = useMemo(() => createReactFlowElements(graphologyGraph), [graph]); /** * Called when a node is dragged in querybuilder to allow for movement * @param event * @param node */ - const onNodeDrag = ( - event: React.MouseEvent<Element, MouseEvent>, - node: Node<any> - ) => { - // Get the node in the elements list to get the previous location - const pNode = elements.nodes.find((e) => e?.id === node?.id); - if (!(pNode && isNode(pNode))) return; - // This is then used to calculate the delta position - const dx = node.position.x - pNode.position.x; - const dy = node.position.y - pNode.position.y; - - // console.log(node, pNode); - // Check if we started dragging, if so, call the drag started usecase - if (!isDraggingPill.current) { - dragPillStarted(node.id, graphologyGraph); - isDraggingPill.current = true; - } - - // Call the drag usecase - movePillTo(node.id, graphologyGraph, dx, dy, node.position); - - // Dispatch the new graphology object, so reactflow will get rerendered - dispatch(setQuerybuilderNodes(graphologyGraph.export())); + const onNodeDrag = (event: React.MouseEvent<Element, MouseEvent>, node: Node<any>, nodes: Node[]) => { + // console.log(nodes); + // // Get the node in the elements list to get the previous location + // const pNode = elements.nodes.find((e) => e?.id === node?.id); + // if (!(pNode && isNode(pNode))) return; + // // This is then used to calculate the delta position + // const dx = node.position.x - pNode.position.x; + // const dy = node.position.y - pNode.position.y; + // // Check if we started dragging, if so, call the drag started usecase + // if (!isDraggingPill.current) { + // dragPillStarted(node.id, graphologyGraph); + // isDraggingPill.current = true; + // } + // // Call the drag usecase + // movePillTo(node.id, graphologyGraph, dx, dy, node.position); + // // Dispatch the new graphology object, so reactflow will get rerendered + // dispatch(setQuerybuilderNodes(graphologyGraph.export())); }; /** @@ -118,10 +146,7 @@ export const QueryBuilder: React.FC = () => { * @param event * @param node **/ - const onNodeDragStop = ( - event: React.MouseEvent<Element, MouseEvent>, - node: Node<any> - ) => { + const onNodeDragStop = (event: React.MouseEvent<Element, MouseEvent>, node: Node<any>) => { // isDraggingPill.current = false; // // // Call the drag pill stopped usecase @@ -161,6 +186,23 @@ export const QueryBuilder: React.FC = () => { if (n.type !== 'position') { // updated = true; // graphologyGraph.updateAttributes(n.id, n.data); + } else { + const node = n as NodePositionChange; + // Get the node in the elements list to get the previous location + const pNode = elements.nodes.find((e) => e?.id === node?.id); + if (!(pNode && isNode(pNode)) || !node?.position) return; + // This is then used to calculate the delta position + const dx = node.position.x - pNode.position.x; + const dy = node.position.y - pNode.position.y; + // Check if we started dragging, if so, call the drag started usecase + if (!isDraggingPill.current) { + dragPillStarted(node.id, graphologyGraph); + isDraggingPill.current = true; + } + // Call the drag usecase + movePillTo(node.id, graphologyGraph, dx, dy, node.position); + // Dispatch the new graphology object, so reactflow will get rerendered + dispatch(setQuerybuilderNodes(graphologyGraph.export())); } }); } @@ -180,62 +222,58 @@ export const QueryBuilder: React.FC = () => { // The dropped element should be a valid reactflow element const data: string = event.dataTransfer.getData('application/reactflow'); - if (data.length == 0 || !reactFlowInstanceRef?.current) return; + if (data.length == 0 || !reactFlow) return; const dragData = JSON.parse(data); - const reactFlow = divRef.current as HTMLDivElement; - const reactFlowBounds = reactFlow.getBoundingClientRect(); - const position = reactFlowInstanceRef.current.project({ + console.log(reactFlowWrapper); + + const bounds = reactFlowWrapper?.current?.getBoundingClientRect() || { x: 0, y: 0 }; + const position = reactFlow.project({ //TODO: this position should be centre of entity, rather than topleft - x: event.clientX - reactFlowBounds.left, - y: event.clientY - reactFlowBounds.top, + x: event.clientX - bounds.x, + y: event.clientY - bounds.y, }); switch (dragData.type) { case QueryElementTypes.Entity: - graphologyGraph.addPill2Graphology({ - type: QueryElementTypes.Entity, - x: position.x, - y: position.y, - name: dragData.name, - fadeIn: dragData.fadeIn, - }); + // console.log('entity drop', dragData, schema.getNodeAttribute(dragData.name, 'attributes')); + graphologyGraph.addPill2Graphology( + { + type: QueryElementTypes.Entity, + x: position.x, + y: position.y, + name: dragData.name, + }, + schema.getNodeAttribute(dragData.name, 'attributes') + ); + dispatch(setQuerybuilderNodes(graphologyGraph.export())); break; // Creates a relation element and will also create the 2 related entities together with the connections case QueryElementTypes.Relation: - const relationId = graphologyGraph.addPill2Graphology({ - type: QueryElementTypes.Relation, - x: position.x, - y: position.y, - depth: { min: 0, max: 1 }, - // name: dragData.name, - name: dragData.collection, // TODO leave collection or use name? - fadeIn: dragData.fadeIn, - collection: dragData.collection, - }); - const leftEntityId = graphologyGraph.addPill2Graphology({ - type: QueryElementTypes.Entity, - ...RelationPosToFromEntityPos(position), - name: dragData.from, - fadeIn: true, - }); - const rightEntityId = graphologyGraph.addPill2Graphology({ - type: QueryElementTypes.Entity, - ...RelationPosToToEntityPos(position), - name: dragData.to, - fadeIn: true, - }); + console.log('relation drop', dragData, schema.export()); - graphologyGraph.addEdge(leftEntityId, relationId, { - type: 'connection', - sourceHandle: Handles.ToRelation, - targetHandle: Handles.RelationLeft, - }); - graphologyGraph.addEdge(rightEntityId, relationId, { - type: 'connection', - sourceHandle: Handles.ToRelation, - targetHandle: Handles.RelationRight, - }); + const relation = graphologyGraph.addPill2Graphology( + { + type: QueryElementTypes.Relation, + x: position.x, + y: position.y, + depth: { min: 0, max: 1 }, + name: dragData.collection, + collection: dragData.collection, + }, + schema.getEdgeAttribute(dragData.label, 'attributes') + ); + // const leftEntity = graphologyGraph.addPill2Graphology( + // { type: QueryElementTypes.Entity, ...RelationPosToFromEntityPos(position), name: dragData.from }, + // schema.getNodeAttribute(dragData.from, 'attributes') + // ); + // const rightEntity = graphologyGraph.addPill2Graphology( + // { type: QueryElementTypes.Entity, ...RelationPosToToEntityPos(position), name: dragData.to }, + // schema.getNodeAttribute(dragData.to, 'attributes') + // ); + + // graphologyGraph.addEdge2Graphology(leftEntity, relation); + // graphologyGraph.addEdge2Graphology(relation, rightEntity); if (config.autoSendQueries) { // sendQuery(); @@ -249,102 +287,512 @@ export const QueryBuilder: React.FC = () => { // QueryElementTypes.Attribute, // position, // dragData.name, - // dragData.fadeIn, // dragData.datatype // ); // break; + default: + const logic = AllLogicMap[dragData.value.key]; + const firstLeftLogicInput = logic.inputs?.[0]; + if (!firstLeftLogicInput) return; + + // logicAttributes[0].handles = [connectingNodeId.current.handleId]; + const logicNode = graphologyGraph.addLogicPill2Graphology({ + name: dragData.value.name, + type: QueryElementTypes.Logic, + x: position.x, + y: position.y, + logic: logic, + }); + + dispatch(setQuerybuilderNodes(graphologyGraph.export())); } }; + const onNodeMouseEnter = (event: React.MouseEvent, node: Node) => { + console.log(node); + console.log(schema.getNodeAttribute(node.type, 'attributes')); + }; + + const onNodeMouseLeave = (event: React.MouseEvent, node: Node) => { + console.log(node); + }; + + const onConnect = useCallback( + (connection: Connection) => { + if (!isEdgeUpdating.current) { + isOnConnect.current = true; + if (!connection.sourceHandle || !connection.targetHandle) throw new Error('Connection has no source or target'); + console.log('onConnect', connection); + + if (!graphologyGraph.hasEdge(connection.source, connection.target)) { + graphologyGraph.addEdge(connection.source, connection.target, { + type: 'connection', + sourceHandleData: toHandleData(connection.sourceHandle), + targetHandleData: toHandleData(connection.targetHandle), + }); + dispatch(setQuerybuilderNodes(graphologyGraph.export())); + } + } + }, + [graph] + ); + + const onConnectStart = useCallback( + (event: React.MouseEvent | React.TouchEvent, params: OnConnectStartParams) => { + // console.log('onConnectStart', params); + if (!params?.handleId) return; + + let node = graphologyGraph.getNodeAttributes(params.nodeId); + const handleData = toHandleData(params.handleId); + // console.log(attributeName, attributeType, node.attributes?.filter((a) => a.name === attributeName)?.[0]); + + connectingNodeId.current = { + params, + node, + position: { x: 0, y: 0 }, + attribute: { handleData: handleData }, + }; + }, + [graph] + ); + + const onConnectEnd = useCallback( + (event: any) => { + const targetIsPane = event.target.classList.contains('react-flow__pane'); + if (isOnConnect.current) { + isOnConnect.current = false; + return; + } + + if (targetIsPane && !isEdgeUpdating.current) { + let clientX: number = 0; + let clientY: number = 0; + + if ('touches' in event) clientX = event?.touches?.[0]?.clientX; + else if ('clientX' in event) clientX = event?.clientX; + if ('touches' in event) clientY = event?.touches?.[0]?.clientY; + else if ('clientY' in event) clientY = event?.clientY; + + if (reactFlowWrapper.current) { + const { top, left } = reactFlowWrapper.current.getBoundingClientRect(); + clientX -= left; + clientY -= top; + } + const position = reactFlow.project({ x: clientX, y: clientY }); + if (connectingNodeId?.current) connectingNodeId.current.position = position; + + setOpenPopup(true); + } + }, + [reactFlow.project] + ); + + const onNewNodeFromPopup = (value: AllLogicDescriptions, type: InputNodeType) => { + if (connectingNodeId.current === null || connectingNodeId.current?.params?.handleId == null) return; + const params = connectingNodeId.current.params; + const position = connectingNodeId.current.position; + + // console.log('onNewNodeFromPopup', value, type, params, position); + + const logic = AllLogicMap[value.key]; + const firstLeftLogicInput = logic.inputs?.[0]; + if (!firstLeftLogicInput) return; + + // logicAttributes[0].handles = [connectingNodeId.current.handleId]; + const logicNode = graphologyGraph.addLogicPill2Graphology({ + name: value.name, + type: QueryElementTypes.Logic, + x: position.x, + y: position.y, + logic: logic, + }); + + if (!logicNode?.id) throw new Error('Logic node has no id'); + if (!logicNode?.name) throw new Error('Logic node has no name'); + if (!params.handleId) throw new Error('Connection has no source or target'); + + const sourceHandleData = toHandleData(params.handleId); + graphologyGraph.addEdge2Graphology( + graphologyGraph.getNodeAttributes(params.nodeId), + graphologyGraph.getNodeAttributes(logicNode.id), + { type: 'connection' }, + { sourceHandleName: sourceHandleData.attributeName, targetHandleName: firstLeftLogicInput.name } + ); + + dispatch(setQuerybuilderNodes(graphologyGraph.export())); + setOpenPopup(false); + }; + + const onEdgeUpdateStart = useCallback(() => { + isEdgeUpdating.current = true; + }, []); + + const onEdgeUpdate = useCallback( + (oldEdge: Edge, newConnection: Connection) => { + if (isEdgeUpdating.current) { + isEdgeUpdating.current = false; + + if (graphologyGraph.hasEdge(oldEdge.id)) { + graphologyGraph.dropEdge(oldEdge.id); + } + + if (!newConnection.sourceHandle || !newConnection.targetHandle) throw new Error('Connection has no source or target'); + graphologyGraph.addEdge(newConnection.source, newConnection.target, { + type: 'connection', + sourceHandleData: toHandleData(newConnection.sourceHandle), + targetHandleData: toHandleData(newConnection.targetHandle), + }); + dispatch(setQuerybuilderNodes(graphologyGraph.export())); + } + }, + [graph] + ); + + const onEdgesChange = (params: OnEdgesChange) => { + console.log('edges change', params); + }; + + const onEdgeUpdateEnd = useCallback( + (event: MouseEvent | TouchEvent, edge: Edge, handleType: HandleType) => { + if (isEdgeUpdating.current) { + if (graphologyGraph.hasEdge(edge.id)) { + graphologyGraph.dropEdge(edge.id); + } + dispatch(setQuerybuilderNodes(graphologyGraph.export())); + // setEdges((eds) => eds.filter((e) => e.id !== edge.id)); + } + isEdgeUpdating.current = false; + }, + [graph] + ); + + const onNodeContextMenu = (event: React.MouseEvent, node: Node) => { + event.preventDefault(); + // console.log('context menu', node); + graphologyGraph.dropNode(node.id); + dispatch(setQuerybuilderNodes(graphologyGraph.export())); + }; + + return ( + <div ref={reactFlowWrapper} className={styles.full}> + <PopupMenu + open={openPopup} + type={connectingNodeId.current?.attribute.handleData.attributeType || 'int'} + onClose={() => { + setOpenPopup(false); + }} + onClick={onNewNodeFromPopup} + /> + <ReactFlow + edges={elements.edges} + nodes={elements.nodes} + snapGrid={[10, 10]} + snapToGrid + nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + connectionLineComponent={ConnectionDragLine} + // connectionMode={ConnectionMode.Loose} + onInit={onInit} + onNodesChange={onNodesChange} + onNodeDrag={onNodeDrag} + onNodeDragStop={onNodeDragStop} + onDragOver={onDragOver} + onConnect={onConnect} + onConnectStart={onConnectStart} + onConnectEnd={onConnectEnd} + // onNodeMouseEnter={onNodeMouseEnter} + // onNodeMouseLeave={onNodeMouseLeave} + onEdgeUpdate={onEdgeUpdate} + onEdgeUpdateStart={onEdgeUpdateStart} + onEdgeUpdateEnd={onEdgeUpdateEnd} + onDrop={onDrop} + // onContextMenu={onContextMenu} + onNodeContextMenu={onNodeContextMenu} + // onNodesDelete={onNodesDelete} + // onNodesChange={onNodesChange} + deleteKeyCode="Backspace" + className={styles.reactflow} + attributionPosition="top-right" + > + <Background gap={10} size={0.7} /> + {elements && elements.edges.length == 0 && elements.nodes.length == 0 && ( + <Card variant="outlined" sx={{ minWidth: 275, marginTop: 3, marginRight: 10 }}> + <CardContent> + <Typography sx={{ fontSize: 20 }} color="text.secondary"> + Drag some node from the Schema panel + </Typography> + </CardContent> + </Card> + )} + + <Controls showZoom={false} showInteractive={false} className={styles.controls}> + <ControlButton className={styles.buttons} title={'Remove all elements'} onClick={() => clearAllNodes()}> + <DeleteIcon /> + </ControlButton> + <ControlButton + className={styles.buttons} + title={'Export querybuilder'} + onClick={(event) => { + event.stopPropagation(); + // this.setState({ + // ...this.state, + // exportMenuAnchor: event.currentTarget, + // }); + }} + > + <ExportIcon /> + </ControlButton> + <ControlButton + className={styles.buttons} + title={'Other settings'} + onClick={(event) => { + event.stopPropagation(); + // this.setState({ + // ...this.state, + // settingsMenuAnchor: event.currentTarget, + // }); + }} + > + <SettingsIcon /> + </ControlButton> + <ControlButton + className={styles.buttons} + title={'Run Query'} + onClick={(event) => { + event.stopPropagation(); + if (props.onRunQuery) props.onRunQuery(); + }} + > + <CachedIcon /> + </ControlButton> + </Controls> + </ReactFlow> + </div> + ); +}; + +// const math_functions = ['AVG', 'COUNT', 'MAX', 'MIN', 'SUM', 'ROUND', 'CEIL', 'FLOOR', '+', '-', '*', '/', '%', 'CUSTOM']; +// const string_functions = ['CONCAT', 'LOWER', 'UPPER', 'SUBSTRING', 'TRIM']; +// const math_filters = ['==', '!=', '>', '<', '>=', '<=']; +// const string_filters = ['==', '!=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN']; + +function PaperComponent(props: PaperProps) { + return ( + <Draggable handle="#draggable-dialog-title" cancel={'[class*="MuiDialogContent-root"]'}> + <Paper {...props} /> + </Draggable> + ); +} + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +function CustomTabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + <div + role="tabpanel" + hidden={value !== index} + id={`simple-tabpanel-${index}`} + aria-labelledby={`simple-tab-${index}`} + {...other} + style={{ maxHeight: '85%', overflow: 'auto' }} + > + {value === index && ( + <Box> + <Typography>{children}</Typography> + </Box> + )} + </div> + ); +} + +function PopupMenu(props: { + open: boolean; + type: InputNodeType; + onClose: () => void; + onClick: (value: AllLogicDescriptions, type: InputNodeType) => void; +}) { + const [value, setValue] = React.useState(props.type === 'string' ? 1 : 0); + + const handleChange = (event: React.SyntheticEvent, newValue: number) => { + setValue(newValue); + }; + + const handleClose = () => { + props.onClose(); + }; + + const handleListItemClick = (value: AllLogicDescriptions, type: InputNodeType) => { + props.onClick(value, type); + }; + + const generateList = (list: Record<string, AllLogicDescriptions>, type: InputNodeType) => ( + <List sx={{ pt: 0 }}> + {Object.keys(list).map((f, i) => ( + <ListItemButton onClick={() => handleListItemClick(list[f], type)} key={f + type}> + {/* <ListItemAvatar> + <Avatar sx={{ bgcolor: blue[100], color: blue[600] }}> + <PersonIcon /> + </Avatar> + </ListItemAvatar> */} + <ListItemText primary={list[f].name} secondary={list[f].description} /> + </ListItemButton> + ))} + </List> + ); + + function a11yProps(index: number) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, + }; + } + + return ( + <Dialog onClose={handleClose} open={props.open} PaperComponent={PaperComponent}> + <DialogTitle>Add New Node</DialogTitle> + <Box> + <Box sx={{ borderBottom: 1, borderColor: 'divider' }}> + <Tabs value={value} onChange={handleChange} aria-label="basic tabs example"> + <Tab label="Aggregations" disabled={props.type === 'string'} {...a11yProps(0)} /> + <Tab label="Operations" {...a11yProps(1)} /> + <Tab label="Filters" {...a11yProps(2)} /> + </Tabs> + </Box> + <CustomTabPanel value={value} index={0}> + {props.type === 'float' && generateList(MathAggregations, props.type)} + {props.type === 'int' && generateList(MathAggregations, props.type)} + {/* {props.type === 'string' && generateList(MathAggregations, props.type)} */} + </CustomTabPanel> + <CustomTabPanel value={value} index={1}> + {props.type === 'float' && generateList(MathFunctions, props.type)} + {props.type === 'int' && generateList(MathFunctions, props.type)} + {props.type === 'string' && generateList(StringFunctions, props.type)} + </CustomTabPanel> + <CustomTabPanel value={value} index={2}> + {props.type === 'float' && generateList(MathFilters, props.type)} + {props.type === 'int' && generateList(MathFilters, props.type)} + {props.type === 'string' && generateList(StringFilters, props.type)} + </CustomTabPanel> + </Box> + </Dialog> + ); +} + +export const QueryBuilderPills = () => { + const onDragStart = (event: React.DragEvent, value: AllLogicDescriptions, nodeType: InputNodeType) => { + console.log('drag start', nodeType); + + event.dataTransfer.setData('application/reactflow', JSON.stringify({ value, nodeType })); + event.dataTransfer.effectAllowed = 'move'; + }; + + const [value, setValue] = React.useState(0); + + const handleChange = (event: React.SyntheticEvent, newValue: number) => { + setValue(newValue); + }; + + function a11yProps(index: number) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, + }; + } + + const handleListItemClick = (value: AllLogicDescriptions, type: InputNodeType) => {}; + + // const generateList = (list: Record<string, AllLogicDescriptions>, type: InputNodeType) => ( + // <List sx={{ pt: 0 }}> + // {Object.keys(list).map((f, i) => ( + // <ListItemButton onClick={() => handleListItemClick(list[f], type)} key={f + type}> + // <ListItemText primary={list[f].name} secondary={list[f].description} /> + // </ListItemButton> + // ))} + // </List> + // ); + const generateList = (list: Record<string, AllLogicDescriptions>, type: InputNodeType) => ( + <List> + {Object.keys(list).map((f, i) => ( + <ListItem key={JSON.stringify(list[f]) + type + i}> + <ListItemText + draggable + primary={list[f].name} + secondary={list[f].description} + onDragStart={(event) => onDragStart(event, list[f], type)} + key={f + type} + /> + </ListItem> + ))} + </List> + ); + + return ( + <aside className=""> + <Box> + <Tabs value={value} onChange={handleChange} aria-label="basic tabs example"> + <Tab label="Aggregations" {...a11yProps(0)} /> + <Tab label="Operations" {...a11yProps(1)} /> + <Tab label="Filters" {...a11yProps(2)} /> + </Tabs> + </Box> + <CustomTabPanel value={value} index={0}> + {generateList(MathAggregations, 'float')} + </CustomTabPanel> + <CustomTabPanel value={value} index={1}> + {generateList(MathFunctions, 'float')} + {generateList(StringFunctions, 'string')} + </CustomTabPanel> + <CustomTabPanel value={value} index={2}> + {generateList(MathFilters, 'float')} + {generateList(StringFilters, 'string')} + </CustomTabPanel> + </aside> + ); +}; + +export type QueryBuilderProps = { + onRunQuery?: () => void; +}; + +export const QueryBuilder = (props: QueryBuilderProps) => { return ( <div style={{ width: '100%', - height: '100%', + height: '22rem', + display: 'flex', + gap: '1rem', }} - ref={divRef} > + <QueryBuilderPills /> <ReactFlowProvider> - <ReactFlow - edges={elements.edges} - nodes={elements.nodes} - snapGrid={[10, 10]} - snapToGrid - nodeTypes={nodeTypes} - edgeTypes={edgeTypes} - connectionLineComponent={ConnectionDragLine} - // connectionMode={ConnectionMode.Loose} - onInit={onInit} - onNodeDrag={onNodeDrag} - onNodeDragStop={onNodeDragStop} - onDragOver={onDragOver} - // onConnect={this.queryBuilderViewModel.onConnect} - onDrop={onDrop} - onNodesDelete={onNodesDelete} - onNodesChange={onNodesChange} - deleteKeyCode="Backspace" - className={styles.reactflow} - attributionPosition="top-right" - > - <Background gap={10} size={0.7} /> - {elements && - elements.edges.length == 0 && - elements.nodes.length == 0 && ( - <Card - variant="outlined" - sx={{ minWidth: 275, marginTop: 3, marginRight: 10 }} - > - <CardContent> - <Typography sx={{ fontSize: 20 }} color="text.secondary"> - Drag some node from the Schema panel - </Typography> - </CardContent> - </Card> - )} - - <Controls - showZoom={false} - showInteractive={false} - className={styles.controls} - > - <ControlButton - className={styles.buttons} - title={'Remove all elements'} - onClick={() => clearAllNodes()} - > - <DeleteIcon /> - </ControlButton> - <ControlButton - className={styles.buttons} - title={'Export querybuilder'} - onClick={(event) => { - event.stopPropagation(); - // this.setState({ - // ...this.state, - // exportMenuAnchor: event.currentTarget, - // }); - }} - > - <ExportIcon /> - </ControlButton> - <ControlButton - className={styles.buttons} - title={'Other settings'} - onClick={(event) => { - event.stopPropagation(); - // this.setState({ - // ...this.state, - // settingsMenuAnchor: event.currentTarget, - // }); - }} - > - <SettingsIcon /> - </ControlButton> - </Controls> - </ReactFlow> + <QueryBuilderInner {...props} /> </ReactFlowProvider> </div> ); }; +// export const QueryBuilder = () => { +// const graphologyGraph = useQuerybuilderGraphology(); +// const schema = useSchemaGraphology(); +// const graph = useQuerybuilderGraph(); +// const config = useConfig(); +// const dispatch = useDispatch(); + +// return ( +// <QueryBuilderRaw +// graphologyGraph={graphologyGraph} +// schema={schema} +// graph={graph} +// config={config} +// /> +// ); +// }; + export default QueryBuilder; diff --git a/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx b/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx index 0bf09cee1..f0ee5ed0d 100644 --- a/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx +++ b/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx @@ -1,24 +1,12 @@ import React from 'react'; import { Meta } from '@storybook/react'; import { Provider } from 'react-redux'; -import { - colorPaletteConfigSlice, - graphQueryResultSlice, - querybuilderSlice, - schemaSlice, - setQuerybuilderNodes, - setSchema, - store, -} from '@graphpolaris/shared/lib/data-access/store'; +import { setQuerybuilderNodes, setSchema, store } from '@graphpolaris/shared/lib/data-access/store'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils'; import { Schema } from '@graphpolaris/shared/lib/schema/panel'; import { movieSchemaRaw } from '@graphpolaris/shared/lib/mock-data'; import { QueryBuilder } from '@graphpolaris/shared/lib/querybuilder'; -import { configureStore } from '@reduxjs/toolkit'; -import { configSlice } from '@graphpolaris/shared/lib/data-access/store/configSlice'; -import { QueryGraph } from '@graphpolaris/shared/lib/querybuilder/model/graphology/model'; -import { MultiGraph } from 'graphology'; import { QueryMultiGraphology } from '@graphpolaris/shared/lib/querybuilder/model/graphology/utils'; const SchemaAndQueryBuilder = () => { diff --git a/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple-disconnected.stories.tsx b/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple-disconnected.stories.tsx new file mode 100644 index 000000000..6d8c661f3 --- /dev/null +++ b/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple-disconnected.stories.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { + colorPaletteConfigSlice, + querybuilderSlice, + setQuerybuilderNodes, + setSchema, + store, +} from '@graphpolaris/shared/lib/data-access/store'; +import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; +import { configureStore } from '@reduxjs/toolkit'; +import { Meta } from '@storybook/react'; +import { Provider } from 'react-redux'; +import QueryBuilderInner from '../querybuilder'; +import { Handles, NodeAttribute, QueryElementTypes, QueryMultiGraphology, toHandleId } from '../../model'; +import { SchemaUtils } from '../../../schema/schema-utils'; +import { ReactFlowProvider } from 'reactflow'; + +const Component: Meta<typeof QueryBuilderInner> = { + component: QueryBuilderInner, + title: 'QueryBuilder/Panel/SimpleDisconnected', + decorators: [(story) => <div>{story()}</div>], +}; + +export const SimpleDisconnected = { + args: {}, + decorators: [ + (story: any) => { + const graph = new QueryMultiGraphology(); + const schema = SchemaUtils.schemaBackend2Graphology({ + nodes: [ + { + name: 'entity', + attributes: [ + { name: 'city', type: 'string' }, + { name: 'vip', type: 'bool' }, + { name: 'state', type: 'string' }, + ], + }, + ], + edges: [ + { + name: 'entity:entity', + from: 'entity', + to: 'entity', + collection: 'entity2entity', + attributes: [ + { name: 'arrivalTime', type: 'int' }, + { name: 'departureTime', type: 'int' }, + ], + }, + ], + }); + + store.dispatch(setSchema(schema.export())); + + const entity1 = graph.addPill2Graphology( + { + id: '0', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 1', + }, + schema.getNodeAttribute('entity', 'attributes') + ); + const entity2 = graph.addPill2Graphology( + { + id: '10', + type: QueryElementTypes.Entity, + x: 200, + y: 200, + name: 'Airport 2', + }, + schema.getNodeAttribute('entity', 'attributes') + ); + + // graph.addNode('0', { type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Entity Pill' }); + const relation1 = graph.addPill2Graphology({ + id: '1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + attributes: schema.getEdgeAttribute('entity:entity_entityentity', 'attributes'), + }); + store.dispatch(setQuerybuilderNodes(graph.export())); + + return ( + <Provider store={store}> + <GraphPolarisThemeProvider> + <div + style={{ + width: '100%', + height: '95vh', + }} + > + <ReactFlowProvider>{story()}</ReactFlowProvider> + </div> + </GraphPolarisThemeProvider> + </Provider> + ); + }, + ], +}; + +export default Component; diff --git a/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple.stories.tsx b/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple.stories.tsx new file mode 100644 index 000000000..bd57e342d --- /dev/null +++ b/libs/shared/lib/querybuilder/panel/stories/querybuilder-simple.stories.tsx @@ -0,0 +1,161 @@ +import React from 'react'; +import { + colorPaletteConfigSlice, + querybuilderSlice, + setQuerybuilderNodes, + setSchema, + store, +} from '@graphpolaris/shared/lib/data-access/store'; +import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; +import { configureStore } from '@reduxjs/toolkit'; +import { Meta } from '@storybook/react'; +import { Provider } from 'react-redux'; +import QueryBuilder from '../querybuilder'; +import { Handles, NodeAttribute, QueryElementTypes, QueryMultiGraphology, toHandleId } from '../../model'; +import { SchemaUtils } from '../../../schema/schema-utils'; + +const Component: Meta<typeof QueryBuilder> = { + component: QueryBuilder, + title: 'QueryBuilder/Panel/Simple', + decorators: [ + (story) => ( + <Provider store={store}> + <GraphPolarisThemeProvider> + <div + style={{ + width: '100%', + height: '95vh', + }} + > + {story()} + </div> + </GraphPolarisThemeProvider> + </Provider> + ), + ], +}; + +export const Simple = { + args: {}, + play: async () => { + const graph = new QueryMultiGraphology(); + const schema = SchemaUtils.schemaBackend2Graphology({ + nodes: [ + { + name: 'entity', + attributes: [ + { name: 'city', type: 'string' }, + { name: 'vip', type: 'bool' }, + { name: 'state', type: 'string' }, + { name: 'customers', type: 'float' }, + ], + }, + ], + edges: [ + { + name: 'entity:entity', + from: 'entity', + to: 'entity', + collection: 'entity2entity', + attributes: [ + { name: 'arrivalTime', type: 'float' }, + { name: 'departureTime', type: 'float' }, + ], + }, + ], + }); + + store.dispatch(setSchema(schema.export())); + + const entity1 = graph.addPill2Graphology( + { + id: '0', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 1', + }, + schema.getNodeAttribute('entity', 'attributes') + ); + const entity2 = graph.addPill2Graphology( + { + id: '10', + type: QueryElementTypes.Entity, + x: 200, + y: 200, + name: 'Airport 2', + }, + schema.getNodeAttribute('entity', 'attributes') + ); + + // graph.addNode('0', { type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Entity Pill' }); + const relation1 = graph.addPill2Graphology({ + id: '1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + attributes: schema.getEdgeAttribute('entity:entity_entityentity', 'attributes'), + }); + // addPill2Graphology( + // '2', + // { + // type: 'attribute', + // x: 170, + // y: 160, + // name: 'Attr string', + // dataType: 'string', + // matchType: 'EQ', + // value: 'mark', + // depth: { min: 0, max: 1 }, + // }, + // graph + // ); + // addPill2Graphology( + // '3', + // { + // type: 'attribute', + // x: 170, + // y: 170, + // name: 'Attr number', + // dataType: 'float', + // matchType: 'EQ', + // depth: { min: 0, max: 1 }, + // }, + // graph + // ); + // addPill2Graphology( + // '4', + // { + // type: 'attribute', + // x: 130, + // y: 120, + // name: 'Attr bool', + // dataType: 'bool', + // matchType: 'EQ', + // value: 'true', + // depth: { min: 0, max: 1 }, + // }, + // graph + // ); + graph.addEdge2Graphology(entity1, relation1); + graph.addEdge2Graphology(relation1, entity2); + // console.log(graph.getNodeAttributes('2')); + // graph.addEdge('2', '1', { type: 'attribute_connection' }); + // graph.addEdge('3', '1', { type: 'attribute_connection' }); + // graph.addEdge('4', '0', { type: 'attribute_connection' }); + // graph.addEdge('0', '1', { + // type: 'entity_relation', + // targetHandle: handles.relation.fromEntity, + // }); + // graph.addEdge('1', '0', { + // type: 'entity_relation', + // sourceHandle: handles.relation.entity, + // }); + store.dispatch(setQuerybuilderNodes(graph.export())); + }, +}; + +export default Component; diff --git a/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-entity.stories.tsx b/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-entity.stories.tsx new file mode 100644 index 000000000..c147c023d --- /dev/null +++ b/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-entity.stories.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { + colorPaletteConfigSlice, + querybuilderSlice, + setQuerybuilderNodes, + setSchema, + store, +} from '@graphpolaris/shared/lib/data-access/store'; +import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; +import { configureStore } from '@reduxjs/toolkit'; +import { Meta } from '@storybook/react'; +import { Provider } from 'react-redux'; +import QueryBuilder from '../querybuilder'; +import { QueryElementTypes, QueryMultiGraphology, toHandleId } from '../../model'; + +const Component: Meta<typeof QueryBuilder> = { + component: QueryBuilder, + title: 'QueryBuilder/Panel/SingleEntity', + decorators: [ + (story) => ( + <Provider store={store}> + <GraphPolarisThemeProvider> + <div + style={{ + width: '100%', + height: '95vh', + }} + > + {story()} + </div> + </GraphPolarisThemeProvider> + </Provider> + ), + ], +}; + +export const SingleEntity = { + args: {}, + play: async () => { + const graph = new QueryMultiGraphology(); + graph.addPill2Graphology({ + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Entity Pill', + }); + store.dispatch(setQuerybuilderNodes(graph.export())); + }, +}; + +export default Component; diff --git a/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-relationship.stories.tsx b/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-relationship.stories.tsx new file mode 100644 index 000000000..db2d6634f --- /dev/null +++ b/libs/shared/lib/querybuilder/panel/stories/querybuilder-single-relationship.stories.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { + colorPaletteConfigSlice, + querybuilderSlice, + setQuerybuilderNodes, + setSchema, + store, +} from '@graphpolaris/shared/lib/data-access/store'; +import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; +import { configureStore } from '@reduxjs/toolkit'; +import { Meta } from '@storybook/react'; +import { Provider } from 'react-redux'; +import QueryBuilder from '../querybuilder'; +import { QueryElementTypes, QueryMultiGraphology, toHandleId } from '../../model'; + +const Component: Meta<typeof QueryBuilder> = { + component: QueryBuilder, + title: 'QueryBuilder/Panel/SingleRelationship', + decorators: [ + (story) => ( + <Provider store={store}> + <GraphPolarisThemeProvider> + <div + style={{ + width: '100%', + height: '95vh', + }} + > + {story()} + </div> + </GraphPolarisThemeProvider> + </Provider> + ), + ], +}; + +export const SingleRelationship = { + args: {}, + play: async () => { + const graph = new QueryMultiGraphology(); + graph.addPill2Graphology({ + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Relation Pill', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + store.dispatch(setQuerybuilderNodes(graph.export())); + }, +}; + +export default Component; diff --git a/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx b/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx index 50625840f..ef15a7b00 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx @@ -1,26 +1,12 @@ import React from 'react'; import { EdgeProps, getSmoothStepPath, Position } from 'reactflow'; -import { handles } from '../../graph/reactflow/pillHandles'; /** * A custom query element edge line component. * @param {EdgeProps} param0 The coordinates for the start and end point, the id and the style. */ // export const EntityRFPill = React.memo(({ data }: { data: any }) => { -export function ConnectionLine({ - id, - sourceX, - sourceY, - targetX, - targetY, - style, - sourceHandleId, - targetHandleId, - source, - target, - sourcePosition, - targetPosition, -}: EdgeProps) { +export function ConnectionLine({ id, sourceX, sourceY, targetX, targetY, style, sourcePosition, targetPosition }: EdgeProps) { //Centering the line // sourceY -= 3; // targetY -= 3; diff --git a/libs/shared/lib/querybuilder/pills/customFlowLines/connectionDrag.tsx b/libs/shared/lib/querybuilder/pills/customFlowLines/connectionDrag.tsx index b43f689d6..f9e33c02c 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowLines/connectionDrag.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowLines/connectionDrag.tsx @@ -5,37 +5,12 @@ import { ConnectionLineComponentProps } from 'reactflow'; * A custom query element to render the line when connecting flow elements. * @param {ConnectionLineComponentProps} param0 Source and target coordinates of the edges. */ -export function ConnectionDragLine({ - fromX, - fromY, - toX, - toY, -}: ConnectionLineComponentProps) { +export function ConnectionDragLine({ fromX, fromY, toX, toY }: ConnectionLineComponentProps) { return ( <g> - <path - fill="none" - stroke="#222" - strokeWidth={2.5} - className="animated" - d={`M${fromX},${fromY}L ${toX},${toY}`} - /> - <circle - cx={fromX} - cy={fromY} - fill="#fff" - r={3} - stroke="#222" - strokeWidth={1.5} - /> - <circle - cx={toX} - cy={toY} - fill="#fff" - r={3} - stroke="#222" - strokeWidth={1.5} - /> + <path fill="none" stroke="#222" strokeWidth={2.5} className="animated" d={`M${fromX},${fromY}L ${toX},${toY}`} /> + <circle cx={fromX} cy={fromY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} /> + <circle cx={toX} cy={toY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} /> </g> ); } diff --git a/libs/shared/lib/querybuilder/pills/customFlowLines/index.ts b/libs/shared/lib/querybuilder/pills/customFlowLines/index.ts index 9a5ca7d28..7c84db092 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowLines/index.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowLines/index.ts @@ -1,2 +1,2 @@ -export * from './connection' -export * from './connectionDrag' \ No newline at end of file +export * from './connection'; +export * from './connectionDrag'; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill-full.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill-full.stories.tsx deleted file mode 100644 index 1bfee71e6..000000000 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill-full.stories.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - setQuerybuilderNodes, -} from '@graphpolaris/shared/lib/data-access/store'; -import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { configureStore } from '@reduxjs/toolkit'; -import { Meta } from '@storybook/react'; -import { Provider } from 'react-redux'; -import { MultiGraph } from 'graphology'; -import { QueryBuilder } from '../../../panel'; -import { QueryGraph } from '../../../model/graphology/model'; -import { circular } from 'graphology-layout'; -import { QueryMultiGraphology } from '@graphpolaris/shared/lib/querybuilder/model/graphology/utils'; - -const Component: Meta<typeof QueryBuilder> = { - component: QueryBuilder, - title: 'Querybuilder/Pills/AttributePill', - decorators: [ - (story) => ( - <Provider store={mockStore}> - <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider> - </Provider> - ), - ], -}; - -// Mock palette store -const mockStore = configureStore({ - reducer: { - colorPaletteConfig: colorPaletteConfigSlice.reducer, - querybuilder: querybuilderSlice.reducer, - }, -}); -const graph = new QueryMultiGraphology(); -graph.addPill2Graphology( - { - type: 'attribute', - x: 170, - y: 160, - name: 'Attr string', - dataType: 'string', - matchType: 'NEQ', - value: 'mark', - fadeIn: false, - // depth: { min: 0, max: 1 }, - }, - '2' -); -console.log(graph.export()); - -mockStore.dispatch(setQuerybuilderNodes(graph.export())); - -export const Flow = { - args: {}, -}; - -export default Component; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.module.scss.d.ts index d44a6b42f..634416569 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.module.scss.d.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.module.scss.d.ts @@ -1,4 +1,16 @@ declare const classNames: { + readonly handle: 'handle'; + readonly handle_logic: 'handle_logic'; + readonly handle_logic_duration: 'handle_logic_duration'; + readonly handle_logic_datetime: 'handle_logic_datetime'; + readonly handle_logic_time: 'handle_logic_time'; + readonly handle_logic_date: 'handle_logic_date'; + readonly handle_logic_bool: 'handle_logic_bool'; + readonly handle_logic_float: 'handle_logic_float'; + readonly handle_logic_int: 'handle_logic_int'; + readonly handle_logic_string: 'handle_logic_string'; + readonly handle_from_relation: 'handle_from_relation'; + readonly handle_to_relation: 'handle_to_relation'; readonly 'react-flow__node': 'react-flow__node'; readonly selected: 'selected'; readonly entityWrapper: 'entityWrapper'; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.storiesDEFUNCT.tsx similarity index 90% rename from libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.stories.tsx rename to libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.storiesDEFUNCT.tsx index eec408f5a..a1788066c 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.stories.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.storiesDEFUNCT.tsx @@ -5,11 +5,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; const Component: Meta<typeof AttributePill> = { diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.tsx index 89c663492..0875fe2a2 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/attributepill.tsx @@ -4,13 +4,13 @@ import styles from './attributepill.module.scss'; import { Handle, NodeProps, Position } from 'reactflow'; import { AttributeOperatorSelect } from './operator'; import Select from './select-component'; -import { AttributeNode, Handles } from '../../../model'; +import { Handles } from '../../../model'; /** * Component to render an attribute flow element * @param {FlowElement<EntityData>)} param0 The data of an entity flow element. */ -export const AttributePill = React.memo(({ id, data }: AttributeNode) => { +export const AttributePill = React.memo(({ id, data }: any) => { const theme = useTheme(); const [read, setRead] = useState(true); // console.log('AttributePill', data); @@ -73,10 +73,7 @@ export const AttributePill = React.memo(({ id, data }: AttributeNode) => { }; //TODO: docstrings - const className = - styles.attributeHandleLeft + - ' ' + - (false ? styles.handleConnectedFill : ''); + const className = styles.attributeHandleLeft + ' ' + (false ? styles.handleConnectedFill : ''); const onChange = (e: any) => { if (data != undefined) { @@ -93,30 +90,19 @@ export const AttributePill = React.memo(({ id, data }: AttributeNode) => { //TODO: fix use of relation boilerplate styling return ( - <div - className={styles.attributeMain} - style={{ backgroundColor: theme.palette.custom.elements.attribute[0] }} - > + <div className={styles.attributeMain} style={{ backgroundColor: theme.palette.custom.elements.attribute[0] }}> <Handle id={Handles.OnAttribute} type="source" position={Position.Left} - className={ - styles.attributeHandleLeft + - ' ' + - (false ? styles.handleConnectedFill : '') - } + className={styles.attributeHandleLeft + ' ' + (false ? styles.handleConnectedFill : '')} style={{ backgroundColor: theme.palette.custom.elements.attribute[1] }} /> <Handle id={Handles.ToAttribute} type="source" position={Position.Left} - className={ - styles.attributeHandleLeft + - ' ' + - (false ? styles.handleConnectedFill : '') - } + className={styles.attributeHandleLeft + ' ' + (false ? styles.handleConnectedFill : '')} style={{ backgroundColor: theme.palette.custom.elements.attribute[1], left: 50, diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/getAttributeBoolOperators.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/getAttributeBoolOperators.ts index 22809a7cd..0d01e2c01 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/getAttributeBoolOperators.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/getAttributeBoolOperators.ts @@ -1,7 +1,5 @@ /** Determines the available boolean operators for a certain datatype. */ -export function GetAttributeBoolOperators( - datatype: string -): { label: string; value: string }[] { +export function GetAttributeBoolOperators(datatype: string): { label: string; value: string }[] { switch (datatype) { case 'text': case 'string': diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/operator/operatorselect.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/operator/operatorselect.tsx index d62e39a1d..4ea581df8 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/operator/operatorselect.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/operator/operatorselect.tsx @@ -30,9 +30,7 @@ export function AttributeOperatorSelect({ }: Props) { const listboxRef = useRef<HTMLUListElement>(null); const [listboxVisible, setListboxVisible] = useState(false); - const [currSelected, setCurrSelected] = useState( - options.find((o) => o.value == selected)?.label || options[0].label - ); + const [currSelected, setCurrSelected] = useState(options.find((o) => o.value == selected)?.label || options[0].label); React.useEffect(() => { if (listboxVisible) { @@ -56,11 +54,7 @@ export function AttributeOperatorSelect({ onFocus={() => setListboxVisible(true)} onBlur={() => setListboxVisible(false)} > - <div - className={ - styles.valueContainer + ' ' + (listboxVisible && styles.highlighted) - } - > + <div className={styles.valueContainer + ' ' + (listboxVisible && styles.highlighted)}> <span>{currSelected}</span> </div> {options.length > 1 && ( @@ -70,11 +64,7 @@ export function AttributeOperatorSelect({ onMouseOver={() => setListboxVisible(true)} > {options.map((option) => ( - <li - className={option.label == currSelected ? styles.selected : ''} - key={option.value} - onClick={() => changeSelected(option)} - > + <li className={option.label == currSelected ? styles.selected : ''} key={option.value} onClick={() => changeSelected(option)}> {option.label} </li> ))} diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/select-component.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/select-component.tsx index 3e6b6a979..2f804d657 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/select-component.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/attributepill/select-component.tsx @@ -11,9 +11,8 @@ import { useTheme } from '@mui/material'; import React, { useState } from 'react'; import styles from './attributepill.module.scss'; -import { AttributeData } from '../../../model'; -export default function SelectComponent({ data }: { data: AttributeData }) { +export default function SelectComponent({ data }: { data: any }) { const theme = useTheme(); /** @@ -68,10 +67,7 @@ export default function SelectComponent({ data }: { data: AttributeData }) { }; return ( - <div - className={styles.matchTypeSelect} - style={{ backgroundColor: theme.palette.custom.elements.attribute[1] }} - > + <div className={styles.matchTypeSelect} style={{ backgroundColor: theme.palette.custom.elements.attribute[1] }}> <select style={{ maxWidth: calcSelectWidth('==') }} value={data.matchType} diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/edge-line.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/edge-line.tsx index 2d9006f67..328e49613 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/edge-line.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/edge-line.tsx @@ -10,30 +10,21 @@ * See testing plan for more details.*/ import React from 'react'; import { EdgeProps, getSmoothStepPath, Position } from 'reactflow'; -import { Handles } from '../../graph-reactflow/handles'; +import { Handles } from '../../model'; /** * A custom query element edge line component. * @param {EdgeProps} param0 The coordinates for the start and end point, the id and the style. */ -export default function EdgeLine({ - id, - sourceX, - sourceY, - targetX, - targetY, - style, - sourceHandleId, - targetHandleId, -}: EdgeProps) { +export default function EdgeLine({ id, sourceX, sourceY, targetX, targetY, style, sourceHandleId, targetHandleId }: EdgeProps) { //Centering the line sourceY -= 3; targetY -= 3; // Correct line positions with hardcoded numbers, because react flow lacks this functionality - if (sourceHandleId == Handles.ToAttributeHandle) sourceX += 2; + if (sourceHandleId == Handles.EntityLeft) sourceX += 2; - if (targetHandleId == Handles.ToAttributeHandle) targetX += 2; + if (targetHandleId == Handles.EntityRight) targetX += 2; let spos: Position = Position.Bottom; if (sourceHandleId == Handles.RelationLeft) { @@ -44,11 +35,7 @@ export default function EdgeLine({ spos = Position.Right; sourceX -= 2; sourceY -= 3; - } else if ( - sourceHandleId !== undefined && - sourceHandleId !== null && - sourceHandleId.includes('functionHandle') - ) { + } else if (sourceHandleId !== undefined && sourceHandleId !== null && sourceHandleId.includes('functionHandle')) { spos = Position.Top; sourceX -= 4; sourceY += 3; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill-full.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill-full.stories.tsx index fe8d4464a..fd47ee277 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill-full.stories.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill-full.stories.tsx @@ -1,18 +1,12 @@ import React from 'react'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - setQuerybuilderNodes, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, setQuerybuilderNodes } from '@graphpolaris/shared/lib/data-access/store'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; import { configureStore } from '@reduxjs/toolkit'; import { Meta } from '@storybook/react'; import { Provider } from 'react-redux'; -import { MultiGraph } from 'graphology'; import { QueryBuilder } from '../../../panel'; -import { QueryGraph } from '../../../model/graphology/model'; -import { circular } from 'graphology-layout'; import { QueryMultiGraphology } from '@graphpolaris/shared/lib/querybuilder/model/graphology/utils'; +import { QueryElementTypes } from '../../../model'; const Component: Meta<typeof QueryBuilder> = { component: QueryBuilder, @@ -34,10 +28,7 @@ const mockStore = configureStore({ }, }); const graph = new QueryMultiGraphology(); -graph.addPill2Graphology( - { type: 'entity', x: 100, y: 100, name: 'Entity Pill', fadeIn: false }, - '2' -); +graph.addPill2Graphology({ id: '2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Entity Pill' }); console.log(graph.export()); mockStore.dispatch(setQuerybuilderNodes(graph.export())); diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss index 79e336437..2590cb9d8 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss @@ -1,43 +1,9 @@ @import '../../querypills.module.scss'; -.entity { - display: flex; - font-family: monospace; - font-weight: bold; - font-size: 10px; - padding: 4px 2ch; - border-radius: 3px; -} - .highlighted { box-shadow: black 0 0 2px; } -.handleLeft { - border: 0px; - border-radius: 0px; - left: 12px; - width: 7px; - height: 7px; - margin-bottom: 11px; - background: rgba(255, 255, 255, 0.6); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3); - transform-origin: center; -} - -// .handleBottom { -// border: 0px; -// border-radius: 0px; -// width: 7px; -// height: 7px; -// left: 27.5px; -// margin-bottom: 11px; -// background: rgba(255, 255, 255, 0.6); -// box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3); -// transform: rotate(-45deg); -// transform-origin: center; -// } - .react-flow__edges { zindex: 3; } @@ -64,13 +30,63 @@ // Entity element .entity { background: #e9e9e9; - display: flex; - border-radius: 1px; + // display: flex; + // border: #ffffff solid 2px; + border-radius: 2px; font-family: monospace; font-weight: bold; - font-size: 11px; color: black; - padding-left: 45px; + min-width: 8rem; + + font-size: 10px; + border-radius: 2px; + // padding-left: 45px; + + .title { + position: relative; + border-top-left-radius: inherit; + border-top-right-radius: inherit; + padding: 0.2rem 1.2rem; + text-align: center; + flex-grow: 1; + background-color: '#eee'; + } + + @keyframes slide-down { + 0% { + max-height: 0px; + opacity: 0; + transform: translateY(0%); + } + 100% { + max-height: 100px; + opacity: 1; + transform: translateY(0); + } + } + + .content { + padding: 0.2rem 1.2rem; + // visibility: hidden; + display: none; + animation: slide-down 0.2s ease-out; + overflow: hidden; + max-height: 0; + } + + .content_display { + // visibility: visible; + display: block; + max-height: 100px; + } + + .io { + top: auto; + transform: translate(0, -115%); + } + .io_right { + right: 0.5rem; + } } .entityFade { @@ -148,12 +164,6 @@ } // General style -.ToRelationHandle { - border-radius: 1px !important; - left: 10px !important; - top: 35% !important; - background: rgba(0, 0, 0, 0.3) !important; -} .ToAttributeHandle { border-radius: 1px !important; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss.d.ts index bc2a7aa4b..2812eed57 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss.d.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.module.scss.d.ts @@ -1,4 +1,16 @@ declare const classNames: { + readonly handle: 'handle'; + readonly handle_logic: 'handle_logic'; + readonly handle_logic_duration: 'handle_logic_duration'; + readonly handle_logic_datetime: 'handle_logic_datetime'; + readonly handle_logic_time: 'handle_logic_time'; + readonly handle_logic_date: 'handle_logic_date'; + readonly handle_logic_bool: 'handle_logic_bool'; + readonly handle_logic_float: 'handle_logic_float'; + readonly handle_logic_int: 'handle_logic_int'; + readonly handle_logic_string: 'handle_logic_string'; + readonly handle_from_relation: 'handle_from_relation'; + readonly handle_to_relation: 'handle_to_relation'; readonly 'react-flow__node': 'react-flow__node'; readonly selected: 'selected'; readonly entityWrapper: 'entityWrapper'; @@ -9,15 +21,18 @@ declare const classNames: { readonly handleConnectedBorderRight: 'handleConnectedBorderRight'; readonly handleConnectedBorderLeft: 'handleConnectedBorderLeft'; readonly handleFunction: 'handleFunction'; - readonly entity: 'entity'; readonly highlighted: 'highlighted'; - readonly handleLeft: 'handleLeft'; readonly contentWrapper: 'contentWrapper'; + readonly entity: 'entity'; + readonly title: 'title'; + readonly content: 'content'; + readonly content_display: 'content_display'; + readonly io: 'io'; + readonly io_right: 'io_right'; readonly entityFade: 'entityFade'; readonly entityHandleLeft: 'entityHandleLeft'; readonly entityHandleBottom: 'entityHandleBottom'; readonly entitySpan: 'entitySpan'; - readonly ToRelationHandle: 'ToRelationHandle'; readonly ToAttributeHandle: 'ToAttributeHandle'; readonly ReceiveFunctionHandle: 'ReceiveFunctionHandle'; readonly handleFunctionEntity: 'handleFunctionEntity'; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.stories.tsx index b637b5938..edff6caa0 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.stories.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.stories.tsx @@ -5,11 +5,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { EntityData } from '../../../model'; @@ -48,7 +44,6 @@ export const Default: StoryObj<{ data: EntityData }> = { args: { data: { name: 'TestEntity', - fadeIn: true, }, }, }; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx index 9a126d5d3..2d0cd37a3 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx @@ -1,37 +1,95 @@ // import { handles } from '@graphpolaris/shared/lib/querybuilder/usecases'; import { useTheme } from '@mui/material'; -import React, { useEffect } from 'react'; -import { ReactFlow, Handle, Position } from 'reactflow'; +import React, { MouseEventHandler, useEffect, useMemo } from 'react'; +import { ReactFlow, Handle, Position, getConnectedEdges } from 'reactflow'; import styles from './entitypill.module.scss'; -import { EntityNode, Handles } from '../../../model'; +import { + SchemaReactflowEntityNode, + Handles, + toHandleId, + handleDataFromReactflowToId, + QueryElementTypes, + NodeAttribute, +} from '../../../model'; +import { SchemaAttribute } from '@graphpolaris/shared/lib/schema'; +import { styleHandleMap } from '../../utils'; +import { useQuerybuilderGraph } from '@graphpolaris/shared/lib/data-access'; /** * Component to render an entity flow element * @param {NodeProps} param0 The data of an entity flow element. */ -export const EntityFlowElement = React.memo(({ data }: EntityNode) => { +export const EntityFlowElement = React.memo((node: SchemaReactflowEntityNode) => { const theme = useTheme(); - // console.log('EntityPill', data); + // console.log('EntityFlowElement', node); + + const data = node.data; + const forceOpen: boolean = false; + if (!data.leftRelationHandleId) throw new Error('EntityFlowElement: data.leftRelationHandleId is undefined'); + if (!data.rightRelationHandleId) throw new Error('EntityFlowElement: data.rightRelationHandleId is undefined'); + + const graph = useQuerybuilderGraph(); + const attributeEdges = useMemo( + () => graph.edges.filter((edge) => edge.source === node.id && !!edge?.attributes?.sourceHandleData.attributeType), + [graph] + ); + + const [hovered, setHovered] = React.useState(false); + const [handleBeingDragged, setHandleBeingDragged] = React.useState(-1); // TODO: Change flow element width when text overflows - const animation = data.fadeIn ? styles.entityFade : ''; + const animation = styles.entityFade; // TODO: Check if correct + + const onMouseEnter = (event: React.MouseEvent) => { + setHovered(true); + }; + + const onMouseLeave = (event: React.MouseEvent) => { + setHovered(false); + }; + + const onHandleMouseDown = (attribute: NodeAttribute, i: number, event: React.MouseEvent) => { + setHandleBeingDragged(i); + window.addEventListener('mouseup', onHandleMouseUp, true); + }; + + const onHandleMouseUp = () => { + setHandleBeingDragged(-1); + window.removeEventListener('mouseup', onHandleMouseUp, true); + }; + + const onConnect = (params: any) => { + console.log('EntityPill onConnect', params); + }; + + const showingDropdown = hovered || handleBeingDragged !== -1 || attributeEdges.length > 0; return ( <div className={`${styles.entity} ${animation} query_builder-entity`} - style={{ backgroundColor: theme.palette.custom.elements.entityBase[0] }} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeave} + style={ + { + // borderColor: theme.palette.custom.elements.entityBase[0], + } + } > <Handle - id={Handles.ToRelation} - type="source" - position={Position.Bottom} - className={ - styles.ToRelationHandle + - ' ' + - (false ? styles.handleConnectedFill : '') - } + // id={getHandleId(data.name, data.type, Handles.ToRelation, '')} + id={toHandleId(data.leftRelationHandleId)} + type="target" + position={Position.Left} + className={styles.handle_to_relation} /> <Handle + // id={getHandleId(data.name, data.type, Handles.ToRelation, '')} + id={toHandleId(data.rightRelationHandleId)} + type="source" + position={Position.Right} + className={styles.handle_to_relation} + /> + {/* <Handle id={Handles.ToAttribute} type="target" position={Position.Bottom} @@ -50,11 +108,58 @@ export const EntityFlowElement = React.memo(({ data }: EntityNode) => { ' ' + (false ? styles.handleConnectedFill : '') } - /> + /> */} - <div className={styles.entityWrapper}> - <span className={styles.entitySpan}>{data.name}</span> + <div + className={styles.title} + style={{ + backgroundColor: theme.palette.custom.elements.entityBase[0], + }} + > + {data.name} </div> + {/* <div className={styles.entityWrapper}> + <span className={styles.entitySpan}>{data.name}</span> + </div> */} + {data?.attributes && ( + <div className={styles.content + ' ' + (showingDropdown || forceOpen || hovered ? styles.content_display : '')}> + {data.attributes + .filter( + (attribute, i) => + forceOpen || + hovered || + handleBeingDragged === i || + (attributeEdges.some( + (edge) => + edge?.attributes?.sourceHandleData && + toHandleId(edge?.attributes?.sourceHandleData) === handleDataFromReactflowToId(node, attribute) + ) && + !!attribute.handleData.attributeName) + ) + .map((attribute, i) => ( + <div + key={(attribute.handleData.attributeName || '') + i} + onMouseDown={(event: React.MouseEvent) => { + onHandleMouseDown(attribute, i, event); + }} + > + {attribute.handleData.attributeName} + <Handle + id={handleDataFromReactflowToId(node, attribute)} + type="source" + position={Position.Right} + className={ + (attribute.handleData.attributeType ? styleHandleMap[attribute.handleData.attributeType] : '') + + ' ' + + styles.io + + ' ' + + styles.io_right + } + ></Handle> + </div> + ))} + </div> + )} </div> ); }); diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/SelectFunction.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/SelectFunction.tsx index 60e40cbb5..5a6fd8c4c 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/SelectFunction.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/SelectFunction.tsx @@ -8,21 +8,20 @@ /* The comment above was added so the code coverage wouldn't count this file towards code coverage. * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ +import { makeStyles } from '@mui/material'; import React, { useState } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import { useStyles } from '../../QueryBuilderStylesheet'; +import styles from './functionpill.module.scss'; // Create style constant to prevent rereaction of styles -const madeStyles = makeStyles(useStyles); +// const madeStyles = makeStyles(useStyles); /** * The flow element for the modifier. * @param param0 The data of the modifier flow element. */ export default function ModifierFlowElement({ data }: any) { - const styles = madeStyles(); const [disable, setDisable] = useState(true); - const [disClass, setDisClass] = useState(styles.disable); + const [disClass, setDisClass] = useState<string>(styles.disable); /** * Calculate the width of the select element based on the displayed value. @@ -64,11 +63,7 @@ export default function ModifierFlowElement({ data }: any) { }; return ( - <div - className={styles.matchModifierTypeSelect} - onBlur={disableSelect} - onDoubleClick={enableSelect} - > + <div className={styles.matchModifierTypeSelect} onBlur={disableSelect} onDoubleClick={enableSelect}> <select style={{ color: disable ? 'black' : 'black', diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.module.scss b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.module.scss index 7e589ded7..ec1d4a5e9 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.module.scss +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.module.scss @@ -1,167 +1,175 @@ -@import '../entitypill/entitypill.module.scss'; - -$baseColor: #8c75c9; - -// Function element -.function { - background-color: $baseColor; - display: 'flex'; - border-radius: '1px'; - font-family: monospace; - font-weight: 'bold'; - font-size: 11px; - color: black; - min-width: '140px'; - text-align: 'center'; - line-height: 20px; - padding-top: 2; - padding-right: 5; - padding-bottom: 4; - padding-left: 5; - - &::before { - position: 'absolute'; - content: '""'; - width: '100%'; - left: '0px'; - top: '0px'; - height: '100%'; - border-radius: '3px'; - z-index: -1; - background-color: $baseColor; - border-bottom: 'none'; - } - - &::after { - position: 'absolute'; - content: '""'; - width: '100%'; - left: '0px'; - top: '0'; - height: '100%'; - border-radius: '3px'; - z-index: -1; - background-color: $baseColor; - border-top: 'none'; - } -} - -.functionWrapper { - display: 'block'; - width: 'inherit'; - align-items: 'center'; - justify-content: 'space-between'; -} -.functionHandleFiller { - flex: '1 1 0'; - display: 'flow-root'; -} -.functionHandle { - border: 0; - border-radius: 0; - width: 8; - height: 8; - left: 9; - top: 7; - background: 'rgba(0, 0, 0, 0.3)'; - transform-origin: 'center'; - position: 'relative'; - float: 'right'; - margin-right: '20px'; - &::before { - content: '""'; - width: 6; - height: 6; - left: 1; - bottom: 1; - border: 0; - border-radius: 0; - background: 'rgba(255, 255, 255, 0.6)'; - z-index: -1; - display: 'inline-block'; - position: 'fixed'; - } -} -.functionHandleBottom { - border: 0; - border-radius: 0; - width: 8; - height: 8; - left: 27.5; - margin-bottom: 10; - background-color: 'rgba(255, 255, 255, 0.6)'; - transform: 'rotate(-45deg)'; - transform-origin: 'center'; - &::before { - content: '""'; - width: 6; - height: 6; - left: 1; - bottom: 1; - border: 0; - border-radius: 0; - background-color: 'rgba(255, 255, 255, 0.6)'; - z-index: -1; - display: 'inline-block'; - position: 'fixed'; - } -} -.functionInputHolder { - display: 'flex'; - float: 'right'; - margin-right: '20px'; - margin-top: '4px'; - margin-left: '5px'; - max-width: '80px'; - background-color: 'rgba(255, 255, 255, 0.6)'; - border-radius: '2px'; - align-items: 'center'; - max-height: '12px'; -} -.functionInput { - z-index: 1; - cursor: 'text'; - min-width: '0px'; - max-width: '1.5ch'; - height: '14px'; - border: 'none'; - background-color: 'rgba(255, 255, 255, 0.6)'; - text-align: 'center'; - font-family: 'monospace'; - font-weight: 'bold'; - font-size: '11px'; - color: '#181520'; - user-select: 'none'; - font-style: 'italic'; - float: 'right'; - margin: '3px 0'; - margin-right: '10px'; - &:focus { - outline: 'none'; - user-select: 'none'; - } - &::placeholder { - outline: 'none'; - user-select: 'none'; - font-style: 'italic'; - } -} -.functionReadonly { - cursor: 'grab !important'; - color: '#181520 !important'; - user-select: 'none'; - font-style: 'normal !important'; -} -.functionSpan { - float: 'left'; - margin-left: 20; - margin-right: 20; -} -.functionSpanRight { - float: 'right'; - margin-right: 10; -} - -.functionDataWrapper { - display: block; -} +@import '../entitypill/entitypill.module.scss'; + +$baseColor: #8c75c9; + +.disable { + pointer-events: none; + opacity: 0.4; +} +.matchModifierTypeSelect { + opacity: 0.4; +} + +// Function element +.function { + background-color: $baseColor; + display: 'flex'; + border-radius: '1px'; + font-family: monospace; + font-weight: 'bold'; + font-size: 11px; + color: black; + min-width: '140px'; + text-align: 'center'; + line-height: 20px; + padding-top: 2; + padding-right: 5; + padding-bottom: 4; + padding-left: 5; + + &::before { + position: 'absolute'; + content: '""'; + width: '100%'; + left: '0px'; + top: '0px'; + height: '100%'; + border-radius: '3px'; + z-index: -1; + background-color: $baseColor; + border-bottom: 'none'; + } + + &::after { + position: 'absolute'; + content: '""'; + width: '100%'; + left: '0px'; + top: '0'; + height: '100%'; + border-radius: '3px'; + z-index: -1; + background-color: $baseColor; + border-top: 'none'; + } +} + +.functionWrapper { + display: 'block'; + width: 'inherit'; + align-items: 'center'; + justify-content: 'space-between'; +} +.functionHandleFiller { + flex: '1 1 0'; + display: 'flow-root'; +} +.functionHandle { + border: 0; + border-radius: 0; + width: 8; + height: 8; + left: 9; + top: 7; + background: 'rgba(0, 0, 0, 0.3)'; + transform-origin: 'center'; + position: 'relative'; + float: 'right'; + margin-right: '20px'; + &::before { + content: '""'; + width: 6; + height: 6; + left: 1; + bottom: 1; + border: 0; + border-radius: 0; + background: 'rgba(255, 255, 255, 0.6)'; + z-index: -1; + display: 'inline-block'; + position: 'fixed'; + } +} +.functionHandleBottom { + border: 0; + border-radius: 0; + width: 8; + height: 8; + left: 27.5; + margin-bottom: 10; + background-color: 'rgba(255, 255, 255, 0.6)'; + transform: 'rotate(-45deg)'; + transform-origin: 'center'; + &::before { + content: '""'; + width: 6; + height: 6; + left: 1; + bottom: 1; + border: 0; + border-radius: 0; + background-color: 'rgba(255, 255, 255, 0.6)'; + z-index: -1; + display: 'inline-block'; + position: 'fixed'; + } +} +.functionInputHolder { + display: 'flex'; + float: 'right'; + margin-right: '20px'; + margin-top: '4px'; + margin-left: '5px'; + max-width: '80px'; + background-color: 'rgba(255, 255, 255, 0.6)'; + border-radius: '2px'; + align-items: 'center'; + max-height: '12px'; +} +.functionInput { + z-index: 1; + cursor: 'text'; + min-width: '0px'; + max-width: '1.5ch'; + height: '14px'; + border: 'none'; + background-color: 'rgba(255, 255, 255, 0.6)'; + text-align: 'center'; + font-family: 'monospace'; + font-weight: 'bold'; + font-size: '11px'; + color: '#181520'; + user-select: 'none'; + font-style: 'italic'; + float: 'right'; + margin: '3px 0'; + margin-right: '10px'; + &:focus { + outline: 'none'; + user-select: 'none'; + } + &::placeholder { + outline: 'none'; + user-select: 'none'; + font-style: 'italic'; + } +} +.functionReadonly { + cursor: 'grab !important'; + color: '#181520 !important'; + user-select: 'none'; + font-style: 'normal !important'; +} +.functionSpan { + float: 'left'; + margin-left: 20; + margin-right: 20; +} +.functionSpanRight { + float: 'right'; + margin-right: 10; +} + +.functionDataWrapper { + display: block; +} diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.module.scss.d.ts index 924514ea1..ae6d376d7 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.module.scss.d.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.module.scss.d.ts @@ -1,4 +1,18 @@ declare const classNames: { + readonly disable: 'disable'; + readonly matchModifierTypeSelect: 'matchModifierTypeSelect'; + readonly handle: 'handle'; + readonly handle_logic: 'handle_logic'; + readonly handle_logic_duration: 'handle_logic_duration'; + readonly handle_logic_datetime: 'handle_logic_datetime'; + readonly handle_logic_time: 'handle_logic_time'; + readonly handle_logic_date: 'handle_logic_date'; + readonly handle_logic_bool: 'handle_logic_bool'; + readonly handle_logic_float: 'handle_logic_float'; + readonly handle_logic_int: 'handle_logic_int'; + readonly handle_logic_string: 'handle_logic_string'; + readonly handle_from_relation: 'handle_from_relation'; + readonly handle_to_relation: 'handle_to_relation'; readonly 'react-flow__node': 'react-flow__node'; readonly selected: 'selected'; readonly entityWrapper: 'entityWrapper'; @@ -9,15 +23,18 @@ declare const classNames: { readonly handleConnectedBorderRight: 'handleConnectedBorderRight'; readonly handleConnectedBorderLeft: 'handleConnectedBorderLeft'; readonly handleFunction: 'handleFunction'; - readonly entity: 'entity'; readonly highlighted: 'highlighted'; - readonly handleLeft: 'handleLeft'; readonly contentWrapper: 'contentWrapper'; + readonly entity: 'entity'; + readonly title: 'title'; + readonly content: 'content'; + readonly content_display: 'content_display'; + readonly io: 'io'; + readonly io_right: 'io_right'; readonly entityFade: 'entityFade'; readonly entityHandleLeft: 'entityHandleLeft'; readonly entityHandleBottom: 'entityHandleBottom'; readonly entitySpan: 'entitySpan'; - readonly ToRelationHandle: 'ToRelationHandle'; readonly ToAttributeHandle: 'ToAttributeHandle'; readonly ReceiveFunctionHandle: 'ReceiveFunctionHandle'; readonly handleFunctionEntity: 'handleFunctionEntity'; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.stories.tsx index 559e2f145..b086172fd 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.stories.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.stories.tsx @@ -5,13 +5,8 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; -import { FunctionData } from '../../../model'; const Component: Meta<typeof FunctionFlowElement> = { /* 👇 The title prop is optional. @@ -44,11 +39,10 @@ const Mockstore = configureStore({ // const Template = (args: any) => <EntityRFPill {...args} />; -export const Default: StoryObj<{ data: FunctionData }> = { +export const Default: StoryObj = { args: { data: { functionType: 'test', - fadeIn: true, args: { string: { displayName: 'testarg', diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.tsx index d97f2c50b..aeaa2b003 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/functionpill.tsx @@ -12,9 +12,9 @@ import React, { useState } from 'react'; import { Handle, Position } from 'reactflow'; import styles from './functionpill.module.scss'; import { useTheme } from '@mui/material'; -import { FunctionData, Handles } from '../../../model'; +import { Handles } from '../../../model'; -const countArgs = (data: FunctionData | undefined) => { +const countArgs = (data: any) => { if (data !== undefined) { let count = 0; @@ -41,7 +41,7 @@ export const capitalizeFirstLetter = (string: string) => { * Component to render a relation flow element * @param { FlowElement<FunctionData>} param0 The data of a relation flow element. */ -export default function RelationFlowElement({ data }: FunctionNode) { +export default function RelationFlowElement({ data }: any) { const [read, setRead] = useState(true); const theme = useTheme(); @@ -52,11 +52,7 @@ export default function RelationFlowElement({ data }: FunctionNode) { if (event.key == 'Enter') setRead(true); }; - const getArgs = ( - styles: any, - data: FunctionData | undefined, - setRead: any - ) => { + const getArgs = (styles: any, data: any, setRead: any) => { let rows: JSX.Element[] = []; if (data != undefined) { @@ -67,18 +63,12 @@ export default function RelationFlowElement({ data }: FunctionNode) { if (item.visible) { rows.push( <span className={styles.functionHandleFiller} key={name}> - <span className={styles.functionSpan}> - {capitalizeFirstLetter(name)} - </span> + <span className={styles.functionSpan}>{capitalizeFirstLetter(name)}</span> <Handle id={Handles.FunctionBase + name} type="source" position={Position.Top} - className={ - styles.functionHandle + - ' ' + - (false ? styles.handleConnectedFill : '') - } + className={styles.functionHandle + ' ' + (false ? styles.handleConnectedFill : '')} style={{ visibility: item.connectable ? 'inherit' : 'hidden', }} @@ -131,29 +121,18 @@ export default function RelationFlowElement({ data }: FunctionNode) { > <div className={styles.functionWrapper}>{rows}</div> </div> - <div - className={`${styles.entity} entityWrapper ${entity === undefined ? 'hidden' : '' - }`} - > + <div className={`${styles.entity} entityWrapper ${entity === undefined ? 'hidden' : ''}`}> <Handle - id={Handles.ToRelation} + id={Handles.FromAttribute} type="source" position={Position.Bottom} - className={ - styles.entityHandleLeft + - ' ' + - (false ? styles.handleConnectedFill : '') - } + className={styles.entityHandleLeft + ' ' + (false ? styles.handleConnectedFill : '')} /> <Handle id={Handles.ToAttribute} type="source" position={Position.Bottom} - className={ - styles.entityHandleBottom + - ' ' + - (false ? styles.handleConnectedFill : '') - } + className={styles.entityHandleBottom + ' ' + (false ? styles.handleConnectedFill : '')} /> <div className={styles.entityWrapper}> <span className={styles.entitySpan}>{entity ? entity : ''}</span> diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/index.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/index.ts index 865e20fa5..3785778d3 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/index.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/functionpill/index.ts @@ -1 +1 @@ -export * from './functionpill' \ No newline at end of file +export * from './functionpill'; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/index.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/index.ts index 3ea6fb9af..22ce04749 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/index.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/index.ts @@ -1,3 +1,3 @@ -export * from './attributepill' -export * from './entitypill' -export * from './relationpill' \ No newline at end of file +export * from './attributepill'; +export * from './entitypill'; +export * from './relationpill'; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.module.scss b/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.module.scss new file mode 100644 index 000000000..81e3710c8 --- /dev/null +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.module.scss @@ -0,0 +1,58 @@ +@import '../../querypills.module.scss'; + +.logic { + color: '#181520'; + // background-color: #e68067; + background-color: #fdfdfd; + border: #e68067 solid 1px; + border-radius: 2px; + font-family: monospace; + font-weight: bold; + font-size: 10; + display: flex; + // border-top-right-radius: 5px; + // border-bottom-right-radius: 5px; + // height: 3rem; + .logicInput { + // float: right; + // background-color: #ee917a; + padding-left: 2px; + padding-right: 2px; + width: 3rem; + height: 1.2rem; + position: relative; + left: 0.8rem; + bottom: 0.3rem; + } + .logicSpan { + // height: 100%; + padding: 0 1rem; + // display: flex; + // text-align: center; + // align-items: center; + } +} + +// .matchlogicTypeSelect { +// float: left; +// background-color: rgba(255, 255, 255, 0.6); +// border-radius: 2px; +// text-align: center; +// & select { +// background: transparent; +// border: none; +// appearance: none; +// font-family: monospace; +// font-weight: bolder; +// color: black; +// font-size: 11; +// } +// & option { +// font-family: monospace; +// font-size: 11px; +// } +// } +// .disable { +// opacity: 1 !important; +// pointer-events: none; +// } diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.module.scss.d.ts new file mode 100644 index 000000000..e9035be6b --- /dev/null +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.module.scss.d.ts @@ -0,0 +1,28 @@ +declare const classNames: { + readonly handle: 'handle'; + readonly handle_logic: 'handle_logic'; + readonly handle_logic_duration: 'handle_logic_duration'; + readonly handle_logic_datetime: 'handle_logic_datetime'; + readonly handle_logic_time: 'handle_logic_time'; + readonly handle_logic_date: 'handle_logic_date'; + readonly handle_logic_bool: 'handle_logic_bool'; + readonly handle_logic_float: 'handle_logic_float'; + readonly handle_logic_int: 'handle_logic_int'; + readonly handle_logic_string: 'handle_logic_string'; + readonly handle_from_relation: 'handle_from_relation'; + readonly handle_to_relation: 'handle_to_relation'; + readonly 'react-flow__node': 'react-flow__node'; + readonly selected: 'selected'; + readonly entityWrapper: 'entityWrapper'; + readonly hidden: 'hidden'; + readonly 'react-flow__edges': 'react-flow__edges'; + readonly 'react-flow__edge-default': 'react-flow__edge-default'; + readonly handleConnectedFill: 'handleConnectedFill'; + readonly handleConnectedBorderRight: 'handleConnectedBorderRight'; + readonly handleConnectedBorderLeft: 'handleConnectedBorderLeft'; + readonly handleFunction: 'handleFunction'; + readonly logic: 'logic'; + readonly logicInput: 'logicInput'; + readonly logicSpan: 'logicSpan'; +}; +export = classNames; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.tsx new file mode 100644 index 000000000..538e6391a --- /dev/null +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/logicpill/logicpill.tsx @@ -0,0 +1,137 @@ +/** + * This program has been developed by students from the bachelor Computer Science at + * Utrecht University within the Software Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ + +/* istanbul ignore file */ +/* The comment above was added so the code coverage wouldn't count this file towards code coverage. + * We do not test components/renderfunctions/styling files. + * See testing plan for more details.*/ +import React, { useCallback, useMemo } from 'react'; +import { Handle, HandleType, NodeProps, Position } from 'reactflow'; +import styles from './logicpill.module.scss'; +import { + AllLogicMap, + EntityNodeAttributes, + Handles, + LogicData, + LogicNodeAttributes, + QueryGraphEdges, + QueryGraphNodes, + SchemaReactflowLogicNode, + toHandleData, + toHandleId, +} from '../../../model'; +import { Input } from '@mui/material'; +import { styleHandleMap } from '../../utils'; +import { + setQuerybuilderNodes, + useAppDispatch, + useQuerybuilderGraph, + useQuerybuilderGraphology, +} from '@graphpolaris/shared/lib/data-access'; +import { InputNode, InputNodeTypeTypes } from '../../../model/logic/general'; +import { SerializedEdge, SerializedNode } from 'graphology-types'; + +/** + * Component to render an entity flow element + * @param param0 Data of the flow element. + */ +export default function LogicPill(node: SchemaReactflowLogicNode) { + const dispatch = useAppDispatch(); + const data = node.data; + const logic = data.logic; + const output = data.logic.output; + + const graph = useQuerybuilderGraph(); + const graphology = useQuerybuilderGraphology(); + const connectionsToLeft = useMemo(() => graph.edges.filter((edge) => edge.target === node.id), [graph]); + const connectionsToRight = useMemo(() => graph.edges.filter((edge) => edge.source === node.id), [graph]); + + if (!data.id) throw new Error('LogicPill: data.id is undefined'); + const defaultHandleData = { + nodeId: data.id, + nodeName: data.name, + nodeType: data.type, + }; + + const onInputUpdated = (value: string, input: InputNode, idx: number) => { + let logicNode = { ...(graphology.getNodeAttributes(node.id) as LogicNodeAttributes) }; + if (!logicNode) throw new Error('LogicPill: logicNode is undefined'); + let logicNodeInputs = { ...logicNode.inputs }; + if (logicNodeInputs[input.name] != value) { + logicNodeInputs[input.name] = value; + logicNode.inputs = logicNodeInputs; + graphology.setNodeAttribute<any>(node.id, 'inputs', logicNodeInputs); // FIXME: I'm not sure why TS requires <any> to work here + dispatch(setQuerybuilderNodes(graphology.export())); + } + }; + + const createLeftHandles = useCallback( + (sideInputs: InputNode[], positionSide: Position, handleType: HandleType) => { + let numOfInputs = 0; + let ret = sideInputs.map((input, i) => { + let inputTextBox = null; + if ( + !connectionsToLeft.some( + (edge) => + edge?.attributes?.targetHandleData.nodeId === data.id && edge?.attributes?.targetHandleData.attributeName === input.name + ) + ) { + inputTextBox = ( + <Input + className={styles.logicInput} + // value={0} + style={{ top: -5, transform: `translateY(-${i * 20}%)` }} + // onChange={(e) => onInputUpdated(e.target.value, input)} + onKeyDown={(e) => { + if (e.key === 'Enter') onInputUpdated((e.target as HTMLInputElement).value, input, i); + }} + onBlur={(e) => onInputUpdated(e.target.value, input, i)} + /> + ); + numOfInputs++; + } + return ( + <Handle + type={handleType} + position={positionSide} + id={toHandleId({ ...defaultHandleData, attributeName: input.name, attributeType: input.type })} // TODO + key={input.name + input.type} + // style={{ top: `${((i + 0.8) / (side.length + 0.6)) * 120}%` }} + style={{ top: `${((i + 0.8) / (sideInputs.length + 0.6)) * 100}%` }} + className={styleHandleMap[input.type]} + > + {inputTextBox} + </Handle> + ); + }); + return { handles: ret, number: numOfInputs }; + }, + [node] + ); + const { handles: leftHandles, number: leftInputsNumber } = createLeftHandles(node.data.logic.inputs, Position.Left, 'target'); + + return ( + <div className={styles.logic}> + <span className={styles.logicSpan} style={{ height: `${Math.max(leftInputsNumber * 2.6, 1.5)}rem` }}> + { + <span> + {connectionsToLeft.map((e) => e?.attributes?.sourceHandleData.attributeName)}.{output.name} + </span> + } + {leftHandles} + {!!node.data.logic.output && ( + <Handle + type={'source'} + position={Position.Right} + id={toHandleId({ ...defaultHandleData, attributeName: output.name, attributeType: output.type })} // TODO + // style={{ top: `${((i + 0.8) / (side.length + 0.6)) * 100}%` }} + className={styleHandleMap?.[output.type]} + ></Handle> + )} + </span> + </div> + ); +} diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.module.scss b/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.module.scss deleted file mode 100644 index 2e259083f..000000000 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.module.scss +++ /dev/null @@ -1,55 +0,0 @@ -@import '../../querypills.module.scss'; - -.modifier { - color: '#181520'; - background-color: #d56a50; - border-radius: 5px; - font-family: monospace; - font-weight: bolder; - font-size: 11; - padding-top: 2; - padding-right: 5; - padding-bottom: 12; - padding-left: 5; -} -.modifierWrapper { - height: 6; - margin-left: 5; - margin-right: 5; - color: black; -} -.modifierInput { - float: right; - background-color: #ee917a; - border-radius: 2px; - padding-left: 2px; - padding-right: 2px; - display: flex; -} -.modifierSpan { - float: left; -} - -.matchModifierTypeSelect { - float: left; - background-color: rgba(255, 255, 255, 0.6); - border-radius: 2px; - text-align: center; - & select { - background: transparent; - border: none; - appearance: none; - font-family: monospace; - font-weight: bolder; - color: black; - font-size: 11; - } - & option { - font-family: monospace; - font-size: 11px; - } -} -.disable { - opacity: 1 !important; - pointer-events: none; -} diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.module.scss.d.ts deleted file mode 100644 index d22770e44..000000000 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.module.scss.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -declare const classNames: { - readonly 'react-flow__node': 'react-flow__node'; - readonly selected: 'selected'; - readonly entityWrapper: 'entityWrapper'; - readonly hidden: 'hidden'; - readonly 'react-flow__edges': 'react-flow__edges'; - readonly 'react-flow__edge-default': 'react-flow__edge-default'; - readonly handleConnectedFill: 'handleConnectedFill'; - readonly handleConnectedBorderRight: 'handleConnectedBorderRight'; - readonly handleConnectedBorderLeft: 'handleConnectedBorderLeft'; - readonly handleFunction: 'handleFunction'; - readonly modifier: 'modifier'; - readonly modifierWrapper: 'modifierWrapper'; - readonly modifierInput: 'modifierInput'; - readonly modifierSpan: 'modifierSpan'; - readonly matchModifierTypeSelect: 'matchModifierTypeSelect'; - readonly disable: 'disable'; -}; -export = classNames; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.tsx deleted file mode 100644 index 6041c775e..000000000 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/modifierpill.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -/* istanbul ignore file */ -/* The comment above was added so the code coverage wouldn't count this file towards code coverage. - * We do not test components/renderfunctions/styling files. - * See testing plan for more details.*/ -import React from 'react'; -import { Handle, NodeProps, Position } from 'reactflow'; -import Select from './select-modifier'; -import styles from './modifierpill.module.scss'; -import { ModifierData, ModifierNode } from '../../../graph-reactflow/model'; - -/** - * Component to render an entity flow element - * @param param0 Data of the flow element. - */ -export default function ModifierFlowElement({ data }: ModifierNode) { - return ( - <div className={styles.modifier}> - <div className={styles.modifierWrapper}> - <span className={styles.modifierInput}> - <span className={styles.modifierSpan}> - <Select data={data} /> - </span> - </span> - </div> - </div> - ); -} diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/mopdifierpill.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/mopdifierpill.stories.tsx deleted file mode 100644 index 4b4c7f263..000000000 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/mopdifierpill.stories.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import { Meta, StoryObj } from '@storybook/react'; -import ModifierPill from './modifierpill'; -import { configureStore } from '@reduxjs/toolkit'; -import { Provider } from 'react-redux'; -import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; - -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; -import { ReactFlowProvider } from 'reactflow'; -import { ModifierData } from '../../../model'; - -const Component: Meta<typeof ModifierPill> = { - /* 👇 The title prop is optional. - * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading - * to learn how to generate automatic titles - */ - title: 'Querybuilder/Pills/ModifierPill', - component: ModifierPill, - decorators: [ - (story) => ( - <Provider store={Mockstore}> - <GraphPolarisThemeProvider> - <ReactFlowProvider>{story()}</ReactFlowProvider> - </GraphPolarisThemeProvider> - </Provider> - ), - ], -}; - -export default Component; - -// A super-simple mock of a redux store -const Mockstore = configureStore({ - reducer: { - colorPaletteConfig: colorPaletteConfigSlice.reducer, - querybuilder: querybuilderSlice.reducer, - // schema: schemaSlice.reducer, - }, -}); - -// const Template = (args: any) => <EntityRFPill {...args} />; - -export const Default: StoryObj<{ data: ModifierData }> = { - args: { - data: { - type: 'SUM', - }, - }, -}; - -// Default.decorators = [ -// (story) => ( -// <Provider store={Mockstore}> -// <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider> -// </Provider> -// ), -// ]; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/select-modifier.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/select-modifier.tsx deleted file mode 100644 index 86ebf29a7..000000000 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/modifierpill/select-modifier.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -/* istanbul ignore file */ -/* The comment above was added so the code coverage wouldn't count this file towards code coverage. - * We do not test components/renderfunctions/styling files. - * See testing plan for more details.*/ -import React, { useState } from 'react'; -import { NodeProps } from 'reactflow'; -import styles from './modifierpill.module.scss'; -import { ModifierData } from '../../../graph-reactflow/model'; - -// Create style constant to prevent rereaction of styles - -/** - * The flow element for the modifier. - * @param param0 The data of the modifier flow element. - */ -export default function SelectModifier({ data }: { data: ModifierData }) { - const [disable, setDisable] = useState(true); - const [disClass, setDisClass] = useState<string>(styles.disable); - - /** - * Calculate the width of the select element based on the displayed value. - * @param str Input string. - * @returns String containg the length in css format. - */ - const calcSelectWidth = (str: string): string => { - if (str == '') return 1.5 + 'ch'; - return str.length + 1.5 + 'ch'; - }; - - /** Disable the select field */ - const disableSelect = (): void => { - setDisable(true); - setDisClass(styles.disable); - }; - - /** Enable the select field */ - const enableSelect = (): void => { - setDisable(false); - setDisClass(''); - }; - - /** - * Constant switch to append the right options for the select element based on the data.type. - * @returns {JSX.Element} Option list using React.Fragment as parent element. - */ - const list = (): JSX.Element => { - return ( - <React.Fragment> - <option color="black" value="COUNT"> - COUNT - </option> - <option value="SUM">SUM</option> - <option value="MIN">MIN</option> - <option value="MAX">MAX</option> - </React.Fragment> - ); - }; - - return ( - <div - className={styles.matchModifierTypeSelect} - onBlur={disableSelect} - onDoubleClick={enableSelect} - > - <select - style={{ - color: disable ? 'black' : 'black', - maxWidth: calcSelectWidth('COUNT'), - }} - name="operators" - className={disClass} - disabled={disable} - onChange={(e) => { - data.type = e.target.value; - e.target.style.maxWidth = calcSelectWidth(e.target.value); - }} - > - {list()} - </select> - </div> - ); -} diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-full_reactflow.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-full_reactflow.stories.tsx index 3527cfb33..5b6d0c231 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-full_reactflow.stories.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-full_reactflow.stories.tsx @@ -1,18 +1,12 @@ import React from 'react'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - setQuerybuilderNodes, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, setQuerybuilderNodes } from '@graphpolaris/shared/lib/data-access/store'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; import { configureStore } from '@reduxjs/toolkit'; import { Meta } from '@storybook/react'; import { Provider } from 'react-redux'; import { MultiGraph } from 'graphology'; import { QueryBuilder } from '../../../panel'; -import { QueryGraph } from '../../../model/graphology/model'; -import { circular } from 'graphology-layout'; -import { QueryMultiGraphology } from '../../../model'; +import { QueryElementTypes, QueryMultiGraphology } from '../../../model'; const Component: Meta<typeof QueryBuilder> = { component: QueryBuilder, @@ -34,17 +28,14 @@ const mockStore = configureStore({ }, }); const graph = new QueryMultiGraphology(); -graph.addPill2Graphology( - { - type: 'relation', - x: 140, - y: 140, - name: 'Relation Pill', - depth: { min: 0, max: 1 }, - fadeIn: false, - }, - '2' -); +graph.addPill2Graphology({ + id: '2', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Relation Pill', + depth: { min: 0, max: 1 }, +}); console.log(graph.export()); mockStore.dispatch(setQuerybuilderNodes(graph.export())); diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss.d.ts index a0a450e53..2938864f6 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss.d.ts +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.module.scss.d.ts @@ -1,4 +1,16 @@ declare const classNames: { + readonly handle: 'handle'; + readonly handle_logic: 'handle_logic'; + readonly handle_logic_duration: 'handle_logic_duration'; + readonly handle_logic_datetime: 'handle_logic_datetime'; + readonly handle_logic_time: 'handle_logic_time'; + readonly handle_logic_date: 'handle_logic_date'; + readonly handle_logic_bool: 'handle_logic_bool'; + readonly handle_logic_float: 'handle_logic_float'; + readonly handle_logic_int: 'handle_logic_int'; + readonly handle_logic_string: 'handle_logic_string'; + readonly handle_from_relation: 'handle_from_relation'; + readonly handle_to_relation: 'handle_to_relation'; readonly 'react-flow__node': 'react-flow__node'; readonly selected: 'selected'; readonly entityWrapper: 'entityWrapper'; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.stories.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.stories.tsx index 0401aba54..b288a8764 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.stories.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.stories.tsx @@ -5,11 +5,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { RelationData } from '../../../model'; @@ -50,7 +46,6 @@ export const Default: StoryObj<{ data: RelationData }> = { name: 'TestEntity', collection: 'test', depth: { min: 0, max: 1 }, - fadeIn: false, }, }, }; diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx index 073f594c9..323310f8a 100644 --- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx +++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx @@ -4,7 +4,7 @@ import { useTheme } from '@mui/material'; import { Handle, Position } from 'reactflow'; import styles from './relationpill.module.scss'; -import { RelationNode, Handles } from '../../../model'; +import { SchemaReactflowRelationNode, Handles, toHandleId } from '../../../model'; // export type RelationRFPillProps = { // data: { @@ -19,7 +19,8 @@ import { RelationNode, Handles } from '../../../model'; * Component to render a relation flow element * @param { FlowElement<RelationData>} param0 The data of a relation flow element. */ -export const RelationPill = memo(({ data }: RelationNode) => { +export const RelationPill = memo((node: SchemaReactflowRelationNode) => { + const data = node.data; // export default function RelationRFPill({ data }: { data: any }) { const theme = useTheme(); // console.log('RelationRFPill', data); @@ -47,10 +48,8 @@ export const RelationPill = memo(({ data }: RelationNode) => { } // Set to the correct width - if (maxRef.current) - maxRef.current.style.maxWidth = calcWidth(data.depth.max); - if (minRef.current) - minRef.current.style.maxWidth = calcWidth(data.depth.min); + if (maxRef.current) maxRef.current.style.maxWidth = calcWidth(data.depth.max); + if (minRef.current) minRef.current.style.maxWidth = calcWidth(data.depth.min); } }; @@ -66,18 +65,9 @@ export const RelationPill = memo(({ data }: RelationNode) => { }; return ( - <div - className={styles.relation} - style={{ backgroundColor: theme.palette.custom.elements.relation[0] }} - > - <div - className={styles.rightArrow} - style={{ borderLeftColor: theme.palette.custom.elements.relation[0] }} - ></div> - <div - className={styles.leftArrow} - style={{ borderRightColor: theme.palette.custom.elements.relation[0] }} - ></div> + <div className={styles.relation} style={{ backgroundColor: theme.palette.custom.elements.relation[0] }}> + <div className={styles.rightArrow} style={{ borderLeftColor: theme.palette.custom.elements.relation[0] }}></div> + <div className={styles.leftArrow} style={{ borderRightColor: theme.palette.custom.elements.relation[0] }}></div> {/* <span className={styles.relationTop} style={{ backgroundColor: theme.palette.custom.elements.relation[0] }} @@ -89,53 +79,39 @@ export const RelationPill = memo(({ data }: RelationNode) => { <div className={styles.relationWrapper}> <span className={styles.relationHandleFiller} - // style={{ transform: 'translate(-100px,0)' }} + // style={{ transform: 'translate(-100px,0)' }} > - <Handle - id={Handles.RelationLeft} - type="target" - position={Position.Left} - className={ - styles.relationHandleLeft + - ' ' + - (false ? styles.handleConnectedBorderLeft : '') - } - /> + {data.leftEntityHandleId && ( + <Handle + id={toHandleId(data.leftEntityHandleId)} + type="target" + position={Position.Left} + className={styles.relationHandleLeft + ' ' + (false ? styles.handleConnectedBorderLeft : '')} + /> + )} </span> - <span className={styles.relationHandleFiller}> + {/* <span className={styles.relationHandleFiller}> <Handle - id={Handles.ToAttribute} + id={getHandleId(data.name, data.type, Handles.ToAttribute, '')} type="target" position={Position.Left} - className={ - styles.relationHandleAttribute + - ' ' + - (false ? styles.handleConnectedFill : '') - } + className={styles.relationHandleAttribute + ' ' + (false ? styles.handleConnectedFill : '')} /> </span> <span className={styles.relationHandleFiller}> <Handle - id={Handles.ReceiveFunction} + id={getHandleId(data.name, data.type, Handles.ReceiveFunction, '')} type="target" position={Position.Left} - className={ - styles.relationHandleFunction + - ' ' + - (false ? styles.handleConnectedFill : '') - } + className={styles.relationHandleFunction + ' ' + (false ? styles.handleConnectedFill : '')} /> - </span> + </span> */} <div className={styles.relationDataWrapper}> <span className={styles.relationSpan}>{data?.name}</span> <span className={styles.relationInputHolder}> <span>[</span> <input - className={ - styles.relationInput + - ' ' + - (readOnlyMin ? styles.relationReadonly : '') - } + className={styles.relationInput + ' ' + (readOnlyMin ? styles.relationReadonly : '')} ref={minRef} type="string" min={0} @@ -144,9 +120,7 @@ export const RelationPill = memo(({ data }: RelationNode) => { value={data?.depth.min} onChange={(e) => { if (data != undefined) { - data.depth.min = isNumber(e.target.value) - ? parseInt(e.target.value) - : 0; + data.depth.min = isNumber(e.target.value) ? parseInt(e.target.value) : 0; e.target.style.maxWidth = calcWidth(data.depth.min); } }} @@ -164,11 +138,7 @@ export const RelationPill = memo(({ data }: RelationNode) => { ></input> <span>..</span> <input - className={ - styles.relationInput + - ' ' + - (readOnlyMax ? styles.relationReadonly : '') - } + className={styles.relationInput + ' ' + (readOnlyMax ? styles.relationReadonly : '')} ref={maxRef} type="string" min={0} @@ -177,9 +147,7 @@ export const RelationPill = memo(({ data }: RelationNode) => { value={data?.depth.max} onChange={(e) => { if (data != undefined) { - data.depth.max = isNumber(e.target.value) - ? parseInt(e.target.value) - : 0; + data.depth.max = isNumber(e.target.value) ? parseInt(e.target.value) : 0; e.target.style.maxWidth = calcWidth(data.depth.max); } }} @@ -199,16 +167,14 @@ export const RelationPill = memo(({ data }: RelationNode) => { </span> </div> <span className={styles.relationHandleFiller}> - <Handle - id={Handles.RelationRight} - type="target" - position={Position.Right} - className={ - styles.relationHandleRight + - ' ' + - (false ? styles.handleConnectedBorderRight : '') - } - /> + {data.rightEntityHandleId && ( + <Handle + id={toHandleId(data.rightEntityHandleId)} + type="source" + position={Position.Right} + className={styles.relationHandleRight + ' ' + (false ? styles.handleConnectedBorderRight : '')} + /> + )} </span> </div> </div> diff --git a/libs/shared/lib/querybuilder/pills/dragging/dragAttribute.ts b/libs/shared/lib/querybuilder/pills/dragging/dragAttribute.ts index df89ab57d..00f823e6d 100644 --- a/libs/shared/lib/querybuilder/pills/dragging/dragAttribute.ts +++ b/libs/shared/lib/querybuilder/pills/dragging/dragAttribute.ts @@ -7,24 +7,17 @@ export function DragAttributePillStarted(id: string, nodes: MultiGraph) { es.forEach((e) => nodes.dropEdge(e)); } -export function DragAttributePill( - id: string, - nodes: MultiGraph, - dx: number, - dy: number -) { +export function DragAttributePill(id: string, nodes: MultiGraph, dx: number, dy: number) { // Get the closes entity or relation node const closestNode = GetClosestPill(id, nodes, ['entity', 'relation']); // If we found one, highlight it by adding an attribute - if (closestNode) - nodes.setNodeAttribute(closestNode, 'suggestedForConnection', true); + if (closestNode) nodes.setNodeAttribute(closestNode, 'suggestedForConnection', true); } export function DragAttibutePillStopped(id: string, nodes: MultiGraph) { // If there is currently a node with the suggestedForConnection attribute // connect this attribute to it nodes.forEachNode((node, { suggestedForConnection }) => { - if (suggestedForConnection) - nodes.addEdge(id, node, { type: 'attribute_connection' }); + if (suggestedForConnection) nodes.addEdge(id, node, { type: 'attribute_connection' }); }); } diff --git a/libs/shared/lib/querybuilder/pills/dragging/dragAttributesAlong.ts b/libs/shared/lib/querybuilder/pills/dragging/dragAttributesAlong.ts index 56697ab3f..7a8a3220a 100644 --- a/libs/shared/lib/querybuilder/pills/dragging/dragAttributesAlong.ts +++ b/libs/shared/lib/querybuilder/pills/dragging/dragAttributesAlong.ts @@ -8,12 +8,7 @@ import Graph from 'graphology'; * @param dy The change in y * @returns True if any attribute positions were changed */ -export function DragAttributesAlong( - id: string, - nodes: Graph, - dx: number, - dy: number -): boolean { +export function DragAttributesAlong(id: string, nodes: Graph, dx: number, dy: number): boolean { let didChangeAttributes = false; nodes.forEachInNeighbor(id, (nb) => { if (nodes.getNodeAttribute(nb, 'type') == 'attribute') { diff --git a/libs/shared/lib/querybuilder/pills/dragging/dragEntity.ts b/libs/shared/lib/querybuilder/pills/dragging/dragEntity.ts index 171124ac9..61acf0615 100644 --- a/libs/shared/lib/querybuilder/pills/dragging/dragEntity.ts +++ b/libs/shared/lib/querybuilder/pills/dragging/dragEntity.ts @@ -4,12 +4,7 @@ export function DragEntityPillStarted(id: string, nodes: MultiGraph) { // Started dragging entity usecase } -export function DragEntityPill( - id: string, - nodes: MultiGraph, - dx: number, - dy: number -) { +export function DragEntityPill(id: string, nodes: MultiGraph, dx: number, dy: number) { // Code for dragging an entity pill should go here } diff --git a/libs/shared/lib/querybuilder/pills/dragging/dragPill.ts b/libs/shared/lib/querybuilder/pills/dragging/dragPill.ts index 728c5f0e3..e88fc1950 100644 --- a/libs/shared/lib/querybuilder/pills/dragging/dragPill.ts +++ b/libs/shared/lib/querybuilder/pills/dragging/dragPill.ts @@ -1,21 +1,9 @@ import { MultiGraph } from 'graphology'; import { XYPosition } from 'reactflow'; -import { - DragAttibutePillStopped, - DragAttributePill, - DragAttributePillStarted, -} from './dragAttribute'; +import { DragAttibutePillStopped, DragAttributePill, DragAttributePillStarted } from './dragAttribute'; import { DragAttributesAlong } from './dragAttributesAlong'; -import { - DragEntityPill, - DragEntityPillStarted, - DragEntityPillStopped, -} from './dragEntity'; -import { - DragRelationPill, - DragRelationPillStarted, - DragRelationPillStopped, -} from './dragRelation'; +import { DragEntityPill, DragEntityPillStarted, DragEntityPillStopped } from './dragEntity'; +import { DragRelationPill, DragRelationPillStarted, DragRelationPillStopped } from './dragRelation'; export function dragPillStarted(id: string, nodes: MultiGraph) { switch (nodes.getNodeAttribute(id, 'type')) { @@ -39,21 +27,13 @@ export function dragPillStarted(id: string, nodes: MultiGraph) { * @param dy Delta y * @param position The already updated positioning (dx dy are already applied) */ -export function movePillTo( - id: string, - nodes: MultiGraph, - dx: number, - dy: number, - position: XYPosition -) { +export function movePillTo(id: string, nodes: MultiGraph, dx: number, dy: number, position: XYPosition) { // Update the position of the node in the graphology object nodes.setNodeAttribute(id, 'x', position.x); nodes.setNodeAttribute(id, 'y', position.y); // Remove the highlighted attribute from each node - nodes.forEachNode((node) => - nodes.removeNodeAttribute(node, 'suggestedForConnection') - ); + nodes.forEachNode((node) => nodes.removeNodeAttribute(node, 'suggestedForConnection')); switch (nodes.getNodeAttribute(id, 'type')) { case 'attribute': @@ -84,7 +64,5 @@ export function dragPillStopped(id: string, nodes: MultiGraph) { } // Remove all suggestedForConnection attributes - nodes.forEachNode((node) => - nodes.removeNodeAttribute(node, 'suggestedForConnection') - ); + nodes.forEachNode((node) => nodes.removeNodeAttribute(node, 'suggestedForConnection')); } diff --git a/libs/shared/lib/querybuilder/pills/dragging/dragRelation.ts b/libs/shared/lib/querybuilder/pills/dragging/dragRelation.ts index 12bf51961..470805411 100644 --- a/libs/shared/lib/querybuilder/pills/dragging/dragRelation.ts +++ b/libs/shared/lib/querybuilder/pills/dragging/dragRelation.ts @@ -4,12 +4,7 @@ export function DragRelationPillStarted(id: string, nodes: MultiGraph) { // Started dragging relation usecase } -export function DragRelationPill( - id: string, - nodes: MultiGraph, - dx: number, - dy: number -) { +export function DragRelationPill(id: string, nodes: MultiGraph, dx: number, dy: number) { // Code for dragging an relation pill should go here } diff --git a/libs/shared/lib/querybuilder/pills/dragging/getClosestPill.ts b/libs/shared/lib/querybuilder/pills/dragging/getClosestPill.ts index 0a3fd4487..16edb31a9 100644 --- a/libs/shared/lib/querybuilder/pills/dragging/getClosestPill.ts +++ b/libs/shared/lib/querybuilder/pills/dragging/getClosestPill.ts @@ -8,12 +8,7 @@ import { MultiGraph } from 'graphology'; * @param maxDistance The maximum distance * @returns the closest node if within range */ -export function GetClosestPill( - id: string, - nodes: MultiGraph, - allowedNodeTypes: string[], - maxDistance = 150 -): string | undefined { +export function GetClosestPill(id: string, nodes: MultiGraph, allowedNodeTypes: string[], maxDistance = 150): string | undefined { const { x, y, w, h } = nodes.getNodeAttributes(id); const center: { x: number; y: number } = { x: x + w / 2, y: y + h / 2 }; diff --git a/libs/shared/lib/querybuilder/pills/handles.module.scss b/libs/shared/lib/querybuilder/pills/handles.module.scss new file mode 100644 index 000000000..a39b5f8ea --- /dev/null +++ b/libs/shared/lib/querybuilder/pills/handles.module.scss @@ -0,0 +1,57 @@ +.handle { + z-index: 1; + height: 8px !important; + width: 8px !important; +} + +.handle_to_relation { + @extend .handle; + border-radius: 1px !important; + top: 0.6rem !important; + background: rgb(39, 131, 145) !important; +} + +.handle_from_relation { + @extend .handle; + border-radius: 1px !important; + // left: 10px !important; + top: 0.55rem !important; + // background: rgba(0, 0, 0, 0.3) !important; +} + +.handle_logic { + @extend .handle; + + &_string { + @extend .handle_logic; + background: rgb(145, 39, 39) !important; + } + &_int { + @extend .handle_logic; + background: rgb(117, 20, 117) !important; + } + &_float { + @extend .handle_logic; + background: rgb(23, 117, 20) !important; + } + &_bool { + @extend .handle_logic; + background: rgb(77, 20, 212) !important; + } + &_date { + @extend .handle_logic; + background: rgb(77, 20, 212) !important; + } + &_time { + @extend .handle_logic; + background: rgb(77, 20, 212) !important; + } + &_datetime { + @extend .handle_logic; + background: rgb(77, 20, 212) !important; + } + &_duration { + @extend .handle_logic; + background: rgb(77, 20, 212) !important; + } +} diff --git a/libs/shared/lib/querybuilder/pills/handles.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/handles.module.scss.d.ts new file mode 100644 index 000000000..78139bdb5 --- /dev/null +++ b/libs/shared/lib/querybuilder/pills/handles.module.scss.d.ts @@ -0,0 +1,15 @@ +declare const classNames: { + readonly handle: 'handle'; + readonly handle_logic: 'handle_logic'; + readonly handle_logic_duration: 'handle_logic_duration'; + readonly handle_logic_datetime: 'handle_logic_datetime'; + readonly handle_logic_time: 'handle_logic_time'; + readonly handle_logic_date: 'handle_logic_date'; + readonly handle_logic_bool: 'handle_logic_bool'; + readonly handle_logic_float: 'handle_logic_float'; + readonly handle_logic_int: 'handle_logic_int'; + readonly handle_logic_string: 'handle_logic_string'; + readonly handle_from_relation: 'handle_from_relation'; + readonly handle_to_relation: 'handle_to_relation'; +}; +export = classNames; diff --git a/libs/shared/lib/querybuilder/pills/querypills.module.scss b/libs/shared/lib/querybuilder/pills/querypills.module.scss index 2cb3fedf0..14cb45860 100644 --- a/libs/shared/lib/querybuilder/pills/querypills.module.scss +++ b/libs/shared/lib/querybuilder/pills/querypills.module.scss @@ -1,3 +1,5 @@ +@import './handles.module.scss'; + .react-flow__node { &.selected { border: #000 solid 1px; diff --git a/libs/shared/lib/querybuilder/pills/querypills.module.scss.d.ts b/libs/shared/lib/querybuilder/pills/querypills.module.scss.d.ts index a040b2068..442165543 100644 --- a/libs/shared/lib/querybuilder/pills/querypills.module.scss.d.ts +++ b/libs/shared/lib/querybuilder/pills/querypills.module.scss.d.ts @@ -1,4 +1,16 @@ declare const classNames: { + readonly handle: 'handle'; + readonly handle_logic: 'handle_logic'; + readonly handle_logic_duration: 'handle_logic_duration'; + readonly handle_logic_datetime: 'handle_logic_datetime'; + readonly handle_logic_time: 'handle_logic_time'; + readonly handle_logic_date: 'handle_logic_date'; + readonly handle_logic_bool: 'handle_logic_bool'; + readonly handle_logic_float: 'handle_logic_float'; + readonly handle_logic_int: 'handle_logic_int'; + readonly handle_logic_string: 'handle_logic_string'; + readonly handle_from_relation: 'handle_from_relation'; + readonly handle_to_relation: 'handle_to_relation'; readonly 'react-flow__node': 'react-flow__node'; readonly selected: 'selected'; readonly entityWrapper: 'entityWrapper'; diff --git a/libs/shared/lib/querybuilder/pills/utils.ts b/libs/shared/lib/querybuilder/pills/utils.ts new file mode 100644 index 000000000..02098af66 --- /dev/null +++ b/libs/shared/lib/querybuilder/pills/utils.ts @@ -0,0 +1,13 @@ +import { InputNodeType } from '../model/logic/general'; +import styles from './querypills.module.scss'; + +export const styleHandleMap: Record<InputNodeType, string> = { + string: styles.handle_logic_string, + int: styles.handle_logic_int, + float: styles.handle_logic_float, + bool: styles.handle_logic_bool, + date: styles.handle_logic_date, + time: styles.handle_logic_time, + datetime: styles.handle_logic_datetime, + duration: styles.handle_logic_duration, +}; diff --git a/libs/shared/lib/querybuilder/query-utils/index.ts b/libs/shared/lib/querybuilder/query-utils/index.ts index ca1c6a26a..0477263bf 100644 --- a/libs/shared/lib/querybuilder/query-utils/index.ts +++ b/libs/shared/lib/querybuilder/query-utils/index.ts @@ -1,2 +1,2 @@ -export * from './query-utils' -export * from '../model/BackendQueryFormat' \ No newline at end of file +export * from './query2backend'; +export * from '../model/BackendQueryFormat'; diff --git a/libs/shared/lib/querybuilder/query-utils/query-utils.ts b/libs/shared/lib/querybuilder/query-utils/query-utils.ts deleted file mode 100644 index 8e4a47ec8..000000000 --- a/libs/shared/lib/querybuilder/query-utils/query-utils.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { EntityNodeAttributes, RelationNodeAttributes } from "../model/graphology/model"; -import { QueryMultiGraph } from "../model/graphology/utils"; -import { BackendQueryFormat } from "../model/BackendQueryFormat"; -import { Handles, QueryElementTypes } from "../model"; - -/** - * Converts the ReactFlow query to a json data structure to send the query to the backend. - * @returns {BackendQueryFormat} A JSON object in the `JSONFormat`. - */ -export function Query2BackendQuery(databaseName: string, graph: QueryMultiGraph): BackendQueryFormat { - // dict of nodes per type - const entities = graph.nodes.filter(n => n.attributes?.type === QueryElementTypes.Entity) - .map((n) => ({ name: n.attributes!.name, ID: Number(n.key), constraints: [] })); - - const relations = graph.nodes.filter(n => n.attributes?.type === QueryElementTypes.Relation) - .map((n) => { - const attributes = n.attributes as RelationNodeAttributes; - const leftEdge = graph.edges.filter(e => (e.target === n.key && e.attributes!.targetHandle === Handles.RelationLeft))?.[0]; - const rightEdge = graph.edges.filter(e => (e.target === n.key && e.attributes!.targetHandle === Handles.RelationRight))?.[0]; - if (!leftEdge || !rightEdge) throw Error('Malformed Graph! One or more edges of a relation node do not exist'); - - const leftType = graph.nodes.filter(n => n.key === leftEdge.source)?.[0]?.attributes?.type; - const rightType = graph.nodes.filter(n => n.key === rightEdge.source)?.[0]?.attributes?.type; - if (!rightType || !leftType) throw Error('Malformed Graph! edges must have a type'); - - return { - name: attributes!.collection, ID: Number(n.key), depth: attributes!.depth, - fromType: leftType, fromID: Number(leftEdge.source), // need to treat function==groupby - toType: rightType, toID: Number(rightEdge.source), // need to treat function==groupby - constraints: [], - } - }); - - // TODO: Groubby not implemented (constraints) - const result: BackendQueryFormat = { - databaseName: databaseName, - return: { entities: entities.map(e => e.ID), relations: relations.map(r => r.ID), groupBys: [] }, - entities: entities, - relations: relations, - groupBys: [], // TODO - machineLearning: [], // TODO - limit: 5000, // TODO - }; - console.log(result, graph); - - - //note that attribute nodes are not processed here, but are detected if they are connected to any entity/relation/function node - - //add nodes to JSON query - // entityNodes.forEach((node) => { - // this.AddEntityToJsonObject(result, node); - // }); - // relationNodes.forEach((node) => { - // this.AddRelationToJsonObject(result, node); - // }); - - // functionNodes.forEach((functionNode: FunctionNode) => { - // switch (functionNode.data?.functionType) { - // case FunctionTypes.GroupBy: - // this.AddGroupByToJsonObject(result, functionNode); - // break; - // case FunctionTypes.link: - // this.AddLinkPredictionToJsonObject(result, functionNode); - // break; - // case FunctionTypes.communityDetection: - // this.AddCommunityDetectionToJsonObject(result, functionNode); - // break; - // case FunctionTypes.centrality: - // this.AddCentralityToJsonObject(result, functionNode); - // break; - // case FunctionTypes.shortestPath: - // this.addShortestPathToJsonOBject(result, functionNode); - // break; - // } - // }); - return result; -} \ No newline at end of file diff --git a/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts b/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts new file mode 100644 index 000000000..e99347c6f --- /dev/null +++ b/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts @@ -0,0 +1,960 @@ +import { describe, expect, it } from 'vitest'; +// import { Query2BackendQuery } from './query-utils'; +import { BackendQueryFormat, LogicNodeAttributes, MathFilters, MathFunctions, QueryElementTypes } from '../model'; +import { QueryMultiGraphology } from '../model/graphology/utils'; +import { MathAggregationTypes, MathFilterTypes, MathFunctionTypes } from '../model/logic/general'; +import { Query2BackendQuery, calculateQueryLogic } from './query2backend'; +import { SerializedNode } from 'graphology-types'; +import { MathAggregations } from '../model/logic/mathAggregations'; + +const defaultQuery = { + databaseName: 'database', + query: [], + limit: 500, + return: ['*'], +}; + +describe('QueryUtils Entity and Relations', () => { + it('should create an instance', () => { + const graph = new QueryMultiGraphology(); + expect(true).toBeTruthy(); + }); + + it('should run simple query translation', () => { + const graph = new QueryMultiGraphology(); + + const entity1 = graph.addPill2Graphology({ + id: '0', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 1', + }); + const entity2 = graph.addPill2Graphology({ + id: '10', + type: QueryElementTypes.Entity, + x: 200, + y: 200, + name: 'Airport 2', + }); + + const relation1 = graph.addPill2Graphology({ + id: '1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(entity1, relation1); + graph.addEdge2Graphology(relation1, entity2); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: '0', + label: 'Airport 1', + relation: { + ID: '1', + label: 'Flight between airports', + direction: 'TO', + node: { + ID: '10', + label: 'Airport 2', + relation: undefined, + }, + }, + }, + }, + ], + }; + + let ret = Query2BackendQuery('database', graph.export()); + expect(ret).toMatchObject(expected); + }); + + it('should run multiple path query translation', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1, { type: 'connection' }); + graph.addEdge2Graphology(r1, e2, { type: 'connection' }); + + const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12' }); + const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22' }); + + const r12 = graph.addPill2Graphology({ + id: 'r12', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports 2', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e12, r12); + graph.addEdge2Graphology(r12, e22); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e0', + label: 'Airport 1', + relation: { + ID: 'r1', + label: 'Flight between airports', + direction: 'TO', + node: { + ID: 'e1', + label: 'Airport 2', + relation: undefined, + }, + }, + }, + }, + { + ID: 'path_1', + node: { + ID: 'e12', + label: 'Airport 12', + relation: { + ID: 'r12', + label: 'Flight between airports 2', + direction: 'TO', + node: { + ID: 'e22', + label: 'Airport 22', + relation: undefined, + }, + }, + }, + }, + ], + }; + + let ret = Query2BackendQuery('database', graph.export()); + expect(ret).toMatchObject(expected); + }); + + it('should run one relation multiple entity query translation', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2' }); + const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12' }); + const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1); + graph.addEdge2Graphology(r1, e2); + + graph.addEdge2Graphology(e12, r1); + graph.addEdge2Graphology(r1, e22); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e2', label: 'Airport 2' } }, + }, + }, + { + ID: 'path_1', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e22', label: 'Airport 22' } }, + }, + }, + { + ID: 'path_2', + node: { + ID: 'e12', + label: 'Airport 12', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e2', label: 'Airport 2' } }, + }, + }, + { + ID: 'path_3', + node: { + ID: 'e12', + label: 'Airport 12', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e22', label: 'Airport 22' } }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run lone entities query translation', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2' }); + const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12' }); + const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1); + graph.addEdge2Graphology(r1, e2); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e0', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e1', label: 'Airport 2' } }, + }, + }, + { ID: 'path_1', node: { ID: 'e12', label: 'Airport 12' } }, + { ID: 'path_2', node: { ID: 'e22', label: 'Airport 22' } }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run lone relations query translation', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + const r2 = graph.addPill2Graphology({ + id: 'r2', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports 2', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1); + graph.addEdge2Graphology(r1, e2); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e0', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e1', label: 'Airport 2' } }, + }, + }, + { + ID: 'path_1', + node: { relation: { ID: 'r2', label: 'Flight between airports 2', direction: 'TO', node: {} } }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run relation only left side connected query translation', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e0', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: {} }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run relation only right side connected query translation', () => { + const graph = new QueryMultiGraphology(); + + const e2 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(r1, e2); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e0', label: 'Airport 2' } }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run entity and relations multi connection', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + const r2 = graph.addPill2Graphology({ + id: 'r2', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports 2', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1); + graph.addEdge2Graphology(e1, r2); + graph.addEdge2Graphology(r1, e2); + graph.addEdge2Graphology(r2, e2); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e2', label: 'Airport 2' } }, + }, + }, + { + ID: 'path_1', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { ID: 'r2', label: 'Flight between airports 2', direction: 'TO', node: { ID: 'e2', label: 'Airport 2' } }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run in case of loops', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1, { type: 'connection' }); + graph.addEdge2Graphology(r1, e1, { type: 'connection' }); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e1', label: 'Airport 1' } }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run in case of loops and regular', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1, { type: 'connection' }); + graph.addEdge2Graphology(r1, e1, { type: 'connection' }); + graph.addEdge2Graphology(r1, e2, { type: 'connection' }); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { + ID: 'r1', + label: 'Flight between airports', + direction: 'TO', + node: { ID: 'e2', label: 'Airport 1', relation: undefined }, + }, + }, + }, + { + ID: 'path_1', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e1', label: 'Airport 1' } }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run in case of loops and regular left', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(e1, r1, { type: 'connection' }); + graph.addEdge2Graphology(e2, r1, { type: 'connection' }); + graph.addEdge2Graphology(r1, e1, { type: 'connection' }); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e1', label: 'Airport 1' } }, + }, + }, + { + ID: 'path_1', + node: { + ID: 'e2', + label: 'Airport 1', + relation: { ID: 'r1', label: 'Flight between airports', direction: 'TO', node: { ID: 'e1', label: 'Airport 1' } }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run in case of direct entity connection', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + + graph.addEdge2Graphology(e1, e2, { type: 'connection' }); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { direction: 'TO', node: { ID: 'e2', label: 'Airport 1' } }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run in case of direct relation connection', () => { + const graph = new QueryMultiGraphology(); + + const r1 = graph.addPill2Graphology({ + id: 'r1', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + const r2 = graph.addPill2Graphology({ + id: 'r2', + type: QueryElementTypes.Relation, + x: 140, + y: 140, + name: 'Flight between airports 2', + collection: 'Relation Pill', + depth: { min: 0, max: 1 }, + }); + + graph.addEdge2Graphology(r1, r2, { type: 'connection' }); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + relation: { + ID: 'r1', + label: 'Flight between airports', + direction: 'TO', + node: { relation: { ID: 'r2', label: 'Flight between airports 2', direction: 'TO', node: {} } }, + }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run in case of entity only loop', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + + graph.addEdge2Graphology(e1, e2, { type: 'connection' }); + graph.addEdge2Graphology(e2, e1, { type: 'connection' }); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: { direction: 'TO', node: { ID: 'e2', label: 'Airport 1' } }, + }, + }, + { + ID: 'path_1', + node: { + ID: 'e2', + label: 'Airport 1', + relation: { direction: 'TO', node: { ID: 'e1', label: 'Airport 1' } }, + }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run in case of entity only self loop', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1' }); + + graph.addEdge2Graphology(e1, e1, { type: 'connection' }); + + const expected = { + ...defaultQuery, + query: [ + { + ID: 'path_0', + node: { ID: 'e1', label: 'Airport 1', relation: { direction: 'TO', node: { ID: 'e1', label: 'Airport 1' } } }, + }, + ], + }; + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); +}); + +describe('QueryUtils calculateQueryLogic', () => { + it('should run simple logic', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology( + { + id: 'e1', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 1', + }, + [{ name: 'age', type: 'string' }] + ); + + const l1 = graph.addLogicPill2Graphology({ + id: 'l1', + type: QueryElementTypes.Logic, + x: 100, + y: 100, + name: 'Logic 1', + logic: MathFilters[MathFilterTypes.EQUAL], + }); + + graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' }); + const graphExport = graph.export(); + let logics = graphExport.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Logic) as SerializedNode<LogicNodeAttributes>[]; + + const ret = calculateQueryLogic(logics[0], graphExport, logics); + console.log(ret); + }); +}); + +describe('QueryUtils with Logic', () => { + it('should run simple query with logic', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology( + { + id: 'e1', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 1', + }, + [{ name: 'age', type: 'string' }] + ); + + const l1 = graph.addLogicPill2Graphology({ + id: 'l1', + type: QueryElementTypes.Logic, + x: 100, + y: 100, + name: 'Logic 1', + logic: MathFilters[MathFilterTypes.EQUAL], + }); + + graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' }); + + const expected: BackendQueryFormat = { + ...defaultQuery, + logic: ['==', '@e1.age', 0], + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: undefined, + }, + }, + ], + }; + + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should run add age and filter', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology( + { + id: 'e1', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 1', + }, + [{ name: 'age', type: 'string' }] + ); + + const e2 = graph.addPill2Graphology( + { + id: 'e2', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 2', + }, + [{ name: 'age', type: 'string' }] + ); + + const l1 = graph.addLogicPill2Graphology({ + id: 'l1', + type: QueryElementTypes.Logic, + x: 100, + y: 100, + name: 'Filter EQ', + logic: MathFilters[MathFilterTypes.EQUAL], + }); + + const l2 = graph.addLogicPill2Graphology({ + id: 'l2', + type: QueryElementTypes.Logic, + x: 100, + y: 100, + name: 'Logic ADD', + logic: MathFunctions[MathFunctionTypes.ADD], + }); + + graph.addEdge2Graphology(e1, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' }); + graph.addEdge2Graphology(e2, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '2' }); + graph.addEdge2Graphology(l2, l1, { type: 'connection' }, { sourceHandleName: MathFilterTypes.EQUAL, targetHandleName: '1' }); + + const expected: BackendQueryFormat = { + ...defaultQuery, + logic: ['==', ['+', '@e1.age', '@e2.age'], 0], + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: undefined, + }, + }, + { + ID: 'path_1', + node: { + ID: 'e2', + label: 'Airport 2', + relation: undefined, + }, + }, + ], + }; + + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should handle average logic', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology( + { + id: 'e1', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 1', + }, + [{ name: 'age', type: 'string' }] + ); + + const l1 = graph.addLogicPill2Graphology({ + id: 'l1', + type: QueryElementTypes.Logic, + x: 100, + y: 100, + name: 'Logic LT', + logic: MathFilters[MathFilterTypes.LESS_THAN], + }); + + const l2 = graph.addLogicPill2Graphology({ + id: 'l2', + type: QueryElementTypes.Logic, + x: 100, + y: 100, + name: 'Logic average', + logic: MathAggregations[MathAggregationTypes.AVG], + }); + + graph.addEdge2Graphology(e1, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' }); + graph.addEdge2Graphology(l2, l1, { type: 'connection' }, { sourceHandleName: MathAggregationTypes.AVG, targetHandleName: '1' }); + + const expected: BackendQueryFormat = { + ...defaultQuery, + logic: ['<', ['Avg', '@e1.age'], 0], + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: undefined, + }, + }, + ], + }; + + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); + + it('should allow custom values in logic pills', () => { + const graph = new QueryMultiGraphology(); + + const e1 = graph.addPill2Graphology( + { + id: 'e1', + type: QueryElementTypes.Entity, + x: 100, + y: 100, + name: 'Airport 1', + }, + [{ name: 'age', type: 'string' }] + ); + + const l1 = graph.addLogicPill2Graphology( + { + id: 'l1', + type: QueryElementTypes.Logic, + x: 100, + y: 100, + name: 'Logic LT', + logic: MathFilters[MathFilterTypes.LESS_THAN], + }, + { '2': 5 } + ); + + graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' }); + + const expected: BackendQueryFormat = { + ...defaultQuery, + logic: ['<', '@e1.age', 5], + query: [ + { + ID: 'path_0', + node: { + ID: 'e1', + label: 'Airport 1', + relation: undefined, + }, + }, + ], + }; + + let ret = Query2BackendQuery('database', graph.export()); + // console.log(JSON.stringify(ret, null, 2)); + expect(ret).toMatchObject(expected); + }); +}); diff --git a/libs/shared/lib/querybuilder/query-utils/query2backend.ts b/libs/shared/lib/querybuilder/query-utils/query2backend.ts new file mode 100644 index 000000000..f4f74943d --- /dev/null +++ b/libs/shared/lib/querybuilder/query-utils/query2backend.ts @@ -0,0 +1,311 @@ +import { EntityNodeAttributes, LogicNodeAttributes, QueryGraphNodes, RelationNodeAttributes } from '../model/graphology/model'; +import { QueryMultiGraph } from '../model/graphology/utils'; +import { BackendQueryFormat, NodeStruct, QueryStruct, RelationStruct } from '../model/BackendQueryFormat'; +import { Handles, QueryElementTypes, toHandleData } from '../model'; +import { get } from 'http'; +import { SerializedEdge, SerializedNode } from 'graphology-types'; +import { G } from 'vitest/dist/types-fafda418'; +import { hasCycle } from 'graphology-dag'; +import Graph, { MultiGraph } from 'graphology'; +import { allSimplePaths } from 'graphology-simple-path'; +import { AllLogicStatement, ReferenceStatement } from '../model/logic/general'; + +// export type QueryI { + +// } + +export type Query2BackendQueryOptions = { + limit?: number; +}; + +// Chunk extraction: traverse graph to find all paths of logic between relations and entities +const traverseEntityRelationPaths = ( + node: SerializedNode<QueryGraphNodes>, + paths: QueryGraphNodes[][], + currentIdx: number, + graph: QueryMultiGraph, + entities: SerializedNode<EntityNodeAttributes>[], + relations: SerializedNode<RelationNodeAttributes>[] +): number => { + if (!node?.attributes) throw Error('Malformed Graph! Node has no attributes'); + // console.log(paths); + + if (!paths?.[currentIdx]) { + // console.log('new path'); + paths.push([]); + if (node.attributes.type === QueryElementTypes.Relation) { + paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.y }); + } + } else if (paths[currentIdx].length > 0) { + const lastNode = paths[currentIdx][paths[currentIdx].length - 1]; + if (lastNode.type === node.attributes.type) { + if (lastNode.type === QueryElementTypes.Entity) { + paths[currentIdx].push({ type: QueryElementTypes.Relation, x: node.attributes.x, y: node.attributes.x }); + } else { + paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.x }); + } + } + } + paths[currentIdx].push(node.attributes); + // if ( + // (node.attributes.type === QueryElementTypes.Entity || node.attributes.type === QueryElementTypes.Relation) && + // paths[currentIdx].some((n) => n.type === QueryElementTypes.Logic) + // ) { + // return 0; + // } + + // const rightHandle = + // node.type === QueryElementTypes.Entity + // ? (node as EntityNodeAttributes)?.rightRelationHandleId + // : (node as RelationNodeAttributes)?.rightEntityHandleId; + + const edges = graph.edges.filter( + (n) => + n?.attributes?.sourceHandleData.nodeType !== QueryElementTypes.Logic && + n?.attributes?.targetHandleData.nodeType !== QueryElementTypes.Logic + ); + let connections = edges.filter((e) => e.source === node.key); + if (connections.length === 0) { + if (node.attributes.type === QueryElementTypes.Relation) { + paths[currentIdx].push({ type: QueryElementTypes.Entity, x: node.attributes.x, y: node.attributes.x }); + } + return 0; + } + // console.log('connections', connections); + + const nodesToRight = connections + .map((c, i) => { + const rightNodeHandleData = c.attributes?.targetHandleData; + if (!rightNodeHandleData) throw Error('Malformed Graph! One or more edges of a relation node do not exist'); + + // console.log('nodesToRight', c); + // console.log('entities', entities); + if (paths[currentIdx].length > 10 || currentIdx > 10) + throw Error('Malformed Graph! One or more edges of a relation node do not exist'); + const rightNode = + rightNodeHandleData.nodeType === QueryElementTypes.Entity + ? entities.find((r) => r.key === c.target) + : relations.find((r) => r.key === c.target); + return rightNode; + }) + .filter((n) => n !== undefined) as SerializedNode<QueryGraphNodes>[]; + + let chunkOffset = 0; + let pathBeforeTraversing = [...paths[currentIdx]]; + nodesToRight.forEach((rightNode, i) => { + if (i > 0) { + paths.push([...pathBeforeTraversing]); // clone previous path in case of branching + } + chunkOffset += traverseEntityRelationPaths(rightNode, paths, currentIdx + i + chunkOffset, graph, entities, relations); + }); + + return chunkOffset + nodesToRight.length - 1; // offset +}; + +export function calculateQueryLogic( + node: SerializedNode<LogicNodeAttributes>, + graph: QueryMultiGraph, + logics: SerializedNode<LogicNodeAttributes>[] +): AllLogicStatement { + if (!node?.attributes) throw Error('Malformed Graph! Node has no attributes'); + let connectionsToLeft = graph.edges.filter((e) => e.target === node.key); + + let ret = [...node.attributes.logic.logic].map((l) => { + if (typeof l !== 'string') throw Error('Malformed Graph! Logic node has no logic'); + if (!node.attributes) throw Error('Malformed Graph! Logic node has no attributes'); + + if (l.includes('@')) { + // @ means it needs to fetch data from connection + const inputRefIdx = node.attributes.logic.inputs.findIndex((input, i) => input.name === l.replace('@', '')); // fetches the corresponding element from input definition + const inputRef = node.attributes.logic.inputs[inputRefIdx]; + if (!inputRef) throw Error('Malformed Graph! Logic node has incorrect input reference'); + + const connectionToInputRef = connectionsToLeft.find((c) => c?.attributes?.targetHandleData.attributeName === inputRef.name); + if (!connectionToInputRef) { + // Not connected, search for set or default value + let val = node.attributes.inputs?.[inputRef.name] || inputRef.default; + if (val && inputRef.type === 'string') val = `\"${val}\"`; + console.log('val', val, inputRef, node); + return val; + } else if (connectionToInputRef.attributes?.sourceHandleData.nodeType === QueryElementTypes.Logic) { + // Is connected to another logic node + const leftLogic = logics.find((r) => r.key === connectionToInputRef.attributes?.sourceHandleData.nodeId); + if (!leftLogic) throw Error('Malformed Graph! Logic node is connected but has no logic data'); + return calculateQueryLogic(leftLogic, graph, logics); + } else { + if (!connectionToInputRef.attributes?.sourceHandleData) + throw Error('Malformed Graph! Logic node is connected but has no sourceHandleData'); + // Is connected to entity or relation node + return `@${connectionToInputRef.attributes.sourceHandleData.nodeId}.${connectionToInputRef.attributes.sourceHandleData.attributeName}`; + } + } else { + return l; + } + }); + return ret as AllLogicStatement; +} + +function queryLogicUnion(graphLogicChunks: AllLogicStatement[]): AllLogicStatement | undefined { + if (graphLogicChunks.length === 1) return graphLogicChunks[0]; + else if (graphLogicChunks.length > 1) { + return ['And', graphLogicChunks[0], queryLogicUnion(graphLogicChunks.slice(1)) || '0']; + } + return undefined; +} + +/** + * Converts the ReactFlow query to a json data structure to send the query to the backend. + * @returns {BackendQueryFormat} A JSON object in the `JSONFormat`. + */ +export function Query2BackendQuery( + databaseName: string, + graph: QueryMultiGraph, + options: Query2BackendQueryOptions = {} +): BackendQueryFormat { + let query: BackendQueryFormat = { + databaseName: databaseName, + query: [], + limit: options.limit || 500, + return: ['*'], // TODO + }; + + let entities = graph.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Entity) as SerializedNode<EntityNodeAttributes>[]; + let relations = graph.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Relation) as SerializedNode<RelationNodeAttributes>[]; + + const graphologyQuery = Graph.from(graph); + graphologyQuery + .filterNodes((n, att) => att?.type == QueryElementTypes.Logic) + .forEach((n) => { + graphologyQuery.dropNode(n); + }); // Remove all logic nodes from the graph for cycle test + if (hasCycle(graphologyQuery)) { + const cycles = entities.map((entity, i) => { + return allSimplePaths(graphologyQuery, entity.key, entity.key); + }); + cycles.forEach((cycles_inner, i) => { + cycles_inner.forEach((cycle, j) => { + const origin = cycle[0]; + const target = cycle[cycle.length - 2]; + const newOrigin = graphologyQuery.addNode(origin + 'cycle', graphologyQuery.getNodeAttributes(origin)); + const edgeAttributes = graphologyQuery.getEdgeAttributes(target, origin); + graphologyQuery.dropEdge(target, origin); + // edgeAttributes.target = newOrigin; + graphologyQuery.addEdge(target, newOrigin, edgeAttributes); + }); + }); + + console.log('graph', graph); + return Query2BackendQuery(databaseName, graphologyQuery.export()); + + // if ( + // relations.some((entity, i) => { + // return allSimplePaths(graphologyQuery, entity.id, entity.id); + // }) + // ) + // throw Error('Cycles in query are not supported yet'); + // console.log('cycles', cycles); + // return null; + // } + // return null; + } + // Chunk extraction: traverse graph to find all paths of logic between relations and entities + let graphSequenceChunks: QueryGraphNodes[][] = []; + let graphSequenceLogicChunks: QueryGraphNodes[][] = []; + let graphSequenceChunksIdMap: Record<string, [number, number]> = {}; + let chunkOffset = 0; + + let entitiesEmptyLeftHandle = entities.filter((n) => !graph.edges.some((e) => e.target === n.key)); + let relationsEmptyLeftHandle = relations.filter((n) => !graph.edges.some((e) => e.target === n.key)); + // let entitiesEmptyRightHandle = entities.filter((n) => !n?.attributes?.rightRelationHandleId); + entitiesEmptyLeftHandle.map((entity, i) => { + // start with all entities that have no left handle, which means it "starts" a logic + chunkOffset += traverseEntityRelationPaths(entity, graphSequenceChunks, i + chunkOffset, graph, entities, relations); + }); + if (entitiesEmptyLeftHandle.length > 0) chunkOffset++; + relationsEmptyLeftHandle.map((entity, i) => { + // then, for all relations that have no left handle, since they weren't accounted by the loop above + chunkOffset += traverseEntityRelationPaths(entity, graphSequenceChunks, i + chunkOffset, graph, entities, relations); + }); + graphSequenceChunks.forEach((chunkSequence, i) => { + chunkSequence.forEach((chunk, j) => { + graphSequenceChunksIdMap[chunk.id || chunk.name || ''] = [i, j]; + }); + }); + + // Logic pathways extraction: now traverse the graph again though the logic components to construct the logic chunks + // The traversal is done in reverse order, starting from the logic pill's right handle connected to an entity or relation, and going backwards + let logics = graph.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Logic) as SerializedNode<LogicNodeAttributes>[]; + let logicsRightHandleConnectedOutside = logics.filter((n) => { + return graph.edges.some((e) => e.source === n.key && e.attributes?.targetHandleData.nodeType === QueryElementTypes.Entity); + }); + let logicsRightHandleFinal = logics.filter((n) => { + return !graph.edges.some((e) => e.source === n.key); + }); + let graphLogicChunks = logicsRightHandleFinal.map((node) => calculateQueryLogic(node, graph, logics)); + query.logic = queryLogicUnion(graphLogicChunks); + + // Logic pathways extraction: now traverse the graph again though the logic components to construct the logic chunks + + // console.log('logics', logics); + // console.log('graphSequenceChunks', graphSequenceChunks); + // console.log('graphLogicChunks', graphLogicChunks); + // console.log('logicsRightHandleConnectedOutside', logicsRightHandleConnectedOutside); + // console.log('logicsRightHandleFinal', logicsRightHandleFinal); + // console.log('graphSequenceChunksIdMap', graphSequenceChunksIdMap); + if (!graphSequenceChunks || graphSequenceChunks.length === 0 || graphSequenceChunks?.[0].length === 0) return query; + + const processConnection = (chunk: QueryGraphNodes[], position: number): RelationStruct | NodeStruct => { + const currNode = chunk[position]; + + if (currNode.type === QueryElementTypes.Relation) { + const ret: RelationStruct = { + ID: currNode?.id, + label: currNode?.name || undefined, + // depth: QuerySearchDepthStruct; + direction: 'TO', + node: chunk.length === position + 1 ? undefined : (processConnection(chunk, position + 1) as NodeStruct), + }; + return ret; + } else if (currNode.type === QueryElementTypes.Entity) { + const ret: NodeStruct = { + ID: currNode?.id, + label: currNode?.name, + // logic: LogicStruct[]; + // subQuery?: QueryStruct; + // export: ExportNodeStruct[]; + relation: chunk.length === position + 1 ? undefined : (processConnection(chunk, position + 1) as RelationStruct), + }; + return ret; + } + + throw Error('Malformed Chunks! ' + chunk + position); + }; + + query.query = graphSequenceChunks.map((chunk, i) => { + const ret: QueryStruct = { + ID: 'path_' + i, //TODO: chunk[0].id || + node: processConnection(chunk, 0), + }; + return ret; + }); + + console.debug('New query', graph, query); + + return query; +} + +// function processConnectionFromRelation( +// c: SerializedEdge, +// entities: EntityNodeAttributes[], +// relations: RelationNodeAttributes[] +// ): NodeStruct { +// if (c.attributes?.targetHandle === null) throw Error('Malformed Graph! One or more edges of a relation node do not exist'); +// const targetHandleData = fromHandleId(c.attributes?.targetHandle); + +// if (targetHandleData.nodeType === QueryElementTypes.Entity) { +// const targetEntity = entities.find((r) => r.id === c.target); +// } else if (targetHandleData.nodeType === QueryElementTypes.Relation) { +// const targetRelation = relations.find((r) => r.id === c.target); +// } else if (targetHandleData.nodeType === QueryElementTypes.Logic) { +// } +// } diff --git a/libs/shared/lib/schema/index.ts b/libs/shared/lib/schema/index.ts index 44942b3cb..9f8ccaddf 100644 --- a/libs/shared/lib/schema/index.ts +++ b/libs/shared/lib/schema/index.ts @@ -1,2 +1 @@ -export * from './model' -export * from '../querybuilder/model' \ No newline at end of file +export * from './model'; diff --git a/libs/shared/lib/schema/model/FromBackend.ts b/libs/shared/lib/schema/model/FromBackend.ts index f2c6f701b..058ce78f1 100644 --- a/libs/shared/lib/schema/model/FromBackend.ts +++ b/libs/shared/lib/schema/model/FromBackend.ts @@ -1,31 +1,29 @@ /*************** schema format from the backend *************** */ /** Schema type, consist of nodes and edges */ export type SchemaFromBackend = { - edges: SchemaEdge[]; - nodes: SchemaNode[]; + edges: SchemaEdge[]; + nodes: SchemaNode[]; }; +export type SchemaAttributeTypes = 'string' | 'float' | 'int' | 'bool' | 'date' | 'time' | 'datetime' | 'duration'; + /** Attribute type, consist of a name */ export type SchemaAttribute = { - name: string; - type: 'string' | 'int' | 'bool' | 'float'; - // nodeCount: number; - // summedNullAmount: number; - // connectedRatio: number; - // handles: string[]; + name: string; + type: SchemaAttributeTypes; }; /** Node type, consist of a name and a list of attributes */ export type SchemaNode = { - name: string; - attributes: SchemaAttribute[]; + name: string; + attributes: SchemaAttribute[]; }; /** Edge type, consist of a name, start point, end point and a list of attributes */ export type SchemaEdge = { - name: string; - to: string; - from: string; - collection: string; - attributes: SchemaAttribute[]; -}; \ No newline at end of file + name: string; + to: string; + from: string; + collection: string; + attributes: SchemaAttribute[]; +}; diff --git a/libs/shared/lib/schema/model/graphology.ts b/libs/shared/lib/schema/model/graphology.ts index e335f845a..773ff2b98 100644 --- a/libs/shared/lib/schema/model/graphology.ts +++ b/libs/shared/lib/schema/model/graphology.ts @@ -1,6 +1,6 @@ -import { MultiGraph } from "graphology"; -import { Attributes as GAttributes, NodeEntry, EdgeEntry, SerializedGraph } from "graphology-types"; -import { SchemaAttribute, SchemaNode } from "./FromBackend"; +import { MultiGraph } from 'graphology'; +import { Attributes as GAttributes, NodeEntry, EdgeEntry, SerializedGraph } from 'graphology-types'; +import { SchemaAttribute, SchemaNode } from './FromBackend'; /** Attribute type, consist of a name */ export type SchemaGraphologyNode = GAttributes & SchemaNode; @@ -9,5 +9,5 @@ export type SchemaGraphologyEdge = GAttributes; export type SchemaGraphologyNodeEntry = NodeEntry<SchemaGraphologyNode>; export type SchemaGraphologyEdgeEntry = EdgeEntry<SchemaGraphologyNode, SchemaGraphologyNode>; -export class SchemaGraphology extends MultiGraph<SchemaGraphologyNode, SchemaGraphologyEdge, GAttributes> { }; +export class SchemaGraphology extends MultiGraph<SchemaGraphologyNode, SchemaGraphologyEdge, GAttributes> {} export type SchemaGraph = SerializedGraph<SchemaGraphologyNode, SchemaGraphologyEdge, GAttributes>; diff --git a/libs/shared/lib/schema/model/index.ts b/libs/shared/lib/schema/model/index.ts index 2439a89d0..9c1be168b 100644 --- a/libs/shared/lib/schema/model/index.ts +++ b/libs/shared/lib/schema/model/index.ts @@ -1,3 +1,3 @@ -export * from './FromBackend' -export * from './graphology' -export * from './reactflow' \ No newline at end of file +export * from './FromBackend'; +export * from './graphology'; +export * from './reactflow'; diff --git a/libs/shared/lib/schema/model/reactflow.tsx b/libs/shared/lib/schema/model/reactflow.tsx index 71a4399d2..88dc8dc55 100644 --- a/libs/shared/lib/schema/model/reactflow.tsx +++ b/libs/shared/lib/schema/model/reactflow.tsx @@ -29,20 +29,21 @@ export enum AttributeCategory { // selfEdges: Edge[]; // }; -export interface SchemaGraphData { +export interface SchemaReactflowData { name: string; attributes: SchemaGraphologyNode[]; nodeCount: number; summedNullAmount: number; + label: string; } -export interface SchemaGraphNode extends SchemaGraphData { - handles: string[]; +export interface SchemaReactflowNode extends SchemaReactflowData { + // handles: string[]; connectedRatio: number; name: string; } -export interface SchemaGraphRelation extends SchemaGraphData { +export interface SchemaReactflowRelation extends SchemaReactflowData { from: string; to: string; collection: string; @@ -50,12 +51,12 @@ export interface SchemaGraphRelation extends SchemaGraphData { toRatio: number; } -export interface SchemaGraphNodeWithFunctions extends SchemaGraphNode { +export interface SchemaReactflowNodeWithFunctions extends SchemaReactflowNode { toggleNodeQualityPopup: (id: string) => void; toggleAttributeAnalyticsPopupMenu: (id: string) => void; } -export interface SchemaGraphRelationWithFunctions extends SchemaGraphRelation { +export interface SchemaReactflowRelationWithFunctions extends SchemaReactflowRelation { toggleNodeQualityPopup: (id: string) => void; toggleAttributeAnalyticsPopupMenu: (id: string) => void; } @@ -116,12 +117,7 @@ export interface AttributeAnalyticsData { onClickPlaceInQueryBuilderButton: (name: string, type: string) => void; searchForAttributes: (id: string, searchbarValue: string) => void; resetAttributeFilters: (id: string) => void; - applyAttributeFilters: ( - id: string, - category: AttributeCategory, - predicate: string, - percentage: number - ) => void; + applyAttributeFilters: (id: string, category: AttributeCategory, predicate: string, percentage: number) => void; } /** diff --git a/libs/shared/lib/schema/panel/index.ts b/libs/shared/lib/schema/panel/index.ts index 5d5aa13bd..e27a6e2f5 100644 --- a/libs/shared/lib/schema/panel/index.ts +++ b/libs/shared/lib/schema/panel/index.ts @@ -1 +1 @@ -export * from './schema'; \ No newline at end of file +export * from './schema'; diff --git a/libs/shared/lib/schema/panel/schema.spec.ts b/libs/shared/lib/schema/panel/schema.spec.ts index b71c93f13..548ec907b 100644 --- a/libs/shared/lib/schema/panel/schema.spec.ts +++ b/libs/shared/lib/schema/panel/schema.spec.ts @@ -1,4 +1,4 @@ -import { assert, describe, expect, it } from "vitest"; +import { assert, describe, expect, it } from 'vitest'; // import { SchemaUtils } from '@graphpolaris/schema-utils'; // import { SchemaFromBackend } from '@graphpolaris/models'; diff --git a/libs/shared/lib/schema/panel/schema.stories.tsx b/libs/shared/lib/schema/panel/schema.stories.tsx index 9a0136871..79a7114d4 100644 --- a/libs/shared/lib/schema/panel/schema.stories.tsx +++ b/libs/shared/lib/schema/panel/schema.stories.tsx @@ -2,11 +2,7 @@ import React from 'react'; import { Meta, Story, ComponentStory } from '@storybook/react'; import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils'; -import { - colorPaletteConfigSlice, - schemaSlice, - setSchema, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, schemaSlice, setSchema } from '@graphpolaris/shared/lib/data-access/store'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; diff --git a/libs/shared/lib/schema/panel/schema.tsx b/libs/shared/lib/schema/panel/schema.tsx index de9362d13..846faaeea 100644 --- a/libs/shared/lib/schema/panel/schema.tsx +++ b/libs/shared/lib/schema/panel/schema.tsx @@ -1,17 +1,6 @@ -import { - AlgorithmToLayoutProvider, - AllLayoutAlgorithms, - LayoutFactory, -} from '@graphpolaris/shared/lib/graph-layout'; -import { - schemaGraphology2Reactflow, - schemaExpandRelation, -} from '@graphpolaris/shared/lib/schema/schema-utils'; -import { - useSchemaGraph, - useSchemaGraphology, - useSchemaLayout, -} from '@graphpolaris/shared/lib/data-access/store'; +import { AlgorithmToLayoutProvider, AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/shared/lib/graph-layout'; +import { schemaGraphology2Reactflow, schemaExpandRelation } from '@graphpolaris/shared/lib/schema/schema-utils'; +import { useSchemaGraph, useSchemaGraphology, useSchemaLayout } from '@graphpolaris/shared/lib/data-access/store'; import { MultiGraph } from 'graphology'; // import { AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/graph-layout'; import { useEffect, useMemo, useRef, useState } from 'react'; @@ -26,6 +15,7 @@ import ReactFlow, { ReactFlowInstance, Background, } from 'reactflow'; +import CachedIcon from '@mui/icons-material/Cached'; import 'reactflow/dist/style.css'; @@ -78,9 +68,7 @@ export const Schema = (props: Props) => { const [nodes, setNodes, onNodeChanged] = useNodesState([] as Node[]); const [edges, setEdges, onEdgeChanged] = useEdgesState([] as Edge[]); const [firstUserConnection, setFirstUserConnection] = useState<boolean>(true); - const [firstUserConnectionCheck, setFirstUserConnectionCheck] = useState< - string | null - >(sessionStorage.getItem('firstUserConnection')); + const [firstUserConnectionCheck, setFirstUserConnectionCheck] = useState<string | null>(sessionStorage.getItem('firstUserConnection')); const [auth, setAuth] = useState(props.auth); const [authCheck, setAuthCheck] = useState<boolean | null | undefined>(false); @@ -96,22 +84,17 @@ export const Schema = (props: Props) => { // console.log('dbSchema', schemaGraphology, schemaGraphology.order); // }, [schemaGraphology]); - const toggleNodeQualityPopup = (id: string) => { }; - const toggleAttributeAnalyticsPopupMenu = (id: string) => { }; + const toggleNodeQualityPopup = (id: string) => {}; + const toggleAttributeAnalyticsPopupMenu = (id: string) => {}; function updateLayout() { const layoutFactory = new LayoutFactory(); - layout.current = layoutFactory.createLayout( - schemaLayout as AllLayoutAlgorithms - ); // TODO: more layouts here + layout.current = layoutFactory.createLayout(schemaLayout as AllLayoutAlgorithms); // TODO: more layouts here } useEffect(() => { updateLayout(); - sessionStorage.setItem( - 'firstUserConnection', - firstUserConnection.toString() - ); + sessionStorage.setItem('firstUserConnection', firstUserConnection.toString()); }, []); useEffect(() => { @@ -130,13 +113,8 @@ export const Schema = (props: Props) => { setFirstUserConnection(false); } setTimeout(() => { - let sessionStorageUserConnection = sessionStorage.getItem( - 'firstUserConnection' - ); - if ( - sessionStorageUserConnection && - sessionStorageUserConnection === 'true' - ) { + let sessionStorageUserConnection = sessionStorage.getItem('firstUserConnection'); + if (sessionStorageUserConnection && sessionStorageUserConnection === 'true') { sessionStorage.setItem('firstUserConnection', 'false'); setFirstUserConnection(false); } @@ -149,8 +127,7 @@ export const Schema = (props: Props) => { return; } // console.log(schemaGraphology.export()); - - console.log(schemaLayout); + // console.log(schemaLayout); updateLayout(); const expandedSchema = schemaExpandRelation(schemaGraphology); @@ -185,7 +162,7 @@ export const Schema = (props: Props) => { // console.log(nodes, edges); return ( - <div className={styles.schemaPanel} > + <div className={styles.schemaPanel}> {firstUserConnection && ( <Card variant="outlined" @@ -203,10 +180,7 @@ export const Schema = (props: Props) => { </Typography> <LinearProgress sx={{ - color: (theme) => - theme.palette.grey[ - theme.palette.mode === 'light' ? 200 : 800 - ], + color: (theme) => theme.palette.grey[theme.palette.mode === 'light' ? 200 : 800], }} /> </CardContent> @@ -228,12 +202,7 @@ export const Schema = (props: Props) => { panOnDrag={false} attributionPosition="top-right" > - <Controls - showInteractive={false} - showZoom={false} - showFitView={true} - className={styles.controls} - > + <Controls showInteractive={false} showZoom={false} showFitView={true} className={styles.controls}> {/* <ControlButton className={styles.exportButton} title={'Export graph schema'} @@ -247,6 +216,15 @@ export const Schema = (props: Props) => { > <img src={exportIcon} width={21}></img> </ControlButton> */} + <ControlButton + className={styles.exportButton} + title={'Refresh graph schema'} + onClick={(event) => { + event.stopPropagation(); + }} + > + <CachedIcon /> + </ControlButton> </Controls> </ReactFlow> </ReactFlowProvider> diff --git a/libs/shared/lib/schema/panel/schemaOLD.tsx b/libs/shared/lib/schema/panel/schemaOLD.tsx index f2d62bf1c..6f6d33e74 100644 --- a/libs/shared/lib/schema/panel/schemaOLD.tsx +++ b/libs/shared/lib/schema/panel/schemaOLD.tsx @@ -1,27 +1,10 @@ -import { - AllLayoutAlgorithms, - LayoutFactory, -} from '@graphpolaris/shared/lib/graph-layout'; -import { - schemaGraphology2Reactflow, - schemaExpandRelation, -} from '@graphpolaris/shared/lib/schema/schema-utils'; -import { - useSchemaGraphology, - useSchemaLayout, -} from '@graphpolaris/shared/lib/data-access/store'; +import { AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/shared/lib/graph-layout'; +import { schemaGraphology2Reactflow, schemaExpandRelation } from '@graphpolaris/shared/lib/schema/schema-utils'; +import { useSchemaGraphology, useSchemaLayout } from '@graphpolaris/shared/lib/data-access/store'; import { MultiGraph } from 'graphology'; // import { AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/graph-layout'; import { useEffect, useMemo, useState } from 'react'; -import ReactFlow, { - ControlButton, - Controls, - Node, - Edge, - ReactFlowProvider, - useNodesState, - useEdgesState, -} from 'reactflow'; +import ReactFlow, { ControlButton, Controls, Node, Edge, ReactFlowProvider, useNodesState, useEdgesState } from 'reactflow'; import 'reactflow/dist/style.css'; @@ -64,10 +47,7 @@ export const Schema = (props: Props) => { // console.log('dbSchema', dbschema, dbschema.order); // }, [dbschema]); - const expandedSchema = useMemo( - () => schemaExpandRelation(dbschema), - [dbschema] - ); + const expandedSchema = useMemo(() => schemaExpandRelation(dbschema), [dbschema]); useEffect(() => { if (dbschema == undefined || dbschema.order == 0) { @@ -76,20 +56,13 @@ export const Schema = (props: Props) => { const layoutFactory = new LayoutFactory(); console.log('schema Layout', schemaLayout, 'order', expandedSchema); - const layout = layoutFactory.createLayout( - schemaLayout as AllLayoutAlgorithms - ); + const layout = layoutFactory.createLayout(schemaLayout as AllLayoutAlgorithms); layout?.layout(expandedSchema); const flowElements = schemaGraphology2Reactflow(expandedSchema); setNodes(flowElements.nodes); setEdges(flowElements.edges); - console.log( - 'update schema useEffect', - dbschema, - dbschema.order, - flowElements - ); + console.log('update schema useEffect', dbschema, dbschema.order, flowElements); }, [dbschema, schemaLayout]); const graphStyles = { width: '100%', height: '500px' }; @@ -119,12 +92,7 @@ export const Schema = (props: Props) => { onLoad={onLoad} attributionPosition="top-right" > - <Controls - showInteractive={false} - showZoom={false} - showFitView={true} - className={styles.controls} - > + <Controls showInteractive={false} showZoom={false} showFitView={true} className={styles.controls}> <ControlButton className={styles.exportButton} title={'Export graph schema'} diff --git a/libs/shared/lib/schema/panel/view-model/schemaViewModel.test.t b/libs/shared/lib/schema/panel/view-model/schemaViewModel.test.t index 2460545f6..1eaaf31a0 100644 --- a/libs/shared/lib/schema/panel/view-model/schemaViewModel.test.t +++ b/libs/shared/lib/schema/panel/view-model/schemaViewModel.test.t @@ -1,1320 +1,1320 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ -import { - schema, - schema2, -} from '../../../data/mock-data/graph-schema/MockGraph'; -import { - mockAttributeDataNLEdge2, - mockAttributeDataNLEdge2IncorrectId, - mockAttributeDataNLNode1, - mockAttributeDataNLNode2, - mockAttributeDataNLNode2IncorrectId, -} from '../../../data/mock-data/graph-schema/MockAttributeDataBatchedNL'; -import SecondChamberSchemaMock from '../../../data/mock-data/schema-result/2ndChamberSchemaMock'; -import GraphUseCase from '../../../domain/usecases/graph-schema/GraphUseCase'; -import SchemaViewModelImpl from './SchemaViewModel'; -import { Node, Edge, ArrowHeadType } from 'react-flow-renderer'; -import DrawOrderUseCase from '../../../domain/usecases/graph-schema/DrawOrderUseCase'; -import EdgeUseCase from '../../../domain/usecases/graph-schema/EdgeUseCase'; -import NodeUseCase from '../../../domain/usecases/graph-schema/NodeUseCase'; -import { - AttributeCategory, - BoundingBox, - NodeQualityDataForEntities, - NodeQualityDataForRelations, - NodeType, -} from '../../../domain/entity/graph-schema/structures/Types'; -import { - Attribute, - AttributeData, -} from '../../../domain/entity/graph-schema/structures/InputDataTypes'; -import mockQueryResult from '../../../data/mock-data/query-result/big2ndChamberQueryResult'; -import mockSchemaResult from '../../../data/mock-data/schema-result/2ndChamberSchemaMock'; -import Broker from '../../../domain/entity/broker/broker'; - -jest.mock('../../view/graph-schema/SchemaStyleSheet'); -jest.mock('../../util/graph-schema/utils.tsx', () => { - return { - //TODO Is this already updated? - getWidthOfText: () => { - return 10; - }, - calcWidthRelationNodeBox: () => { - return 75; - }, - calcWidthEntityNodeBox: () => { - return 75; - }, - makeBoundingBox: (x: number, y: number, width: number, height: number) => { - let boundingBox: BoundingBox; - boundingBox = { - topLeft: { x: x, y: y }, - bottomRight: { x: x + width, y: y + height }, - }; - return boundingBox; - }, - doBoxesOverlap: (firstBB: BoundingBox, secondBB: BoundingBox) => { - if ( - firstBB.topLeft.x >= secondBB.bottomRight.x || - secondBB.topLeft.x >= firstBB.bottomRight.x - ) - return false; - - if ( - firstBB.topLeft.y >= secondBB.bottomRight.y || - secondBB.topLeft.y >= firstBB.bottomRight.y - ) - return false; - - return true; - }, - }; -}); - -describe('schemaViewModelImpl', () => { - beforeEach(() => jest.resetModules()); - const graphUseCase = new GraphUseCase(); - const drawOrderUseCase = new DrawOrderUseCase(); - const nodeUseCase = new NodeUseCase(); - const edgeUseCase = new EdgeUseCase(); - const attributesInQueryBuilder: any = []; - const addAttribute = (name: string, type: string) => { - attributesInQueryBuilder.push({ name: name, type: type }); - return; - }; - - function anonymous( - attributes: Attribute[], - id: string, - hiddenAttributes: boolean - ): void { - //Empty methode. - } - - it('should create a relation', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - - const expectedrelationode = { - type: 'relation', - id: '5', - position: { x: -72.5, y: 20 }, - data: { - width: 220, - height: 20, - collection: 'none', - attributes: [], - from: 'from:here', - to: 'to:here', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }; - - schemaViewModel.createRelationNode( - '5', - [], - 'none', - schemaViewModel.elements - ); - schemaViewModel.setRelationNodePosition( - 0, - 0, - '5', - 'from:here', - 'to:here', - [] - ); - expect(JSON.stringify(schemaViewModel.elements.nodes[0])).toEqual( - JSON.stringify(expectedrelationode) - ); - - const expectedrelationode2 = { - type: 'relation', - id: '6', - position: { x: -72.5, y: 40 }, - data: { - width: 220, - height: 20, - collection: 'none', - attributes: [], - from: 'from:hereagain', - to: 'to:hereagain', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }; - schemaViewModel.createRelationNode( - '6', - [], - 'none', - schemaViewModel.elements - ); - schemaViewModel.setRelationNodePosition( - 0, - 0, - '6', - 'from:hereagain', - 'to:hereagain', - [] - ); - expect(JSON.stringify(schemaViewModel.elements.nodes[1])).toEqual( - JSON.stringify(expectedrelationode2) - ); - }); - - it('should console log that method is not implemented', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - const consoleSpy = jest.spyOn(console, 'log'); - schemaViewModel.exportToPDF(); - expect(consoleSpy).toHaveBeenCalledWith('Method not implemented.'); - }); - - it('fitToView', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - const getWidthHeight = { width: 300, height: 700 }; - const spy = jest.spyOn(schemaViewModel, 'getWidthHeight'); - spy.mockReturnValue(getWidthHeight); - - schemaViewModel.consumeMessageFromBackend(mockSchemaResult); - schemaViewModel.fitToView(); - - const consoleSpy = jest.spyOn(console, 'log'); - expect(consoleSpy).toHaveBeenCalledWith( - 'this.reactFlowInstance is undefined!' - ); - }); - - /** - * These are the testcases for consuming messages from the backend - */ - describe('consumeMessageFromBackend', () => { - it('should consume schema result only when subscribed to broker for schema_result', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - const mockConsumeMessages = jest.fn(); - const message = 'test schema result'; - - schemaViewModel.consumeMessageFromBackend = mockConsumeMessages; - - // should consume message for schema result when subscribed - schemaViewModel.subscribeToSchemaResult(); - Broker.instance().publish(message, 'schema_result'); - expect(mockConsumeMessages.mock.calls[0][0]).toEqual(message); - expect(mockConsumeMessages).toBeCalledTimes(1); - - // should not consume message for query_result - Broker.instance().publish(message, 'query_result'); - expect(mockConsumeMessages).toBeCalledTimes(1); - - // should not consume message for schema result when unsubscribed - schemaViewModel.unSubscribeFromSchemaResult(); - Broker.instance().publish(message, 'schema_result'); - expect(mockConsumeMessages).toBeCalledTimes(1); - }); - - it('should consume attribute-data only when subscribed to broker for gsa_node_result & gsa_edge_result', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - const mockConsumeMessages = jest.fn(); - const message = 'test analytics data'; - - schemaViewModel.consumeMessageFromBackend = mockConsumeMessages; - - // should consume message for schema result when subscribed - schemaViewModel.subscribeToAnalyticsData(); - Broker.instance().publish(message, 'gsa_node_result'); - Broker.instance().publish(message, 'gsa_edge_result'); - expect(mockConsumeMessages.mock.calls[0][0]).toEqual(message); - expect(mockConsumeMessages).toBeCalledTimes(2); - - // should not consume message for schema result when unsubscribed - schemaViewModel.unSubscribeFromAnalyticsData(); - Broker.instance().publish(message, 'schema_result'); - Broker.instance().publish(message, 'gsa_node_result'); - Broker.instance().publish(message, 'gsa_edge_result'); - expect(mockConsumeMessages).toBeCalledTimes(2); - }); - - //TODO: also test the message for the analytics - it('should console log and should not change any elements when receiving unrelated messages', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - const consoleSpy = jest.spyOn(console, 'log'); - - expect(schemaViewModel.visible).toEqual(true); - schemaViewModel.consumeMessageFromBackend(mockQueryResult); - expect(consoleSpy).toHaveBeenCalledWith('This is no valid input!'); - - expect(schemaViewModel.visible).toEqual(true); - expect(schemaViewModel.elements.nodes).toEqual([]); - expect(schemaViewModel.elements.edges).toEqual([]); - }); - }); - - /** - * These are the testcases for the attribute-analytics popup menu - */ - describe('AttributeAnalyticsPopupMenu', () => { - it('should throw error that given id does not exist', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(schema); - // Nodes that are not in elements cannot have popups - const name = 'Edje'; - expect( - schemaViewModel.elements.nodes.find((node) => node.id == name) - ).toEqual(undefined); - expect(() => - schemaViewModel.toggleAttributeAnalyticsPopupMenu(name) - ).toThrowError( - 'Node ' + name + ' does not exist therefore no popup menu can be shown.' - ); - }); - - it('should show the attribute-analytics popup menu', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - const popup = schemaViewModel.attributeAnalyticsPopupMenu; - schemaViewModel.consumeMessageFromBackend(schema); - - // test for entity node - const node = schemaViewModel.elements.nodes[0]; - schemaViewModel.toggleAttributeAnalyticsPopupMenu(node.id); - expect(popup.isHidden).toEqual(false); - expect(popup.nodeID).toEqual(node.id); - - // test for relation node - const edge = schemaViewModel.elements.edges[0]; - schemaViewModel.setRelationNodePosition( - 0, - 0, - edge.id, - edge.source, - edge.target, - edge.data.attributes - ); - schemaViewModel.toggleAttributeAnalyticsPopupMenu(edge.id); - expect(popup.isHidden).toEqual(false); - expect(popup.nodeID).toEqual(edge.id); - }); - - it('should hide the attribute-analytics popup menu as it was already open', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(schema); - schemaViewModel.attributeAnalyticsPopupMenu.nodeID = 'Thijs'; - schemaViewModel.attributeAnalyticsPopupMenu.isHidden = false; - schemaViewModel.toggleAttributeAnalyticsPopupMenu('Thijs'); - expect(schemaViewModel.attributeAnalyticsPopupMenu.isHidden).toEqual( - true - ); - }); - - it('should close the attribute-analytics menu', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(schema); - schemaViewModel.attributeAnalyticsData['Thijs'].onClickCloseButton(); - expect(schemaViewModel.attributeAnalyticsPopupMenu.isHidden).toEqual( - true - ); - }); - - it('should make an empty attribute-analytics popupmenu', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.attributeAnalyticsPopupMenu = - schemaViewModel.emptyAttributeAnalyticsPopupMenuNode(); - expect(schemaViewModel.attributeAnalyticsPopupMenu.id).toEqual( - 'attributeAnalyticsPopupMenu' - ); - expect(schemaViewModel.attributeAnalyticsPopupMenu.nodeID).toEqual(''); - expect(schemaViewModel.attributeAnalyticsPopupMenu.data.nodeType).toEqual( - NodeType.relation - ); - expect( - schemaViewModel.attributeAnalyticsPopupMenu.data.attributes - ).toEqual([]); - expect( - schemaViewModel.attributeAnalyticsPopupMenu.data.isAttributeDataIn - ).toEqual(false); - }); - - it('should place an attribute node in the querybuilder', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(schema); - const node = schemaViewModel.elements.nodes[0]; - const attribute = node.data.attributes; - schemaViewModel.attributeAnalyticsData[ - node.id - ].onClickPlaceInQueryBuilderButton(attribute.name, attribute.type); - expect(attributesInQueryBuilder[0]).toEqual({ - name: attribute.name, - type: attribute.type, - }); - }); - - it('should have a working searchbar', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(mockSchemaResult); - schemaViewModel.searchForAttributes('kamerleden', 'aa'); - - let attributes = - schemaViewModel.attributeAnalyticsPopupMenu.data.attributes; - expect(attributes.length).toEqual(2); - - schemaViewModel.searchForAttributes('kamerleden', ''); - attributes = schemaViewModel.attributeAnalyticsPopupMenu.data.attributes; - expect(attributes.length).toEqual(6); - }); - - it('should have working filters', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(mockSchemaResult); - schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLNode1); - - schemaViewModel.applyAttributeFilters( - 'kamerleden', - AttributeCategory.categorical, - 'Bigger', - -1 - ); - let attributes = - schemaViewModel.attributeAnalyticsPopupMenu.data.attributes; - expect(attributes.length).toEqual(1); - - schemaViewModel.resetAttributeFilters('kamerleden'); - attributes = schemaViewModel.attributeAnalyticsPopupMenu.data.attributes; - expect(attributes.length).toEqual(6); - }); - }); - - /** - * These are the testcases for the node-quality popup menu - */ - describe('nodeQualityPopup', () => { - it('should throw error that given id does not exist', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(schema); - expect(() => schemaViewModel.toggleNodeQualityPopup('Edje')).toThrowError( - 'Node does not exist therefore no popup can be shown.' - ); - }); - - it('should show the node-quality popup menu for an entity node', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(SecondChamberSchemaMock); - schemaViewModel.toggleNodeQualityPopup('commissies'); - expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(false); - expect(schemaViewModel.nodeQualityPopup.type).toEqual( - 'nodeQualityEntityPopup' - ); - expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual('commissies'); - }); - - it('should show the node-quality popup menu for a relation node', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.createRelationNode( - '5', - [], - '5', - schemaViewModel.elements - ); - schemaViewModel.toggleNodeQualityPopup('5'); - expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(false); - expect(schemaViewModel.nodeQualityPopup.type).toEqual( - 'nodeQualityRelationPopup' - ); - expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual('5'); - }); - - it('should hide the node-quality popup menu as it was already open', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(schema); - schemaViewModel.nodeQualityPopup.nodeID = 'Thijs'; - schemaViewModel.nodeQualityPopup.isHidden = false; - schemaViewModel.toggleNodeQualityPopup('Thijs'); - expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(true); - }); - - it('should close the node-quality menu', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(schema); - schemaViewModel.nodeQualityData['Thijs'].onClickCloseButton(); - expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(true); - }); - - it('should make an empty node-quality popupmenu', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.nodeQualityPopup = - schemaViewModel.emptyNodeQualityPopupNode(); - expect(schemaViewModel.nodeQualityPopup.id).toEqual('nodeQualityPopup'); - expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual(''); - expect(schemaViewModel.nodeQualityPopup.data.nodeCount).toEqual(0); - expect(schemaViewModel.nodeQualityPopup.data.attributeNullCount).toEqual( - 0 - ); - expect(schemaViewModel.nodeQualityPopup.data.isAttributeDataIn).toEqual( - false - ); - }); - }); - - describe('AttributeData', () => { - it('should process the incoming data correctly for node-data', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(mockSchemaResult); - schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLNode2); - - const attributeAnalyticsData = - schemaViewModel.attributeAnalyticsData['commissies']; - const nodeQualityData = schemaViewModel.nodeQualityData[ - 'commissies' - ] as NodeQualityDataForEntities; - expect(attributeAnalyticsData.isAttributeDataIn).toEqual(true); - expect(attributeAnalyticsData.attributes[0].category).toEqual( - AttributeCategory.other - ); - expect(attributeAnalyticsData.attributes[0].nullAmount).toEqual(1); - expect(nodeQualityData.nodeCount).toEqual(38); - expect(nodeQualityData.attributeNullCount).toEqual(1); - expect(nodeQualityData.notConnectedNodeCount).toEqual(0.03); - }); - - it('should process the incoming data correctly for edge-data', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(mockSchemaResult); - schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLEdge2); - - const attributeAnalyticsData = - schemaViewModel.attributeAnalyticsData['lid_van']; - const nodeQualityData = schemaViewModel.nodeQualityData[ - 'lid_van' - ] as NodeQualityDataForRelations; - expect(attributeAnalyticsData.isAttributeDataIn).toEqual(true); - expect(attributeAnalyticsData.attributes[0].category).toEqual( - AttributeCategory.categorical - ); - expect(attributeAnalyticsData.attributes[0].nullAmount).toEqual(0); - expect(nodeQualityData.nodeCount).toEqual(149); - expect(nodeQualityData.attributeNullCount).toEqual(0); - expect(nodeQualityData.fromRatio).toEqual(1); - expect(nodeQualityData.toRatio).toEqual(1); - }); - - it('should console log when the given data has no corresponding id for entities', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(mockSchemaResult); - - const consoleSpy = jest.spyOn(console, 'log'); - schemaViewModel.consumeMessageFromBackend( - mockAttributeDataNLNode2IncorrectId - ); - - expect(consoleSpy).toBeCalledTimes(2); - }); - - it('should console log when the given data has no corresponding id for relations', () => { - const schemaViewModel = new SchemaViewModelImpl( - drawOrderUseCase, - nodeUseCase, - edgeUseCase, - graphUseCase, - addAttribute - ); - schemaViewModel.consumeMessageFromBackend(mockSchemaResult); - - const consoleSpy = jest.spyOn(console, 'log'); - schemaViewModel.consumeMessageFromBackend( - mockAttributeDataNLEdge2IncorrectId - ); - - expect(consoleSpy).toBeCalledTimes(2); - }); - }); - - /** expected results */ - - const expectedNodesInElements: Node[] = [ - { - type: 'entity', - id: 'Airport', - position: { x: 0, y: 0 }, - data: { - attributes: [ - { name: 'city', type: 'string' }, - { name: 'vip', type: 'bool' }, - { name: 'state', type: 'string' }, - ], - handles: [ - 'entityTargetBottom', - 'entityTargetRight', - 'entityTargetRight', - 'entitySourceLeft', - 'entitySourceLeft', - 'entitySourceLeft', - 'entityTargetRight', - ], - width: 165, - height: 20, - nodeCount: 0, - summedNullAmount: 0, - connectedRatio: 0, - }, - }, - { - type: 'entity', - id: 'Plane', - position: { x: 0, y: 150 }, - data: { - attributes: [ - { name: 'type', type: 'string' }, - { name: 'maxFuelCapacity', type: 'int' }, - ], - handles: ['entitySourceTop', 'entityTargetBottom', 'entityTargetRight'], - width: 165, - height: 20, - nodeCount: 0, - summedNullAmount: 0, - connectedRatio: 0, - }, - }, - { - type: 'entity', - id: 'Staff', - position: { x: 0, y: 300 }, - data: { - attributes: [], - handles: ['entityTargetLeft', 'entitySourceTop', 'entitySourceBottom'], - width: 165, - height: 20, - nodeCount: 0, - summedNullAmount: 0, - connectedRatio: 0, - }, - }, - { - type: 'entity', - id: 'Airport2', - position: { x: 0, y: 450 }, - data: { - attributes: [ - { name: 'city', type: 'string' }, - { name: 'vip', type: 'bool' }, - { name: 'state', type: 'string' }, - ], - handles: ['entitySourceRight', 'entitySourceRight', 'entityTargetTop'], - width: 165, - height: 20, - nodeCount: 0, - summedNullAmount: 0, - connectedRatio: 0, - }, - }, - - { - type: 'entity', - id: 'Thijs', - position: { x: 0, y: 600 }, - data: { - attributes: [], - handles: ['entitySourceRight', 'entityTargetLeft'], - width: 165, - height: 20, - nodeCount: 0, - summedNullAmount: 0, - connectedRatio: 0, - }, - }, - - { - type: 'entity', - id: 'Unconnected', - position: { x: 0, y: 750 }, - data: { - attributes: [], - handles: [], - width: 165, - height: 20, - nodeCount: 0, - summedNullAmount: 0, - connectedRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [ - { - name: 'arrivalTime', - type: 'int', - }, - { - name: 'departureTime', - type: 'int', - }, - ], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [ - { - name: 'salary', - type: 'int', - }, - ], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [ - { - name: 'hallo', - type: 'string', - }, - ], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [ - { - name: 'hallo', - type: 'string', - }, - ], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [ - { - name: 'hallo', - type: 'string', - }, - ], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [ - { - name: 'hallo', - type: 'string', - }, - ], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [ - { - name: 'hallo', - type: 'string', - }, - ], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - { - type: 'relation', - id: 'flights', - position: { x: 0, y: 0 }, - data: { - width: 220, - height: 40, - collection: 'flights', - attributes: [ - { - name: 'test', - type: 'string', - }, - ], - from: '', - to: '', - nodeCount: 0, - summedNullAmount: 0, - fromRatio: 0, - toRatio: 0, - }, - }, - ]; - - const expectedEdgesInElements: Edge[] = [ - { - id: 'flights', - source: 'Plane', - target: 'Airport', - type: 'nodeEdge', - label: 'Plane:Airport', - data: { - attributes: [], - d: 0, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceTop', - targetHandle: 'entityTargetBottom', - }, - { - id: 'flights', - source: 'Airport2', - target: 'Airport', - type: 'nodeEdge', - label: 'Airport2:Airport', - data: { - attributes: [ - { name: 'arrivalTime', type: 'int' }, - { name: 'departureTime', type: 'int' }, - ], - d: 40, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceRight', - targetHandle: 'entityTargetRight', - }, - { - id: 'flights', - source: 'Thijs', - target: 'Airport', - type: 'nodeEdge', - label: 'Thijs:Airport', - data: { - attributes: [{ name: 'hallo', type: 'string' }], - d: 80, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceRight', - targetHandle: 'entityTargetRight', - }, - { - id: 'flights', - source: 'Airport', - target: 'Staff', - type: 'nodeEdge', - label: 'Airport:Staff', - data: { - attributes: [{ name: 'salary', type: 'int' }], - d: -40, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceLeft', - targetHandle: 'entityTargetLeft', - }, - { - id: 'flights', - source: 'Airport', - target: 'Thijs', - type: 'nodeEdge', - label: 'Airport:Thijs', - data: { - attributes: [{ name: 'hallo', type: 'string' }], - d: -80, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceLeft', - targetHandle: 'entityTargetLeft', - }, - { - id: 'flights', - source: 'Staff', - target: 'Plane', - type: 'nodeEdge', - label: 'Staff:Plane', - data: { - attributes: [{ name: 'hallo', type: 'string' }], - d: 0, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceTop', - targetHandle: 'entityTargetBottom', - }, - { - id: 'flights', - source: 'Airport2', - target: 'Plane', - type: 'nodeEdge', - label: 'Airport2:Plane', - data: { - attributes: [{ name: 'hallo', type: 'string' }], - d: 120, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceRight', - targetHandle: 'entityTargetRight', - }, - { - id: 'flights', - source: 'Staff', - target: 'Airport2', - type: 'nodeEdge', - label: 'Staff:Airport2', - data: { - attributes: [{ name: 'hallo', type: 'string' }], - d: 0, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceBottom', - targetHandle: 'entityTargetTop', - }, - { - id: 'flights', - source: 'Airport', - target: 'Airport', - type: 'selfEdge', - label: 'Airport:Airport', - data: { - attributes: [{ name: 'test', type: 'string' }], - d: 58, - created: false, - collection: 'flights', - edgeCount: 0, - view: anonymous, - }, - arrowHeadType: ArrowHeadType.Arrow, - sourceHandle: 'entitySourceLeft', - targetHandle: 'entityTargetRight', - }, - ]; - - const expectedAttributes: Node[] = [ - { - type: 'attribute', - id: 'Airport:city', - position: { x: 0, y: 21 }, - data: { name: 'city', datatype: 'string' }, - isHidden: true, - }, - { - type: 'attribute', - id: 'Airport:vip', - position: { x: 0, y: 41 }, - data: { name: 'vip', datatype: 'bool' }, - isHidden: true, - }, - { - type: 'attribute', - id: 'Airport:state', - position: { x: 0, y: 61 }, - data: { name: 'state', datatype: 'string' }, - isHidden: true, - }, - { - type: 'attribute', - id: 'Plane:type', - position: { x: 0, y: 171 }, - data: { name: 'type', datatype: 'string' }, - isHidden: true, - }, - { - type: 'attribute', - id: 'Plane:maxFuelCapacity', - position: { x: 0, y: 191 }, - data: { name: 'maxFuelCapacity', datatype: 'int' }, - isHidden: true, - }, - { - type: 'attribute', - id: 'Airport2:city', - position: { x: 0, y: 471 }, - data: { name: 'city', datatype: 'string' }, - isHidden: true, - }, - { - type: 'attribute', - id: 'Airport2:vip', - position: { x: 0, y: 491 }, - data: { name: 'vip', datatype: 'bool' }, - isHidden: true, - }, - { - type: 'attribute', - id: 'Airport2:state', - position: { x: 0, y: 511 }, - data: { name: 'state', datatype: 'string' }, - isHidden: true, - }, - ]; -}); - -/** Result nodes. */ -const nodes: Node[] = [ - { - type: 'entity', - id: 'Thijs', - position: { x: 0, y: 0 }, - data: { attributes: [] }, - }, - { - type: 'entity', - id: 'Airport', - position: { x: 0, y: 0 }, - data: { attributes: [] }, - }, - { - type: 'entity', - id: 'Airport2', - position: { x: 0, y: 0 }, - data: { attributes: [] }, - }, - { - type: 'entity', - id: 'Plane', - position: { x: 0, y: 0 }, - data: { attributes: [] }, - }, - { - type: 'entity', - id: 'Staff', - position: { x: 0, y: 0 }, - data: { attributes: [] }, - }, -]; - -/** Result links. */ -const edges: Edge[] = [ - { - id: 'Airport2:Airport', - label: 'Airport2:Airport', - type: 'nodeEdge', - source: 'Airport2', - target: 'Airport', - arrowHeadType: ArrowHeadType.Arrow, - data: { - d: '', - attributes: [], - }, - }, - { - id: 'Airport:Staff', - label: 'Airport:Staff', - type: 'nodeEdge', - source: 'Airport', - target: 'Staff', - arrowHeadType: ArrowHeadType.Arrow, - data: { d: '', attributes: [] }, - }, - { - id: 'Plane:Airport', - label: 'Plane:Airport', - type: 'nodeEdge', - source: 'Plane', - target: 'Airport', - arrowHeadType: ArrowHeadType.Arrow, - data: { d: '', attributes: [] }, - }, - { - id: 'Airport:Thijs', - label: 'Airport:Thijs', - type: 'nodeEdge', - source: 'Airport', - target: 'Thijs', - arrowHeadType: ArrowHeadType.Arrow, - data: { d: '', attributes: [] }, - }, - { - id: 'Thijs:Airport', - label: 'Thijs:Airport', - type: 'nodeEdge', - source: 'Thijs', - target: 'Airport', - arrowHeadType: ArrowHeadType.Arrow, - data: { d: '', attributes: [] }, - }, - { - id: 'Staff:Plane', - label: 'Staff:Plane', - type: 'nodeEdge', - source: 'Staff', - target: 'Plane', - arrowHeadType: ArrowHeadType.Arrow, - data: { d: '', attributes: [] }, - }, - { - id: 'Staff:Airport2', - label: 'Staff:Airport2', - type: 'nodeEdge', - source: 'Staff', - target: 'Airport2', - arrowHeadType: ArrowHeadType.Arrow, - data: { d: '', attributes: [] }, - }, - { - id: 'Airport2:Plane', - label: 'Airport2:Plane', - type: 'nodeEdge', - source: 'Airport2', - target: 'Plane', - arrowHeadType: ArrowHeadType.Arrow, - data: { d: '', attributes: [] }, - }, -]; +/** + * This program has been developed by students from the bachelor Computer Science at + * Utrecht University within the Software Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ +import { + schema, + schema2, +} from '../../../data/mock-data/graph-schema/MockGraph'; +import { + mockAttributeDataNLEdge2, + mockAttributeDataNLEdge2IncorrectId, + mockAttributeDataNLNode1, + mockAttributeDataNLNode2, + mockAttributeDataNLNode2IncorrectId, +} from '../../../data/mock-data/graph-schema/MockAttributeDataBatchedNL'; +import SecondChamberSchemaMock from '../../../data/mock-data/schema-result/2ndChamberSchemaMock'; +import GraphUseCase from '../../../domain/usecases/graph-schema/GraphUseCase'; +import SchemaViewModelImpl from './SchemaViewModel'; +import { Node, Edge, ArrowHeadType } from 'react-flow-renderer'; +import DrawOrderUseCase from '../../../domain/usecases/graph-schema/DrawOrderUseCase'; +import EdgeUseCase from '../../../domain/usecases/graph-schema/EdgeUseCase'; +import NodeUseCase from '../../../domain/usecases/graph-schema/NodeUseCase'; +import { + AttributeCategory, + BoundingBox, + NodeQualityDataForEntities, + NodeQualityDataForRelations, + NodeType, +} from '../../../domain/entity/graph-schema/structures/Types'; +import { + Attribute, + AttributeData, +} from '../../../domain/entity/graph-schema/structures/InputDataTypes'; +import mockQueryResult from '../../../data/mock-data/query-result/big2ndChamberQueryResult'; +import mockSchemaResult from '../../../data/mock-data/schema-result/2ndChamberSchemaMock'; +import Broker from '../../../domain/entity/broker/broker'; + +jest.mock('../../view/graph-schema/SchemaStyleSheet'); +jest.mock('../../util/graph-schema/utils.tsx', () => { + return { + //TODO Is this already updated? + getWidthOfText: () => { + return 10; + }, + calcWidthRelationNodeBox: () => { + return 75; + }, + calcWidthEntityNodeBox: () => { + return 75; + }, + makeBoundingBox: (x: number, y: number, width: number, height: number) => { + let boundingBox: BoundingBox; + boundingBox = { + topLeft: { x: x, y: y }, + bottomRight: { x: x + width, y: y + height }, + }; + return boundingBox; + }, + doBoxesOverlap: (firstBB: BoundingBox, secondBB: BoundingBox) => { + if ( + firstBB.topLeft.x >= secondBB.bottomRight.x || + secondBB.topLeft.x >= firstBB.bottomRight.x + ) + return false; + + if ( + firstBB.topLeft.y >= secondBB.bottomRight.y || + secondBB.topLeft.y >= firstBB.bottomRight.y + ) + return false; + + return true; + }, + }; +}); + +describe('schemaViewModelImpl', () => { + beforeEach(() => jest.resetModules()); + const graphUseCase = new GraphUseCase(); + const drawOrderUseCase = new DrawOrderUseCase(); + const nodeUseCase = new NodeUseCase(); + const edgeUseCase = new EdgeUseCase(); + const attributesInQueryBuilder: any = []; + const addAttribute = (name: string, type: string) => { + attributesInQueryBuilder.push({ name: name, type: type }); + return; + }; + + function anonymous( + attributes: Attribute[], + id: string, + hiddenAttributes: boolean + ): void { + //Empty methode. + } + + it('should create a relation', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + + const expectedrelationode = { + type: 'relation', + id: '5', + position: { x: -72.5, y: 20 }, + data: { + width: 220, + height: 20, + collection: 'none', + attributes: [], + from: 'from:here', + to: 'to:here', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }; + + schemaViewModel.createRelationNode( + '5', + [], + 'none', + schemaViewModel.elements + ); + schemaViewModel.setRelationNodePosition( + 0, + 0, + '5', + 'from:here', + 'to:here', + [] + ); + expect(JSON.stringify(schemaViewModel.elements.nodes[0])).toEqual( + JSON.stringify(expectedrelationode) + ); + + const expectedrelationode2 = { + type: 'relation', + id: '6', + position: { x: -72.5, y: 40 }, + data: { + width: 220, + height: 20, + collection: 'none', + attributes: [], + from: 'from:hereagain', + to: 'to:hereagain', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }; + schemaViewModel.createRelationNode( + '6', + [], + 'none', + schemaViewModel.elements + ); + schemaViewModel.setRelationNodePosition( + 0, + 0, + '6', + 'from:hereagain', + 'to:hereagain', + [] + ); + expect(JSON.stringify(schemaViewModel.elements.nodes[1])).toEqual( + JSON.stringify(expectedrelationode2) + ); + }); + + it('should console log that method is not implemented', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + const consoleSpy = jest.spyOn(console, 'log'); + schemaViewModel.exportToPDF(); + expect(consoleSpy).toHaveBeenCalledWith('Method not implemented.'); + }); + + it('fitToView', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + const getWidthHeight = { width: 300, height: 700 }; + const spy = jest.spyOn(schemaViewModel, 'getWidthHeight'); + spy.mockReturnValue(getWidthHeight); + + schemaViewModel.consumeMessageFromBackend(mockSchemaResult); + schemaViewModel.fitToView(); + + const consoleSpy = jest.spyOn(console, 'log'); + expect(consoleSpy).toHaveBeenCalledWith( + 'this.reactFlowInstance is undefined!' + ); + }); + + /** + * These are the testcases for consuming messages from the backend + */ + describe('consumeMessageFromBackend', () => { + it('should consume schema result only when subscribed to broker for schema_result', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + const mockConsumeMessages = jest.fn(); + const message = 'test schema result'; + + schemaViewModel.consumeMessageFromBackend = mockConsumeMessages; + + // should consume message for schema result when subscribed + schemaViewModel.subscribeToSchemaResult(); + Broker.instance().publish(message, 'schema_result'); + expect(mockConsumeMessages.mock.calls[0][0]).toEqual(message); + expect(mockConsumeMessages).toBeCalledTimes(1); + + // should not consume message for query_result + Broker.instance().publish(message, 'query_result'); + expect(mockConsumeMessages).toBeCalledTimes(1); + + // should not consume message for schema result when unsubscribed + schemaViewModel.unSubscribeFromSchemaResult(); + Broker.instance().publish(message, 'schema_result'); + expect(mockConsumeMessages).toBeCalledTimes(1); + }); + + it('should consume attribute-data only when subscribed to broker for gsa_node_result & gsa_edge_result', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + const mockConsumeMessages = jest.fn(); + const message = 'test analytics data'; + + schemaViewModel.consumeMessageFromBackend = mockConsumeMessages; + + // should consume message for schema result when subscribed + schemaViewModel.subscribeToAnalyticsData(); + Broker.instance().publish(message, 'gsa_node_result'); + Broker.instance().publish(message, 'gsa_edge_result'); + expect(mockConsumeMessages.mock.calls[0][0]).toEqual(message); + expect(mockConsumeMessages).toBeCalledTimes(2); + + // should not consume message for schema result when unsubscribed + schemaViewModel.unSubscribeFromAnalyticsData(); + Broker.instance().publish(message, 'schema_result'); + Broker.instance().publish(message, 'gsa_node_result'); + Broker.instance().publish(message, 'gsa_edge_result'); + expect(mockConsumeMessages).toBeCalledTimes(2); + }); + + //TODO: also test the message for the analytics + it('should console log and should not change any elements when receiving unrelated messages', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + const consoleSpy = jest.spyOn(console, 'log'); + + expect(schemaViewModel.visible).toEqual(true); + schemaViewModel.consumeMessageFromBackend(mockQueryResult); + expect(consoleSpy).toHaveBeenCalledWith('This is no valid input!'); + + expect(schemaViewModel.visible).toEqual(true); + expect(schemaViewModel.elements.nodes).toEqual([]); + expect(schemaViewModel.elements.edges).toEqual([]); + }); + }); + + /** + * These are the testcases for the attribute-analytics popup menu + */ + describe('AttributeAnalyticsPopupMenu', () => { + it('should throw error that given id does not exist', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(schema); + // Nodes that are not in elements cannot have popups + const name = 'Edje'; + expect( + schemaViewModel.elements.nodes.find((node) => node.id == name) + ).toEqual(undefined); + expect(() => + schemaViewModel.toggleAttributeAnalyticsPopupMenu(name) + ).toThrowError( + 'Node ' + name + ' does not exist therefore no popup menu can be shown.' + ); + }); + + it('should show the attribute-analytics popup menu', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + const popup = schemaViewModel.attributeAnalyticsPopupMenu; + schemaViewModel.consumeMessageFromBackend(schema); + + // test for entity node + const node = schemaViewModel.elements.nodes[0]; + schemaViewModel.toggleAttributeAnalyticsPopupMenu(node.id); + expect(popup.isHidden).toEqual(false); + expect(popup.nodeID).toEqual(node.id); + + // test for relation node + const edge = schemaViewModel.elements.edges[0]; + schemaViewModel.setRelationNodePosition( + 0, + 0, + edge.id, + edge.source, + edge.target, + edge.data.attributes + ); + schemaViewModel.toggleAttributeAnalyticsPopupMenu(edge.id); + expect(popup.isHidden).toEqual(false); + expect(popup.nodeID).toEqual(edge.id); + }); + + it('should hide the attribute-analytics popup menu as it was already open', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(schema); + schemaViewModel.attributeAnalyticsPopupMenu.nodeID = 'Thijs'; + schemaViewModel.attributeAnalyticsPopupMenu.isHidden = false; + schemaViewModel.toggleAttributeAnalyticsPopupMenu('Thijs'); + expect(schemaViewModel.attributeAnalyticsPopupMenu.isHidden).toEqual( + true + ); + }); + + it('should close the attribute-analytics menu', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(schema); + schemaViewModel.attributeAnalyticsData['Thijs'].onClickCloseButton(); + expect(schemaViewModel.attributeAnalyticsPopupMenu.isHidden).toEqual( + true + ); + }); + + it('should make an empty attribute-analytics popupmenu', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.attributeAnalyticsPopupMenu = + schemaViewModel.emptyAttributeAnalyticsPopupMenuNode(); + expect(schemaViewModel.attributeAnalyticsPopupMenu.id).toEqual( + 'attributeAnalyticsPopupMenu' + ); + expect(schemaViewModel.attributeAnalyticsPopupMenu.nodeID).toEqual(''); + expect(schemaViewModel.attributeAnalyticsPopupMenu.data.nodeType).toEqual( + NodeType.relation + ); + expect( + schemaViewModel.attributeAnalyticsPopupMenu.data.attributes + ).toEqual([]); + expect( + schemaViewModel.attributeAnalyticsPopupMenu.data.isAttributeDataIn + ).toEqual(false); + }); + + it('should place an attribute node in the querybuilder', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(schema); + const node = schemaViewModel.elements.nodes[0]; + const attribute = node.data.attributes; + schemaViewModel.attributeAnalyticsData[ + node.id + ].onClickPlaceInQueryBuilderButton(attribute.name, attribute.type); + expect(attributesInQueryBuilder[0]).toEqual({ + name: attribute.name, + type: attribute.type, + }); + }); + + it('should have a working searchbar', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(mockSchemaResult); + schemaViewModel.searchForAttributes('kamerleden', 'aa'); + + let attributes = + schemaViewModel.attributeAnalyticsPopupMenu.data.attributes; + expect(attributes.length).toEqual(2); + + schemaViewModel.searchForAttributes('kamerleden', ''); + attributes = schemaViewModel.attributeAnalyticsPopupMenu.data.attributes; + expect(attributes.length).toEqual(6); + }); + + it('should have working filters', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(mockSchemaResult); + schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLNode1); + + schemaViewModel.applyAttributeFilters( + 'kamerleden', + AttributeCategory.categorical, + 'Bigger', + -1 + ); + let attributes = + schemaViewModel.attributeAnalyticsPopupMenu.data.attributes; + expect(attributes.length).toEqual(1); + + schemaViewModel.resetAttributeFilters('kamerleden'); + attributes = schemaViewModel.attributeAnalyticsPopupMenu.data.attributes; + expect(attributes.length).toEqual(6); + }); + }); + + /** + * These are the testcases for the node-quality popup menu + */ + describe('nodeQualityPopup', () => { + it('should throw error that given id does not exist', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(schema); + expect(() => schemaViewModel.toggleNodeQualityPopup('Edje')).toThrowError( + 'Node does not exist therefore no popup can be shown.' + ); + }); + + it('should show the node-quality popup menu for an entity node', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(SecondChamberSchemaMock); + schemaViewModel.toggleNodeQualityPopup('commissies'); + expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(false); + expect(schemaViewModel.nodeQualityPopup.type).toEqual( + 'nodeQualityEntityPopup' + ); + expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual('commissies'); + }); + + it('should show the node-quality popup menu for a relation node', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.createRelationNode( + '5', + [], + '5', + schemaViewModel.elements + ); + schemaViewModel.toggleNodeQualityPopup('5'); + expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(false); + expect(schemaViewModel.nodeQualityPopup.type).toEqual( + 'nodeQualityRelationPopup' + ); + expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual('5'); + }); + + it('should hide the node-quality popup menu as it was already open', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(schema); + schemaViewModel.nodeQualityPopup.nodeID = 'Thijs'; + schemaViewModel.nodeQualityPopup.isHidden = false; + schemaViewModel.toggleNodeQualityPopup('Thijs'); + expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(true); + }); + + it('should close the node-quality menu', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(schema); + schemaViewModel.nodeQualityData['Thijs'].onClickCloseButton(); + expect(schemaViewModel.nodeQualityPopup.isHidden).toEqual(true); + }); + + it('should make an empty node-quality popupmenu', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.nodeQualityPopup = + schemaViewModel.emptyNodeQualityPopupNode(); + expect(schemaViewModel.nodeQualityPopup.id).toEqual('nodeQualityPopup'); + expect(schemaViewModel.nodeQualityPopup.nodeID).toEqual(''); + expect(schemaViewModel.nodeQualityPopup.data.nodeCount).toEqual(0); + expect(schemaViewModel.nodeQualityPopup.data.attributeNullCount).toEqual( + 0 + ); + expect(schemaViewModel.nodeQualityPopup.data.isAttributeDataIn).toEqual( + false + ); + }); + }); + + describe('AttributeData', () => { + it('should process the incoming data correctly for node-data', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(mockSchemaResult); + schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLNode2); + + const attributeAnalyticsData = + schemaViewModel.attributeAnalyticsData['commissies']; + const nodeQualityData = schemaViewModel.nodeQualityData[ + 'commissies' + ] as NodeQualityDataForEntities; + expect(attributeAnalyticsData.isAttributeDataIn).toEqual(true); + expect(attributeAnalyticsData.attributes[0].category).toEqual( + AttributeCategory.other + ); + expect(attributeAnalyticsData.attributes[0].nullAmount).toEqual(1); + expect(nodeQualityData.nodeCount).toEqual(38); + expect(nodeQualityData.attributeNullCount).toEqual(1); + expect(nodeQualityData.notConnectedNodeCount).toEqual(0.03); + }); + + it('should process the incoming data correctly for edge-data', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(mockSchemaResult); + schemaViewModel.consumeMessageFromBackend(mockAttributeDataNLEdge2); + + const attributeAnalyticsData = + schemaViewModel.attributeAnalyticsData['lid_van']; + const nodeQualityData = schemaViewModel.nodeQualityData[ + 'lid_van' + ] as NodeQualityDataForRelations; + expect(attributeAnalyticsData.isAttributeDataIn).toEqual(true); + expect(attributeAnalyticsData.attributes[0].category).toEqual( + AttributeCategory.categorical + ); + expect(attributeAnalyticsData.attributes[0].nullAmount).toEqual(0); + expect(nodeQualityData.nodeCount).toEqual(149); + expect(nodeQualityData.attributeNullCount).toEqual(0); + expect(nodeQualityData.fromRatio).toEqual(1); + expect(nodeQualityData.toRatio).toEqual(1); + }); + + it('should console log when the given data has no corresponding id for entities', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(mockSchemaResult); + + const consoleSpy = jest.spyOn(console, 'log'); + schemaViewModel.consumeMessageFromBackend( + mockAttributeDataNLNode2IncorrectId + ); + + expect(consoleSpy).toBeCalledTimes(2); + }); + + it('should console log when the given data has no corresponding id for relations', () => { + const schemaViewModel = new SchemaViewModelImpl( + drawOrderUseCase, + nodeUseCase, + edgeUseCase, + graphUseCase, + addAttribute + ); + schemaViewModel.consumeMessageFromBackend(mockSchemaResult); + + const consoleSpy = jest.spyOn(console, 'log'); + schemaViewModel.consumeMessageFromBackend( + mockAttributeDataNLEdge2IncorrectId + ); + + expect(consoleSpy).toBeCalledTimes(2); + }); + }); + + /** expected results */ + + const expectedNodesInElements: Node[] = [ + { + type: QueryElementTypes.Entity, + id: 'Airport', + position: { x: 0, y: 0 }, + data: { + attributes: [ + { name: 'city', type: 'string' }, + { name: 'vip', type: 'bool' }, + { name: 'state', type: 'string' }, + ], + handles: [ + 'entityTargetBottom', + 'entityTargetRight', + 'entityTargetRight', + 'entitySourceLeft', + 'entitySourceLeft', + 'entitySourceLeft', + 'entityTargetRight', + ], + width: 165, + height: 20, + nodeCount: 0, + summedNullAmount: 0, + connectedRatio: 0, + }, + }, + { + type: QueryElementTypes.Entity, + id: 'Plane', + position: { x: 0, y: 150 }, + data: { + attributes: [ + { name: 'type', type: 'string' }, + { name: 'maxFuelCapacity', type: 'int' }, + ], + handles: ['entitySourceTop', 'entityTargetBottom', 'entityTargetRight'], + width: 165, + height: 20, + nodeCount: 0, + summedNullAmount: 0, + connectedRatio: 0, + }, + }, + { + type: QueryElementTypes.Entity, + id: 'Staff', + position: { x: 0, y: 300 }, + data: { + attributes: [], + handles: ['entityTargetLeft', 'entitySourceTop', 'entitySourceBottom'], + width: 165, + height: 20, + nodeCount: 0, + summedNullAmount: 0, + connectedRatio: 0, + }, + }, + { + type: QueryElementTypes.Entity, + id: 'Airport2', + position: { x: 0, y: 450 }, + data: { + attributes: [ + { name: 'city', type: 'string' }, + { name: 'vip', type: 'bool' }, + { name: 'state', type: 'string' }, + ], + handles: ['entitySourceRight', 'entitySourceRight', 'entityTargetTop'], + width: 165, + height: 20, + nodeCount: 0, + summedNullAmount: 0, + connectedRatio: 0, + }, + }, + + { + type: QueryElementTypes.Entity, + id: 'Thijs', + position: { x: 0, y: 600 }, + data: { + attributes: [], + handles: ['entitySourceRight', 'entityTargetLeft'], + width: 165, + height: 20, + nodeCount: 0, + summedNullAmount: 0, + connectedRatio: 0, + }, + }, + + { + type: QueryElementTypes.Entity, + id: 'Unconnected', + position: { x: 0, y: 750 }, + data: { + attributes: [], + handles: [], + width: 165, + height: 20, + nodeCount: 0, + summedNullAmount: 0, + connectedRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [ + { + name: 'arrivalTime', + type: 'int', + }, + { + name: 'departureTime', + type: 'int', + }, + ], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [ + { + name: 'salary', + type: 'int', + }, + ], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [ + { + name: 'hallo', + type: 'string', + }, + ], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [ + { + name: 'hallo', + type: 'string', + }, + ], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [ + { + name: 'hallo', + type: 'string', + }, + ], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [ + { + name: 'hallo', + type: 'string', + }, + ], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [ + { + name: 'hallo', + type: 'string', + }, + ], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + { + type: 'relation', + id: 'flights', + position: { x: 0, y: 0 }, + data: { + width: 220, + height: 40, + collection: 'flights', + attributes: [ + { + name: 'test', + type: 'string', + }, + ], + from: '', + to: '', + nodeCount: 0, + summedNullAmount: 0, + fromRatio: 0, + toRatio: 0, + }, + }, + ]; + + const expectedEdgesInElements: Edge[] = [ + { + id: 'flights', + source: 'Plane', + target: 'Airport', + type: 'nodeEdge', + label: 'Plane:Airport', + data: { + attributes: [], + d: 0, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceTop', + targetHandle: 'entityTargetBottom', + }, + { + id: 'flights', + source: 'Airport2', + target: 'Airport', + type: 'nodeEdge', + label: 'Airport2:Airport', + data: { + attributes: [ + { name: 'arrivalTime', type: 'int' }, + { name: 'departureTime', type: 'int' }, + ], + d: 40, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceRight', + targetHandle: 'entityTargetRight', + }, + { + id: 'flights', + source: 'Thijs', + target: 'Airport', + type: 'nodeEdge', + label: 'Thijs:Airport', + data: { + attributes: [{ name: 'hallo', type: 'string' }], + d: 80, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceRight', + targetHandle: 'entityTargetRight', + }, + { + id: 'flights', + source: 'Airport', + target: 'Staff', + type: 'nodeEdge', + label: 'Airport:Staff', + data: { + attributes: [{ name: 'salary', type: 'int' }], + d: -40, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceLeft', + targetHandle: 'entityTargetLeft', + }, + { + id: 'flights', + source: 'Airport', + target: 'Thijs', + type: 'nodeEdge', + label: 'Airport:Thijs', + data: { + attributes: [{ name: 'hallo', type: 'string' }], + d: -80, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceLeft', + targetHandle: 'entityTargetLeft', + }, + { + id: 'flights', + source: 'Staff', + target: 'Plane', + type: 'nodeEdge', + label: 'Staff:Plane', + data: { + attributes: [{ name: 'hallo', type: 'string' }], + d: 0, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceTop', + targetHandle: 'entityTargetBottom', + }, + { + id: 'flights', + source: 'Airport2', + target: 'Plane', + type: 'nodeEdge', + label: 'Airport2:Plane', + data: { + attributes: [{ name: 'hallo', type: 'string' }], + d: 120, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceRight', + targetHandle: 'entityTargetRight', + }, + { + id: 'flights', + source: 'Staff', + target: 'Airport2', + type: 'nodeEdge', + label: 'Staff:Airport2', + data: { + attributes: [{ name: 'hallo', type: 'string' }], + d: 0, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceBottom', + targetHandle: 'entityTargetTop', + }, + { + id: 'flights', + source: 'Airport', + target: 'Airport', + type: 'selfEdge', + label: 'Airport:Airport', + data: { + attributes: [{ name: 'test', type: 'string' }], + d: 58, + created: false, + collection: 'flights', + edgeCount: 0, + view: anonymous, + }, + arrowHeadType: ArrowHeadType.Arrow, + sourceHandle: 'entitySourceLeft', + targetHandle: 'entityTargetRight', + }, + ]; + + const expectedAttributes: Node[] = [ + { + type: 'attribute', + id: 'Airport:city', + position: { x: 0, y: 21 }, + data: { name: 'city', datatype: 'string' }, + isHidden: true, + }, + { + type: 'attribute', + id: 'Airport:vip', + position: { x: 0, y: 41 }, + data: { name: 'vip', datatype: 'bool' }, + isHidden: true, + }, + { + type: 'attribute', + id: 'Airport:state', + position: { x: 0, y: 61 }, + data: { name: 'state', datatype: 'string' }, + isHidden: true, + }, + { + type: 'attribute', + id: 'Plane:type', + position: { x: 0, y: 171 }, + data: { name: 'type', datatype: 'string' }, + isHidden: true, + }, + { + type: 'attribute', + id: 'Plane:maxFuelCapacity', + position: { x: 0, y: 191 }, + data: { name: 'maxFuelCapacity', datatype: 'int' }, + isHidden: true, + }, + { + type: 'attribute', + id: 'Airport2:city', + position: { x: 0, y: 471 }, + data: { name: 'city', datatype: 'string' }, + isHidden: true, + }, + { + type: 'attribute', + id: 'Airport2:vip', + position: { x: 0, y: 491 }, + data: { name: 'vip', datatype: 'bool' }, + isHidden: true, + }, + { + type: 'attribute', + id: 'Airport2:state', + position: { x: 0, y: 511 }, + data: { name: 'state', datatype: 'string' }, + isHidden: true, + }, + ]; +}); + +/** Result nodes. */ +const nodes: Node[] = [ + { + type: QueryElementTypes.Entity, + id: 'Thijs', + position: { x: 0, y: 0 }, + data: { attributes: [] }, + }, + { + type: QueryElementTypes.Entity, + id: 'Airport', + position: { x: 0, y: 0 }, + data: { attributes: [] }, + }, + { + type: QueryElementTypes.Entity, + id: 'Airport2', + position: { x: 0, y: 0 }, + data: { attributes: [] }, + }, + { + type: QueryElementTypes.Entity, + id: 'Plane', + position: { x: 0, y: 0 }, + data: { attributes: [] }, + }, + { + type: QueryElementTypes.Entity, + id: 'Staff', + position: { x: 0, y: 0 }, + data: { attributes: [] }, + }, +]; + +/** Result links. */ +const edges: Edge[] = [ + { + id: 'Airport2:Airport', + label: 'Airport2:Airport', + type: 'nodeEdge', + source: 'Airport2', + target: 'Airport', + arrowHeadType: ArrowHeadType.Arrow, + data: { + d: '', + attributes: [], + }, + }, + { + id: 'Airport:Staff', + label: 'Airport:Staff', + type: 'nodeEdge', + source: 'Airport', + target: 'Staff', + arrowHeadType: ArrowHeadType.Arrow, + data: { d: '', attributes: [] }, + }, + { + id: 'Plane:Airport', + label: 'Plane:Airport', + type: 'nodeEdge', + source: 'Plane', + target: 'Airport', + arrowHeadType: ArrowHeadType.Arrow, + data: { d: '', attributes: [] }, + }, + { + id: 'Airport:Thijs', + label: 'Airport:Thijs', + type: 'nodeEdge', + source: 'Airport', + target: 'Thijs', + arrowHeadType: ArrowHeadType.Arrow, + data: { d: '', attributes: [] }, + }, + { + id: 'Thijs:Airport', + label: 'Thijs:Airport', + type: 'nodeEdge', + source: 'Thijs', + target: 'Airport', + arrowHeadType: ArrowHeadType.Arrow, + data: { d: '', attributes: [] }, + }, + { + id: 'Staff:Plane', + label: 'Staff:Plane', + type: 'nodeEdge', + source: 'Staff', + target: 'Plane', + arrowHeadType: ArrowHeadType.Arrow, + data: { d: '', attributes: [] }, + }, + { + id: 'Staff:Airport2', + label: 'Staff:Airport2', + type: 'nodeEdge', + source: 'Staff', + target: 'Airport2', + arrowHeadType: ArrowHeadType.Arrow, + data: { d: '', attributes: [] }, + }, + { + id: 'Airport2:Plane', + label: 'Airport2:Plane', + type: 'nodeEdge', + source: 'Airport2', + target: 'Plane', + arrowHeadType: ArrowHeadType.Arrow, + data: { d: '', attributes: [] }, + }, +]; diff --git a/libs/shared/lib/schema/pills/edges/node-edge.tsx b/libs/shared/lib/schema/pills/edges/node-edge.tsx index 335383f51..030980394 100644 --- a/libs/shared/lib/schema/pills/edges/node-edge.tsx +++ b/libs/shared/lib/schema/pills/edges/node-edge.tsx @@ -51,24 +51,13 @@ export default function NodeEdge({ */ useEffect(() => { if (!data.created) { - setRelationNodePosition( - centerX, - centerY, - id, - source, - target, - data.attributes - ); + setRelationNodePosition(centerX, centerY, id, source, target, data.attributes); data.created = true; } }); return ( - <g - stroke={theme.palette.custom.logo} - strokeWidth={0.5} - style={{ pointerEvents: 'none' }} - > + <g stroke={theme.palette.custom.logo} strokeWidth={0.5} style={{ pointerEvents: 'none' }}> <path type="smoothstep" id={id} @@ -76,9 +65,7 @@ export default function NodeEdge({ strokeWidth={0.5} style={style} // The d is used to create the path for the edge. - d={`M${sourceX},${sourceY}h ${data.d} L ${ - targetX + data.d - },${targetY} h ${-data.d}`} + d={`M${sourceX},${sourceY}h ${data.d} L ${targetX + data.d},${targetY} h ${-data.d}`} markerEnd={markerEnd} /> </g> diff --git a/libs/shared/lib/schema/pills/edges/self-edge.tsx b/libs/shared/lib/schema/pills/edges/self-edge.tsx index c01df3b84..3ea0bb005 100644 --- a/libs/shared/lib/schema/pills/edges/self-edge.tsx +++ b/libs/shared/lib/schema/pills/edges/self-edge.tsx @@ -52,14 +52,7 @@ export default function SelfEdge({ */ useEffect(() => { if (!data.created) { - setRelationNodePosition( - centerX, - centerY, - id, - source, - target, - data.attributes - ); + setRelationNodePosition(centerX, centerY, id, source, target, data.attributes); data.created = true; } }); @@ -71,11 +64,7 @@ export default function SelfEdge({ } return ( - <g - stroke={theme.palette.custom.logo} - strokeWidth={0.5} - style={{ pointerEvents: 'none' }} - > + <g stroke={theme.palette.custom.logo} strokeWidth={0.5} style={{ pointerEvents: 'none' }}> <path type="smoothstep" id={id} @@ -83,9 +72,7 @@ export default function SelfEdge({ strokeWidth={0.5} style={style} // The d is used to create the path for the edge. - d={`M${sourceX},${sourceY}h ${-data.d} v ${data.d} L ${ - targetX + data.d - },${targetY + data.d} v ${-data.d} h ${-data.d}`} + d={`M${sourceX},${sourceY}h ${-data.d} v ${data.d} L ${targetX + data.d},${targetY + data.d} v ${-data.d} h ${-data.d}`} markerEnd={markerEnd} /> </g> diff --git a/libs/shared/lib/schema/pills/nodes/entity/entity-node.stories.tsx b/libs/shared/lib/schema/pills/nodes/entity/entity-node.stories.tsx index 72f049dcb..0b1d03671 100644 --- a/libs/shared/lib/schema/pills/nodes/entity/entity-node.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/entity/entity-node.stories.tsx @@ -2,11 +2,7 @@ import React from 'react'; import { Meta, Story, ComponentStory } from '@storybook/react'; import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils'; -import { - colorPaletteConfigSlice, - schemaSlice, - setSchema, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, schemaSlice, setSchema } from '@graphpolaris/shared/lib/data-access/store'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; diff --git a/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx b/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx index 5e6261cd9..49d6a6500 100644 --- a/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx +++ b/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx @@ -11,15 +11,10 @@ import React, { useState } from 'react'; import { Node, Handle, Position, NodeProps } from 'reactflow'; import styles from './entity.module.scss'; -import { - calcWidthEntityNodeBox, - calculateAttributeQuality, - calculateEntityQuality, -} from '@graphpolaris/shared/lib/schema/schema-utils'; +import { calcWidthEntityNodeBox, calculateAttributeQuality, calculateEntityQuality } from '@graphpolaris/shared/lib/schema/schema-utils'; import { useTheme } from '@mui/material'; -import { - SchemaGraphNodeWithFunctions, -} from '../../../model/reactflow'; +import { SchemaReactflowNodeWithFunctions } from '../../../model/reactflow'; +import { QueryElementTypes } from '@graphpolaris/shared/lib/querybuilder'; /** * EntityNode is the node that represents the database entities. @@ -27,194 +22,155 @@ import { * It also has a text to display the entity name and show the amount of attributes it contains. * @param {NodeProps} param0 The data of an entity flow element. */ -export const EntityNode = React.memo( - ({ id, data }: NodeProps<SchemaGraphNodeWithFunctions>) => { - // console.log(data); +export const EntityNode = React.memo(({ id, data }: NodeProps<SchemaReactflowNodeWithFunctions>) => { + // console.log(data); - const [hidden, setHidden] = useState<boolean>(true); - const theme = useTheme(); + const [hidden, setHidden] = useState<boolean>(true); + const theme = useTheme(); - /** - * adds drag functionality in order to be able to drag the entityNode to the schema - * @param event React Mouse drag event - */ - const onDragStart = (event: React.DragEvent<HTMLDivElement>) => { - // console.log(id, data); - event.dataTransfer.setData( - 'application/reactflow', - JSON.stringify({ type: 'entity', name: id }) - ); - event.dataTransfer.effectAllowed = 'move'; - }; + /** + * adds drag functionality in order to be able to drag the entityNode to the schema + * @param event React Mouse drag event + */ + const onDragStart = (event: React.DragEvent<HTMLDivElement>) => { + // console.log('dragging entiry', id, data); + // console.log(id, data); + event.dataTransfer.setData('application/reactflow', JSON.stringify({ type: QueryElementTypes.Entity, name: id })); + event.dataTransfer.effectAllowed = 'move'; + }; - /** - * displays the NodeQualityPopup when clicked, and hides them when clicked again - */ - const onClickToggleNodeQualityPopup = (): void => { - data.toggleNodeQualityPopup(id); - }; + /** + * displays the NodeQualityPopup when clicked, and hides them when clicked again + */ + const onClickToggleNodeQualityPopup = (): void => { + data.toggleNodeQualityPopup(id); + }; - /** - * displays the attribute analytics popup menu when clicked, and hides them when clicked again - */ - const onClickToggleAttributeAnalyticsPopupMenu = (): void => { - data.toggleAttributeAnalyticsPopupMenu(id); - }; + /** + * displays the attribute analytics popup menu when clicked, and hides them when clicked again + */ + const onClickToggleAttributeAnalyticsPopupMenu = (): void => { + data.toggleAttributeAnalyticsPopupMenu(id); + }; - return ( + return ( + <div + className={styles.entityNode} + onDragStart={(event) => onDragStart(event)} + draggable + style={{ + backgroundColor: theme.palette.custom.nodesBase[0], + borderTop: `4px solid ${theme.palette.custom.nodesBase[0]}`, + borderBottom: `6px solid ${theme.palette.custom.elements.entityBase[0]}`, + }} + > <div - className={styles.entityNode} - onDragStart={(event) => onDragStart(event)} - draggable + className={styles.entityNodeAttributesBox} + onClick={() => onClickToggleAttributeAnalyticsPopupMenu()} style={{ - backgroundColor: theme.palette.custom.nodesBase[0], - borderTop: `4px solid ${theme.palette.custom.nodesBase[0]}`, - borderBottom: `6px solid ${theme.palette.custom.elements.entityBase[0]}`, + borderBottomColor: theme.palette.custom.elements.entityBase[0], + width: calcWidthEntityNodeBox(data.attributes.length, data.nodeCount) + 'ch', + background: `linear-gradient(-90deg, + ${theme.palette.custom.nodesBase} 0%, + ${theme.palette.custom.nodesBase} ${calculateAttributeQuality(data)}%, + ${theme.palette.custom.elements.entitySecond[0]} ${calculateAttributeQuality(data)}%)`, }} > - <div - className={styles.entityNodeAttributesBox} - onClick={() => onClickToggleAttributeAnalyticsPopupMenu()} + <span style={{ - borderBottomColor: theme.palette.custom.elements.entityBase[0], - width: - calcWidthEntityNodeBox(data.attributes.length, data.nodeCount) + - 'ch', - background: `linear-gradient(-90deg, - ${theme.palette.custom.nodesBase} 0%, - ${theme.palette.custom.nodesBase} ${calculateAttributeQuality( - data - )}%, - ${theme.palette.custom.elements.entitySecond[0] - } ${calculateAttributeQuality(data)}%)`, + paddingLeft: '5px', + float: 'left', }} > - <span - style={{ - paddingLeft: '5px', - float: 'left', - }} - > - A - </span> - <span className={styles.nodeSpan}>{data.attributes.length}</span> - </div> - <div - className={styles.entityNodeNodesBox} - onClick={() => onClickToggleNodeQualityPopup()} - style={{ - borderBottomColor: theme.palette.custom.elements.entityBase[0], - width: - calcWidthEntityNodeBox(data.attributes.length, data.nodeCount) + - 'ch', - background: `linear-gradient(-90deg, + A + </span> + <span className={styles.nodeSpan}>{data.attributes.length}</span> + </div> + <div + className={styles.entityNodeNodesBox} + onClick={() => onClickToggleNodeQualityPopup()} + style={{ + borderBottomColor: theme.palette.custom.elements.entityBase[0], + width: calcWidthEntityNodeBox(data.attributes.length, data.nodeCount) + 'ch', + background: `linear-gradient(-90deg, ${theme.palette.custom.nodesBase} 0%, ${theme.palette.custom.nodesBase} ${calculateEntityQuality(data)}%, - ${theme.palette.custom.elements.entitySecond[0] - } ${calculateEntityQuality(data)}%)`, + ${theme.palette.custom.elements.entitySecond[0]} ${calculateEntityQuality(data)}%)`, + }} + > + <span + style={{ + paddingLeft: '5px', + float: 'left', }} > - <span - style={{ - paddingLeft: '5px', - float: 'left', - }} - > - N - </span> - <span className={styles.nodeSpan}>{data.nodeCount}</span> - </div> - <Handle - style={{ pointerEvents: 'none' }} - id="entitySourceLeft" - position={Position.Left} - type="source" - hidden={ - Array.from(data.handles).includes('entitySourceLeft') ? false : true - } - ></Handle> - <Handle - style={{ pointerEvents: 'none' }} - id="entityTargetLeft" - position={Position.Left} - type="target" - hidden={ - Array.from(data.handles).includes('entityTargetLeft') ? false : true - } - ></Handle> - <Handle - style={{ pointerEvents: 'none' }} - id="entitySourceRight" - position={Position.Right} - type="source" - hidden={ - Array.from(data.handles).includes('entitySourceRight') - ? false - : true - } - ></Handle> - <Handle - style={{ pointerEvents: 'none' }} - id="entityTargetRight" - position={Position.Right} - type="target" - hidden={ - Array.from(data.handles).includes('entityTargetRight') - ? false - : true - } - ></Handle> - <Handle - style={{ pointerEvents: 'none' }} - id="entitySourceTop" - position={Position.Top} - type="source" - hidden={ - Array.from(data.handles).includes('entitySourceTop') ? false : true - } - ></Handle> - <Handle - style={{ pointerEvents: 'none' }} - id="entityTargetTop" - position={Position.Top} - type="target" - hidden={ - Array.from(data.handles).includes('entityTargetTop') ? false : true - } - ></Handle> - <Handle - style={{ pointerEvents: 'none' }} - id="entitySourceBottom" - position={Position.Bottom} - type="source" - hidden={ - Array.from(data.handles).includes('entitySourceBottom') - ? false - : true - } - ></Handle> - <Handle - style={{ pointerEvents: 'none' }} - id="entityTargetBottom" - position={Position.Bottom} - type="target" - hidden={ - Array.from(data.handles).includes('entityTargetBottom') - ? false - : true - } - ></Handle> - <div className={styles.nodeWrapper}> - <span - className={styles.nodeData} - style={{ color: theme.palette.custom.elementText }} - > - {id} - </span> - </div> + N + </span> + <span className={styles.nodeSpan}>{data.nodeCount}</span> + </div> + <Handle + style={{ pointerEvents: 'none' }} + id="entitySourceLeft" + position={Position.Left} + type="source" + // hidden={Array.from(data.handles).includes('entitySourceLeft') ? false : true} + ></Handle> + <Handle + style={{ pointerEvents: 'none' }} + id="entityTargetLeft" + position={Position.Left} + type="target" + // hidden={Array.from(data.handles).includes('entityTargetLeft') ? false : true} + ></Handle> + <Handle + style={{ pointerEvents: 'none' }} + id="entitySourceRight" + position={Position.Right} + type="source" + // hidden={Array.from(data.handles).includes('entitySourceRight') ? false : true} + ></Handle> + <Handle + style={{ pointerEvents: 'none' }} + id="entityTargetRight" + position={Position.Right} + type="target" + // hidden={Array.from(data.handles).includes('entityTargetRight') ? false : true} + ></Handle> + <Handle + style={{ pointerEvents: 'none' }} + id="entitySourceTop" + position={Position.Top} + type="source" + // hidden={Array.from(data.handles).includes('entitySourceTop') ? false : true} + ></Handle> + <Handle + style={{ pointerEvents: 'none' }} + id="entityTargetTop" + position={Position.Top} + type="target" + // hidden={Array.from(data.handles).includes('entityTargetTop') ? false : true} + ></Handle> + <Handle + style={{ pointerEvents: 'none' }} + id="entitySourceBottom" + position={Position.Bottom} + type="source" + // hidden={Array.from(data.handles).includes('entitySourceBottom') ? false : true} + ></Handle> + <Handle + style={{ pointerEvents: 'none' }} + id="entityTargetBottom" + position={Position.Bottom} + type="target" + // hidden={Array.from(data.handles).includes('entityTargetBottom') ? false : true} + ></Handle> + <div className={styles.nodeWrapper}> + <span className={styles.nodeData} style={{ color: theme.palette.custom.elementText }}> + {id} + </span> </div> - ); - } -); + </div> + ); +}); EntityNode.displayName = 'EntityNode'; diff --git a/libs/shared/lib/schema/pills/nodes/popup/attribute-analytics-popup-menu.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/attribute-analytics-popup-menu.stories.tsx index 3e12e835b..ad5a59f0d 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/attribute-analytics-popup-menu.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/attribute-analytics-popup-menu.stories.tsx @@ -4,11 +4,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { AttributeAnalyticsPopupMenu } from './attribute-analytics-popup-menu'; import { AttributeCategory, NodeType } from '../../../model/reactflow'; @@ -55,20 +51,16 @@ export const Default: Story = { attribute: { name: 'Foo', type: 'string', + attributes: [], }, }, ], isAttributeDataIn: false, - onClickCloseButton: () => { }, - onClickPlaceInQueryBuilderButton: (name: string, type) => { }, - searchForAttributes: (id: string, searchbarValue: string) => { }, - resetAttributeFilters: (id: string) => { }, - applyAttributeFilters: ( - id: string, - category: AttributeCategory, - predicate: string, - percentage: number - ) => { }, + onClickCloseButton: () => {}, + onClickPlaceInQueryBuilderButton: (name: string, type) => {}, + searchForAttributes: (id: string, searchbarValue: string) => {}, + resetAttributeFilters: (id: string) => {}, + applyAttributeFilters: (id: string, category: AttributeCategory, predicate: string, percentage: number) => {}, }, }, }; diff --git a/libs/shared/lib/schema/pills/nodes/popup/attribute-analytics-popup-menu.tsx b/libs/shared/lib/schema/pills/nodes/popup/attribute-analytics-popup-menu.tsx index bc21aacc0..5fbfd9f5b 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/attribute-analytics-popup-menu.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/attribute-analytics-popup-menu.tsx @@ -8,22 +8,10 @@ /* The comment above was added so the code coverage wouldn't count this file towards code coverage. * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ -import { - ButtonBase, - MenuItem, - Accordion, - AccordionSummary, - AccordionDetails, - Typography, - useTheme, -} from '@mui/material'; +import { ButtonBase, MenuItem, Accordion, AccordionSummary, AccordionDetails, Typography, useTheme } from '@mui/material'; import { ExpandMore, Visibility } from '@mui/icons-material'; import React, { ReactElement } from 'react'; -import { - AttributeAnalyticsData, - AttributeWithData, - NodeType, -} from '../../../model/reactflow'; +import { AttributeAnalyticsData, AttributeWithData, NodeType } from '../../../model/reactflow'; import { NodeProps } from 'reactflow'; import './attribute-analytics-popup-menu.module.scss'; @@ -31,15 +19,11 @@ import './attribute-analytics-popup-menu.module.scss'; * AttributeAnalyticsPopupMenuNode is the node that represents the popup menu that shows the attribute analytics for an entity or relation * @param data Input data of type AttributeAnalyticsData, which is for the popup menu. */ -export const AttributeAnalyticsPopupMenu = ({ - data, -}: NodeProps<AttributeAnalyticsData>) => { +export const AttributeAnalyticsPopupMenu = ({ data }: NodeProps<AttributeAnalyticsData>) => { const theme = useTheme(); - if (data == undefined) - throw new Error('No Attribute Analytics data is available for the node.'); + if (data == undefined) throw new Error('No Attribute Analytics data is available for the node.'); let entityOrRelationBase: [string, string, string]; - if (data.nodeType == NodeType.entity) - entityOrRelationBase = theme.palette.custom.elements.entityBase; + if (data.nodeType == NodeType.entity) entityOrRelationBase = theme.palette.custom.elements.entityBase; else entityOrRelationBase = theme.palette.custom.elements.relationBase; let attributesDivs: any[] = []; @@ -47,15 +31,10 @@ export const AttributeAnalyticsPopupMenu = ({ data.attributes.forEach((attributeItem: any) => { attributesDivs.push( <Accordion className="attributesAccordion"> - <AccordionSummary - className="attribute" - expandIcon={<ExpandMore className="expandIcon" />} - > + <AccordionSummary className="attribute" expandIcon={<ExpandMore className="expandIcon" />}> {attributeItem.attribute.name} </AccordionSummary> - <AccordionDetails className="accordionDetails"> - {attributeItem.category} - </AccordionDetails> + <AccordionDetails className="accordionDetails">{attributeItem.category}</AccordionDetails> <AccordionDetails className="accordionDetails"> <span>Null values:</span> <span @@ -78,12 +57,7 @@ export const AttributeAnalyticsPopupMenu = ({ <AccordionDetails className="accordionDetails"> <div className="attributeButtons" - onClick={() => - data.onClickPlaceInQueryBuilderButton( - attributeItem.attribute.name, - attributeItem.attribute.type - ) - } + onClick={() => data.onClickPlaceInQueryBuilderButton(attributeItem.attribute.name, attributeItem.attribute.type)} > <span>Place in query builder</span> </div> @@ -95,21 +69,13 @@ export const AttributeAnalyticsPopupMenu = ({ data.attributes.forEach((attributeItem: AttributeWithData) => { attributesDivs.push( <Accordion className="attributesAccordion"> - <AccordionSummary - className="attribute" - expandIcon={<ExpandMore className="expandIcon" />} - > + <AccordionSummary className="attribute" expandIcon={<ExpandMore className="expandIcon" />}> {attributeItem.attribute.name} </AccordionSummary> <AccordionDetails className="accordionDetails"> <div className="attributeButtons" - onClick={() => - data.onClickPlaceInQueryBuilderButton( - attributeItem.attribute.name, - attributeItem.attribute.type - ) - } + onClick={() => data.onClickPlaceInQueryBuilderButton(attributeItem.attribute.name, attributeItem.attribute.type)} > <span>Place in query builder</span> </div> diff --git a/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.stories.tsx index 79272991e..84a64a45b 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.stories.tsx @@ -4,11 +4,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { NodeQualityEntityPopupNode } from './node-quality-entity-popup'; diff --git a/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.tsx b/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.tsx index 609226cbc..4071f98c7 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/node-quality-entity-popup.tsx @@ -18,12 +18,9 @@ import { NodeQualityDataForEntities } from '../../../model/reactflow'; * NodeQualityEntityPopupNode is the node that represents the popup that shows the node quality for an entity * @param data Input data of type NodeQualityDataForEntities, which is for the popup. */ -export const NodeQualityEntityPopupNode = ({ - data, -}: NodeProps<NodeQualityDataForEntities>) => { +export const NodeQualityEntityPopupNode = ({ data }: NodeProps<NodeQualityDataForEntities>) => { const theme = useTheme(); - if (data == undefined) - throw new Error('No node quality data is available for this node.'); + if (data == undefined) throw new Error('No node quality data is available for this node.'); if (data.isAttributeDataIn) return ( diff --git a/libs/shared/lib/schema/pills/nodes/popup/node-quality-relation-popup.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/node-quality-relation-popup.stories.tsx index 3da710c75..a29cba028 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/node-quality-relation-popup.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/node-quality-relation-popup.stories.tsx @@ -4,11 +4,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { NodeQualityRelationPopupNode } from './node-quality-relation-popup'; diff --git a/libs/shared/lib/schema/pills/nodes/popup/node-quality-relation-popup.tsx b/libs/shared/lib/schema/pills/nodes/popup/node-quality-relation-popup.tsx index 4a2307723..a1933b4e1 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/node-quality-relation-popup.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/node-quality-relation-popup.tsx @@ -19,68 +19,65 @@ import './node-quality-popup.module.scss'; * NodeQualityRelationPopupNode is the node that represents the popup that shows the node quality for a relation * @param data Input data of type NodeQualityDataForRelations, which is for the popup. */ -export const NodeQualityRelationPopupNode = React.memo( - ({ data }: NodeProps<NodeQualityDataForRelations>) => { - const theme = useTheme(); - if (data == undefined) - throw new Error('No node quality data is available for this node.'); +export const NodeQualityRelationPopupNode = React.memo(({ data }: NodeProps<NodeQualityDataForRelations>) => { + const theme = useTheme(); + if (data == undefined) throw new Error('No node quality data is available for this node.'); - if (data.isAttributeDataIn) - return ( - <div> - <div className="title"> - <span id="name">Nodes</span> - <span className="rightSideValue">{data.nodeCount}</span> + if (data.isAttributeDataIn) + return ( + <div> + <div className="title"> + <span id="name">Nodes</span> + <span className="rightSideValue">{data.nodeCount}</span> + </div> + <div className="information"> + <div> + <span>Null attributes</span> + <span className="rightSideValue">{data.attributeNullCount}</span> </div> - <div className="information"> - <div> - <span>Null attributes</span> - <span className="rightSideValue">{data.attributeNullCount}</span> - </div> - <div> - <span>From ratio</span> - <span className="rightSideValue">{data.fromRatio}</span> - </div> - <div> - <span>To ratio</span> - <span className="rightSideValue">{data.toRatio}</span> - </div> + <div> + <span>From ratio</span> + <span className="rightSideValue">{data.fromRatio}</span> </div> - <div className="closeButtonWrapper"> - <ButtonBase - onClick={() => data.onClickCloseButton()} - id="closeButton" - style={{ - backgroundColor: theme.palette.custom.elements.relationBase[0], - }} - > - Close - </ButtonBase> + <div> + <span>To ratio</span> + <span className="rightSideValue">{data.toRatio}</span> </div> </div> - ); - else - return ( - <div> - <div className="title"> - <span id="name">Nodes</span> - <span className="rightSideValue">{data.nodeCount}</span> - </div> - <div className="information"></div> - <div className="closeButtonWrapper"> - <ButtonBase - onClick={() => data.onClickCloseButton()} - id="closeButton" - style={{ - backgroundColor: theme.palette.custom.elements.relationBase[0], - }} - > - Close - </ButtonBase> - </div> + <div className="closeButtonWrapper"> + <ButtonBase + onClick={() => data.onClickCloseButton()} + id="closeButton" + style={{ + backgroundColor: theme.palette.custom.elements.relationBase[0], + }} + > + Close + </ButtonBase> + </div> + </div> + ); + else + return ( + <div> + <div className="title"> + <span id="name">Nodes</span> + <span className="rightSideValue">{data.nodeCount}</span> + </div> + <div className="information"></div> + <div className="closeButtonWrapper"> + <ButtonBase + onClick={() => data.onClickCloseButton()} + id="closeButton" + style={{ + backgroundColor: theme.palette.custom.elements.relationBase[0], + }} + > + Close + </ButtonBase> </div> - ); - } -); + </div> + ); +}); NodeQualityRelationPopupNode.displayName = 'NodeQualityRelationPopupNode'; diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/attribute-analytics-popup-menu.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/attribute-analytics-popup-menu.stories.tsx index e265579a3..053778e32 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/attribute-analytics-popup-menu.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/attribute-analytics-popup-menu.stories.tsx @@ -4,11 +4,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { AttributeAnalyticsPopupMenu } from './attribute-analytics-popup-menu'; import { AttributeCategory, NodeType } from '../../../../model/reactflow'; @@ -55,20 +51,16 @@ export const Default: Story = { attribute: { name: 'Foo', type: 'string', + attributes: [], }, }, ], isAttributeDataIn: false, - onClickCloseButton: () => { }, - onClickPlaceInQueryBuilderButton: (name: string, type) => { }, - searchForAttributes: (id: string, searchbarValue: string) => { }, - resetAttributeFilters: (id: string) => { }, - applyAttributeFilters: ( - id: string, - category: AttributeCategory, - predicate: string, - percentage: number - ) => { }, + onClickCloseButton: () => {}, + onClickPlaceInQueryBuilderButton: (name: string, type) => {}, + searchForAttributes: (id: string, searchbarValue: string) => {}, + resetAttributeFilters: (id: string) => {}, + applyAttributeFilters: (id: string, category: AttributeCategory, predicate: string, percentage: number) => {}, }, }, }; diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/attribute-analytics-popup-menu.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/attribute-analytics-popup-menu.tsx index 8ba0bd700..6db13f653 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/attribute-analytics-popup-menu.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/attribute-analytics-popup-menu.tsx @@ -9,20 +9,10 @@ * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ -import { - ButtonBase, - Accordion, - AccordionSummary, - AccordionDetails, - useTheme, -} from '@mui/material'; +import { ButtonBase, Accordion, AccordionSummary, AccordionDetails, useTheme } from '@mui/material'; import { ExpandMore, Visibility } from '@mui/icons-material'; import React, { ReactElement } from 'react'; -import { - NodeType, - AttributeAnalyticsData, - AttributeCategory, -} from '../../../../model/reactflow'; +import { NodeType, AttributeAnalyticsData, AttributeCategory } from '../../../../model/reactflow'; import { Filter } from './filterbar'; import { Search } from './searchbar'; import { NodeProps } from 'reactflow'; @@ -31,16 +21,12 @@ import { NodeProps } from 'reactflow'; * AttributeAnalyticsPopupMenuNode is the node that represents the popup menu that shows the attribute analytics for an entity or relation * @param data Input data of type AttributeAnalyticsData, which is for the popup menu. */ -export const AttributeAnalyticsPopupMenu = ({ - data, -}: NodeProps<AttributeAnalyticsData>) => { +export const AttributeAnalyticsPopupMenu = ({ data }: NodeProps<AttributeAnalyticsData>) => { const theme = useTheme(); - if (data == undefined) - throw new Error('No Attribute Analytics data is available for the node.'); + if (data == undefined) throw new Error('No Attribute Analytics data is available for the node.'); let entityOrRelationBase: [string, string, string]; - if (data.nodeType == NodeType.entity) - entityOrRelationBase = theme.palette.custom.elements.entityBase; + if (data.nodeType == NodeType.entity) entityOrRelationBase = theme.palette.custom.elements.entityBase; else entityOrRelationBase = theme.palette.custom.elements.relationBase; let dataCopy = { ...data }; @@ -53,23 +39,16 @@ export const AttributeAnalyticsPopupMenu = ({ * @param attributeData Data that is contained in the attributes. * @returns A list of divs that contain the appearance and information of the attributes. */ - function calculateAttributeDivs( - attributeData: AttributeAnalyticsData - ): any[] { + function calculateAttributeDivs(attributeData: AttributeAnalyticsData): any[] { let attributesDivs: any[] = []; if (attributeData.isAttributeDataIn) { attributeData.attributes.forEach((attributeItem: any) => { attributesDivs.push( <Accordion className="attributesAccordion"> - <AccordionSummary - className="attribute" - expandIcon={<ExpandMore className="expandIcon" />} - > + <AccordionSummary className="attribute" expandIcon={<ExpandMore className="expandIcon" />}> {attributeItem.attribute.name} </AccordionSummary> - <AccordionDetails className="accordionDetails"> - {attributeItem.category} - </AccordionDetails> + <AccordionDetails className="accordionDetails">{attributeItem.category}</AccordionDetails> <AccordionDetails className="accordionDetails"> <span>Null-values:</span> <span @@ -92,12 +71,7 @@ export const AttributeAnalyticsPopupMenu = ({ <AccordionDetails className="accordionDetails"> <div className="attributeButtons" - onClick={() => - attributeData.onClickPlaceInQueryBuilderButton( - attributeItem.attribute.name, - attributeItem.attribute.type - ) - } + onClick={() => attributeData.onClickPlaceInQueryBuilderButton(attributeItem.attribute.name, attributeItem.attribute.type)} > <span>Place in query builder</span> </div> @@ -109,21 +83,13 @@ export const AttributeAnalyticsPopupMenu = ({ attributeData.attributes.forEach((attributeItem) => { attributesDivs.push( <Accordion className="attributesAccordion"> - <AccordionSummary - className="attribute" - expandIcon={<ExpandMore className="expandIcon" />} - > + <AccordionSummary className="attribute" expandIcon={<ExpandMore className="expandIcon" />}> {attributeItem.attribute.name} </AccordionSummary> <AccordionDetails className="accordionDetails"> <div className="attributeButtons" - onClick={() => - attributeData.onClickPlaceInQueryBuilderButton( - attributeItem.attribute.name, - attributeItem.attribute.type - ) - } + onClick={() => attributeData.onClickPlaceInQueryBuilderButton(attributeItem.attribute.name, attributeItem.attribute.type)} > <span>Place in query builder</span> </div> @@ -140,35 +106,19 @@ export const AttributeAnalyticsPopupMenu = ({ * @param data This contains the data of the attributes. * @param attributesDivs This contains the list of divs of the attributes. */ - function renderPopupMenu( - data: AttributeAnalyticsData, - attributesDivs: any[] - ) { + function renderPopupMenu(data: AttributeAnalyticsData, attributesDivs: any[]) { return ( <div> <div className="title"> <span id="name">Attributes</span> <span className="rightSideValue">{data.attributes.length}</span> </div> - <Search - searchForAttributes={(searchbarValue: string) => - data.searchForAttributes(data.nodeID, searchbarValue) - } - /> + <Search searchForAttributes={(searchbarValue: string) => data.searchForAttributes(data.nodeID, searchbarValue)} /> <Filter data={data} resetAttributeFilters={() => data.resetAttributeFilters(data.nodeID)} - applyAttributeFilters={( - dataType: AttributeCategory, - predicate: string, - percentage: number - ) => - data.applyAttributeFilters( - data.nodeID, - dataType, - predicate, - percentage - ) + applyAttributeFilters={(dataType: AttributeCategory, predicate: string, percentage: number) => + data.applyAttributeFilters(data.nodeID, dataType, predicate, percentage) } /> diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/filterbar.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/filterbar.stories.tsx index cff36d89e..cb8fa3223 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/filterbar.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/filterbar.stories.tsx @@ -4,11 +4,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { Filter } from './filterbar'; diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/filterbar.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/filterbar.tsx index 2c044fdc1..df5f60ea2 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/filterbar.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/filterbar.tsx @@ -9,30 +9,16 @@ * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ -import { - Accordion, - AccordionDetails, - AccordionSummary, - ButtonBase, - useTheme, -} from '@mui/material'; +import { Accordion, AccordionDetails, AccordionSummary, ButtonBase, useTheme } from '@mui/material'; import { ExpandMore } from '@mui/icons-material'; import React, { ReactElement, useState } from 'react'; -import { - AttributeAnalyticsData, - AttributeCategory, - NodeType, -} from '../../../../model/reactflow'; +import { AttributeAnalyticsData, AttributeCategory, NodeType } from '../../../../model/reactflow'; /** The typing for the props of the filter bar. */ type FilterbarProps = { data: AttributeAnalyticsData; resetAttributeFilters(): void; - applyAttributeFilters( - dataType: AttributeCategory, - predicate: string, - percentage: number - ): void; + applyAttributeFilters(dataType: AttributeCategory, predicate: string, percentage: number): void; }; /** The variables in the state of the filter bar. */ @@ -80,9 +66,7 @@ export const Filter = (props: FilterbarProps) => { * @param event The event that contains the value of the chosen null-values-amount. */ const nullValuesValueChanged = (event: any) => { - const value = event.target.validity.valid - ? event.target.value - : state.nullValuesValue; + const value = event.target.validity.valid ? event.target.value : state.nullValuesValue; let newValue = value; if (Number(newValue) < 0) newValue = '0'; if (Number(newValue) > 100) newValue = '100'; @@ -107,10 +91,7 @@ export const Filter = (props: FilterbarProps) => { */ const applyFilters = () => { // Check if the chosen null-amount-value is valid. - if ( - !(state.nullValuesValue == '') && - Number.isNaN(Number(state.nullValuesValue)) - ) { + if (!(state.nullValuesValue == '') && Number.isNaN(Number(state.nullValuesValue))) { setState({ ...state, }); @@ -119,10 +100,8 @@ export const Filter = (props: FilterbarProps) => { // Sets the data-type. let dataType; - if (state.dataTypeValue == 'Categorical') - dataType = AttributeCategory.categorical; - else if (state.dataTypeValue == 'Numerical') - dataType = AttributeCategory.numerical; + if (state.dataTypeValue == 'Categorical') dataType = AttributeCategory.categorical; + else if (state.dataTypeValue == 'Numerical') dataType = AttributeCategory.numerical; else if (state.dataTypeValue == 'Other') dataType = AttributeCategory.other; else dataType = AttributeCategory.undefined; @@ -131,11 +110,7 @@ export const Filter = (props: FilterbarProps) => { if (state.nullValuesValue == '') percentage = -1; else percentage = Number(state.nullValuesValue); - props.applyAttributeFilters( - dataType, - state.nullValuesPredicate, - percentage - ); + props.applyAttributeFilters(dataType, state.nullValuesPredicate, percentage); }; // Makes sure that the hovered button get the right hover-state. @@ -159,36 +134,24 @@ export const Filter = (props: FilterbarProps) => { }; let entityOrRelationBase: [string, string, string]; - if (props.data.nodeType == NodeType.entity) - entityOrRelationBase = theme.palette.custom.elements.entityBase; + if (props.data.nodeType == NodeType.entity) entityOrRelationBase = theme.palette.custom.elements.entityBase; else entityOrRelationBase = theme.palette.custom.elements.relationBase; let applyButtonColor; - if (state.onHoverApplyButton) - applyButtonColor = '#' + entityOrRelationBase[0]; + if (state.onHoverApplyButton) applyButtonColor = '#' + entityOrRelationBase[0]; else applyButtonColor = 'inherit'; let resetButtonColor; - if (state.onHoverResetButton) - resetButtonColor = '#' + entityOrRelationBase[0]; + if (state.onHoverResetButton) resetButtonColor = '#' + entityOrRelationBase[0]; else resetButtonColor = 'inherit'; return ( <div className="bar"> - <Accordion - className={['attributesAccordion', 'filterbarAccordion'].join(' ')} - > - <AccordionSummary - className="filterbarSummary" - expandIcon={<ExpandMore className="expandIcon" />} - > + <Accordion className={['attributesAccordion', 'filterbarAccordion'].join(' ')}> + <AccordionSummary className="filterbarSummary" expandIcon={<ExpandMore className="expandIcon" />}> Filter bar </AccordionSummary> - <AccordionDetails - className={['accordionDetails', 'accordionDetailsFilterbar'].join( - ' ' - )} - > + <AccordionDetails className={['accordionDetails', 'accordionDetailsFilterbar'].join(' ')}> <span>Datatype:</span> <span> <select @@ -206,11 +169,7 @@ export const Filter = (props: FilterbarProps) => { </select> </span> </AccordionDetails> - <AccordionDetails - className={['accordionDetails', 'accordionDetailsFilterbar'].join( - ' ' - )} - > + <AccordionDetails className={['accordionDetails', 'accordionDetailsFilterbar'].join(' ')}> <span>Null-values:</span> <span className={['nullAmountValue', 'nullValuesBox'].join(' ')} @@ -232,11 +191,7 @@ export const Filter = (props: FilterbarProps) => { </span> <span> <select - className={[ - 'nullAmountValue', - 'dataTypeSelect', - 'predicateSelect', - ].join(' ')} + className={['nullAmountValue', 'dataTypeSelect', 'predicateSelect'].join(' ')} style={{ backgroundColor: entityOrRelationBase[0], }} diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-entity-popup.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-entity-popup.stories.tsx index 57e956e88..76f59b77c 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-entity-popup.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-entity-popup.stories.tsx @@ -4,11 +4,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { NodeQualityEntityPopupNode } from './node-quality-entity-popup'; diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-entity-popup.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-entity-popup.tsx index 1e9b666e6..da68e57ad 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-entity-popup.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-entity-popup.tsx @@ -18,12 +18,9 @@ import { NodeQualityDataForEntities } from '../../../../model/reactflow'; * NodeQualityEntityPopupNode is the node that represents the popup that shows the node quality for an entity * @param data Input data of type NodeQualityDataForEntities, which is for the popup. */ -export const NodeQualityEntityPopupNode = ({ - data, -}: NodeProps<NodeQualityDataForEntities>) => { +export const NodeQualityEntityPopupNode = ({ data }: NodeProps<NodeQualityDataForEntities>) => { const theme = useTheme(); - if (data == undefined) - throw new Error('No node quality data is available for this node.'); + if (data == undefined) throw new Error('No node quality data is available for this node.'); if (data.isAttributeDataIn) return ( @@ -39,9 +36,7 @@ export const NodeQualityEntityPopupNode = ({ </div> <div> <span>Not connected</span> - <span className="rightSideValue"> - {data.notConnectedNodeCount * 100}% - </span> + <span className="rightSideValue">{data.notConnectedNodeCount * 100}%</span> </div> </div> <div className="closeButtonWrapper"> diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-relation-popup.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-relation-popup.stories.tsx index b67629a65..33dc68b42 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-relation-popup.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-relation-popup.stories.tsx @@ -4,11 +4,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { NodeQualityRelationPopupNode } from './node-quality-relation-popup'; diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-relation-popup.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-relation-popup.tsx index 7f99a7af1..a851e0085 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-relation-popup.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/node-quality-relation-popup.tsx @@ -12,21 +12,15 @@ import { ButtonBase, useTheme } from '@mui/material'; import React from 'react'; import { NodeProps } from 'reactflow'; -import { - NodeQualityDataForRelations, - NodeType, -} from '../../../../model/reactflow'; +import { NodeQualityDataForRelations, NodeType } from '../../../../model/reactflow'; /** * NodeQualityRelationPopupNode is the node that represents the popup that shows the node quality for a relation * @param data Input data of type NodeQualityDataForRelations, which is for the popup. */ -export const NodeQualityRelationPopupNode = ({ - data, -}: NodeProps<NodeQualityDataForRelations>) => { +export const NodeQualityRelationPopupNode = ({ data }: NodeProps<NodeQualityDataForRelations>) => { const theme = useTheme(); - if (data == undefined) - throw new Error('No node quality data is available for this node.'); + if (data == undefined) throw new Error('No node quality data is available for this node.'); if (data.isAttributeDataIn) return ( diff --git a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/searchbar.stories.tsx b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/searchbar.stories.tsx index 59b0170cd..c9987ffb4 100644 --- a/libs/shared/lib/schema/pills/nodes/popup/popupmenus/searchbar.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/popup/popupmenus/searchbar.stories.tsx @@ -4,11 +4,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; -import { - colorPaletteConfigSlice, - querybuilderSlice, - schemaSlice, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, querybuilderSlice, schemaSlice } from '@graphpolaris/shared/lib/data-access/store'; import { ReactFlowProvider } from 'reactflow'; import { Search } from './searchbar'; diff --git a/libs/shared/lib/schema/pills/nodes/relation/relation-node.stories.tsx b/libs/shared/lib/schema/pills/nodes/relation/relation-node.stories.tsx index fafa06d8b..836d745c7 100644 --- a/libs/shared/lib/schema/pills/nodes/relation/relation-node.stories.tsx +++ b/libs/shared/lib/schema/pills/nodes/relation/relation-node.stories.tsx @@ -2,11 +2,7 @@ import React from 'react'; import { Meta, Story, ComponentStory } from '@storybook/react'; import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils'; -import { - colorPaletteConfigSlice, - schemaSlice, - setSchema, -} from '@graphpolaris/shared/lib/data-access/store'; +import { colorPaletteConfigSlice, schemaSlice, setSchema } from '@graphpolaris/shared/lib/data-access/store'; import { GraphPolarisThemeProvider } from '@graphpolaris/shared/lib/data-access/theme'; import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; diff --git a/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx b/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx index e209a5fd9..6eab676f8 100644 --- a/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx +++ b/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx @@ -19,186 +19,160 @@ import { calculateRelationQuality, } from '@graphpolaris/shared/lib/schema/schema-utils'; import { useTheme } from '@mui/material'; -import { - SchemaGraphRelation, - SchemaGraphRelationWithFunctions, -} from '../../../model/reactflow'; +import { SchemaReactflowRelation, SchemaReactflowRelationWithFunctions } from '../../../model/reactflow'; +import { QueryElementTypes } from '@graphpolaris/shared/lib/querybuilder'; /** * Relation node component that renders a relation node for the schema. * Can be dragged and dropped to the query builder. * @param {NodeProps} param0 The data of an entity flow element. */ -export const RelationNode = React.memo( - ({ id, data }: NodeProps<SchemaGraphRelationWithFunctions>) => { - const [hidden, setHidden] = useState<boolean>(true); - const theme = useTheme(); - // console.log(data); +export const RelationNode = React.memo(({ id, data }: NodeProps<SchemaReactflowRelationWithFunctions>) => { + const [hidden, setHidden] = useState<boolean>(true); + const theme = useTheme(); + // console.log(data); - /** - * Adds drag functionality in order to be able to drag the relationNode to the schema. - * @param event React Mouse drag event. - */ - const onDragStart = (event: React.DragEvent<HTMLDivElement>) => { - // console.log(id, data); - event.dataTransfer.setData( - 'application/reactflow', - JSON.stringify({ - type: 'relation', - name: id, //TODO id? - from: data.from, - to: data.to, - collection: data.collection, - }) - ); - event.dataTransfer.effectAllowed = 'move'; - }; + /** + * Adds drag functionality in order to be able to drag the relationNode to the schema. + * @param event React Mouse drag event. + */ + const onDragStart = (event: React.DragEvent<HTMLDivElement>) => { + console.log('dragging relation', id, data); + event.dataTransfer.setData( + 'application/reactflow', + JSON.stringify({ + type: QueryElementTypes.Relation, + name: id, //TODO id? + from: data.from, + to: data.to, + collection: data.collection, + label: data.label, + }) + ); + event.dataTransfer.effectAllowed = 'move'; + }; - /** - * Displays the NodeQualityPopup when clicked, and hides them when clicked again - */ - const onClickToggleNodeQualityPopup = (): void => { - data.toggleNodeQualityPopup(data.collection); - }; + /** + * Displays the NodeQualityPopup when clicked, and hides them when clicked again + */ + const onClickToggleNodeQualityPopup = (): void => { + data.toggleNodeQualityPopup(data.collection); + }; - /** - * displays the attribute analytics popup menu when clicked, and hides them when clicked again - */ - const onClickToggleAttributeAnalyticsPopupMenu = (): void => { - data.toggleAttributeAnalyticsPopupMenu(data.collection); - }; + /** + * displays the attribute analytics popup menu when clicked, and hides them when clicked again + */ + const onClickToggleAttributeAnalyticsPopupMenu = (): void => { + data.toggleAttributeAnalyticsPopupMenu(data.collection); + }; - const widthExternalBoxes = data.attributes - ? calcWidthRelationNodeBox(data.attributes.length, data.nodeCount) - : 0; + const widthExternalBoxes = data.attributes ? calcWidthRelationNodeBox(data.attributes.length, data.nodeCount) : 0; - return ( - <div - onDragStart={(event) => onDragStart(event)} - draggable + return ( + <div + onDragStart={(event) => onDragStart(event)} + draggable // style={{ width: 100, height: 100 }} + > + <div + className={styles.relationNode} + style={{ + background: theme.palette.custom.nodesBase[0], + borderTop: `4px solid ${theme.palette.custom.nodesBase[0]}`, + borderBottom: `6px solid ${theme.palette.custom.elements.relationBase[0]}`, + }} > <div - className={styles.relationNode} + className={[styles.relationNodeTriangleGeneral, styles.relationNodeTriangleLeft].join(' ')} + style={{ + borderRightColor: theme.palette.custom.nodesBase[0], + }} + ></div> + <div + className={[styles.relationNodeTriangleGeneral, styles.relationNodeSmallTriangleLeft].join(' ')} + style={{ + borderRightColor: theme.palette.custom.elements.relationBase[0], + }} + ></div> + <div + className={[styles.relationNodeTriangleGeneral, styles.relationNodeTriangleRight].join(' ')} + style={{ borderLeftColor: theme.palette.custom.nodesBase[0] }} + ></div> + <div + className={[styles.relationNodeTriangleGeneral, styles.relationNodeSmallTriangleRight].join(' ')} style={{ - background: theme.palette.custom.nodesBase[0], - borderTop: `4px solid ${theme.palette.custom.nodesBase[0]}`, - borderBottom: `6px solid ${theme.palette.custom.elements.relationBase[0]}`, + borderLeftColor: theme.palette.custom.elements.relationBase[0], + }} + ></div> + <Handle + style={{ + pointerEvents: 'none', + borderBottomColor: theme.palette.custom.nodesBase[0], + }} + className={styles.arrowup} + id="entitySourceLeft" + position={Position.Top} + type="source" + ></Handle> + <div + className={styles.relationNodeAttributesBox} + onClick={() => onClickToggleAttributeAnalyticsPopupMenu()} + style={{ + width: widthExternalBoxes + 'px', + backgroundImage: `linear-gradient(-90deg, + ${theme.palette.custom.nodesBase} 0%, + ${theme.palette.custom.nodesBase} ${calculateAttributeQuality(data)}%, + ${theme.palette.custom.elements.relationSecond[0]} ${calculateAttributeQuality(data)}%)`, }} > - <div - className={[ - styles.relationNodeTriangleGeneral, - styles.relationNodeTriangleLeft, - ].join(' ')} - style={{ - borderRightColor: theme.palette.custom.nodesBase[0], - }} - ></div> - <div - className={[ - styles.relationNodeTriangleGeneral, - styles.relationNodeSmallTriangleLeft, - ].join(' ')} - style={{ - borderRightColor: theme.palette.custom.elements.relationBase[0], - }} - ></div> - <div - className={[ - styles.relationNodeTriangleGeneral, - styles.relationNodeTriangleRight, - ].join(' ')} - style={{ borderLeftColor: theme.palette.custom.nodesBase[0] }} - ></div> - <div - className={[ - styles.relationNodeTriangleGeneral, - styles.relationNodeSmallTriangleRight, - ].join(' ')} + <span style={{ - borderLeftColor: theme.palette.custom.elements.relationBase[0], - }} - ></div> - <Handle - style={{ - pointerEvents: 'none', - borderBottomColor: theme.palette.custom.nodesBase[0], - }} - className={styles.arrowup} - id="entitySourceLeft" - position={Position.Top} - type="source" - ></Handle> - <div - className={styles.relationNodeAttributesBox} - onClick={() => onClickToggleAttributeAnalyticsPopupMenu()} - style={{ - width: widthExternalBoxes + 'px', - backgroundImage: `linear-gradient(-90deg, - ${theme.palette.custom.nodesBase} 0%, - ${theme.palette.custom.nodesBase} ${calculateAttributeQuality( - data - )}%, - ${theme.palette.custom.elements.relationSecond[0] - } ${calculateAttributeQuality(data)}%)`, + paddingLeft: '25px', + float: 'left', }} > - <span - style={{ - paddingLeft: '25px', - float: 'left', - }} - > - A - </span> - <span className={styles.nodeSpan}>{data.attributes.length}</span> - </div> - <div - className={styles.relationNodeNodesBox} - onClick={() => onClickToggleNodeQualityPopup()} - style={{ - width: widthExternalBoxes + 'px', - backgroundImage: `linear-gradient(-90deg, + A + </span> + <span className={styles.nodeSpan}>{data.attributes.length}</span> + </div> + <div + className={styles.relationNodeNodesBox} + onClick={() => onClickToggleNodeQualityPopup()} + style={{ + width: widthExternalBoxes + 'px', + backgroundImage: `linear-gradient(-90deg, ${theme.palette.custom.nodesBase} 0%, - ${theme.palette.custom.nodesBase} ${calculateRelationQuality( - data - )}%, - ${theme.palette.custom.elements.relationSecond[0] - } ${calculateRelationQuality(data)}%)`, + ${theme.palette.custom.nodesBase} ${calculateRelationQuality(data)}%, + ${theme.palette.custom.elements.relationSecond[0]} ${calculateRelationQuality(data)}%)`, + }} + > + <span + style={{ + paddingLeft: '25px', + float: 'left', }} > - <span - style={{ - paddingLeft: '25px', - float: 'left', - }} - > - N - </span> - <span className={styles.nodeSpan}>{data.nodeCount}</span> - </div> - - <div - className={styles.nodeWrapper} - style={{ backgroundColor: theme.palette.custom.nodesBase[0] }} - > - <span className={styles.nodeData}>{data.collection}</span> - </div> + N + </span> + <span className={styles.nodeSpan}>{data.nodeCount}</span> + </div> - <Handle - className={styles.arrowdown} - style={{ - pointerEvents: 'none', - borderTopColor: theme.palette.custom.elements.relationBase[0], - }} - position={Position.Bottom} - type="target" - ></Handle> + <div className={styles.nodeWrapper} style={{ backgroundColor: theme.palette.custom.nodesBase[0] }}> + <span className={styles.nodeData}>{data.collection}</span> </div> + + <Handle + className={styles.arrowdown} + style={{ + pointerEvents: 'none', + borderTopColor: theme.palette.custom.elements.relationBase[0], + }} + position={Position.Bottom} + type="target" + ></Handle> </div> - ); - } -); + </div> + ); +}); RelationNode.displayName = 'RelationNode'; diff --git a/libs/shared/lib/schema/schema-utils/flow-utils.test.tsx b/libs/shared/lib/schema/schema-utils/flow-utils.test.tsx index 63f62de8a..e2daac1a1 100644 --- a/libs/shared/lib/schema/schema-utils/flow-utils.test.tsx +++ b/libs/shared/lib/schema/schema-utils/flow-utils.test.tsx @@ -144,12 +144,8 @@ describe('schemaViewModelImpl', () => { it('capitalizeFirstLetter', () => { expect(capitalizeFirstLetter('')).toEqual(''); expect(capitalizeFirstLetter('hello')).toEqual('Hello'); - expect(capitalizeFirstLetter('1 start with a number')).toEqual( - '1 start with a number' - ); - expect(capitalizeFirstLetter('ALREADY CAPITALIZED')).toEqual( - 'ALREADY CAPITALIZED' - ); + expect(capitalizeFirstLetter('1 start with a number')).toEqual('1 start with a number'); + expect(capitalizeFirstLetter('ALREADY CAPITALIZED')).toEqual('ALREADY CAPITALIZED'); }); // FIXME: stop testing the canvas, test the functionality instead diff --git a/libs/shared/lib/schema/schema-utils/flow-utils.ts b/libs/shared/lib/schema/schema-utils/flow-utils.ts index 8ca5b8bca..5aca28f86 100644 --- a/libs/shared/lib/schema/schema-utils/flow-utils.ts +++ b/libs/shared/lib/schema/schema-utils/flow-utils.ts @@ -39,9 +39,7 @@ export const getCenter = ({ // we expect flows to be horizontal or vertical (all handles left or right respectively top or bottom) // a mixed edge is when one the source is on the left and the target is on the top for example. - const mixedEdge = - (sourceIsLeftOrRight && !targetIsLeftOrRight) || - (targetIsLeftOrRight && !sourceIsLeftOrRight); + const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight); if (mixedEdge) { const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0; @@ -71,12 +69,7 @@ export const getCenter = ({ * @param fontWeight The weight of the font. * @returns {number} Width of the textfield in px. */ -export const getWidthOfText = ( - text: string, - fontname: string, - fontsize: string, - fontWeight = 'normal' -): number => { +export const getWidthOfText = (text: string, fontname: string, fontsize: string, fontWeight = 'normal'): number => { let canvas = document.createElement('canvas'); let canvasText = canvas.getContext('2d') as CanvasRenderingContext2D; let fontSpecs = fontWeight + ' ' + fontsize + ' ' + fontname; @@ -90,12 +83,8 @@ export const getWidthOfText = ( * @param nodeCount Amount of nodes the entityNode has * @returns {number} the width of the largest box (attributesBox or nodesBox) of an entityNode in ch */ -export const calcWidthEntityNodeBox = ( - attributeCount: number, - nodeCount: number -): number => { - if (attributeCount >= nodeCount) - return (attributeCount?.toString()?.length || 0) + 5.5; +export const calcWidthEntityNodeBox = (attributeCount: number, nodeCount: number): number => { + if (attributeCount >= nodeCount) return (attributeCount?.toString()?.length || 0) + 5.5; else return (nodeCount?.toString()?.length || 0) + 5.5; }; @@ -105,12 +94,8 @@ export const calcWidthEntityNodeBox = ( * @param nodeCount Amount of nodes the entityNode has * @returns {number} the width of the largest box (attributesBox or nodesBox) of an relationNode in px */ -export const calcWidthRelationNodeBox = ( - attributeCount: number, - nodeCount: number -): number => { - if (attributeCount >= nodeCount) - return attributeCount.toString().length * 5 + 60; +export const calcWidthRelationNodeBox = (attributeCount: number, nodeCount: number): number => { + if (attributeCount >= nodeCount) return attributeCount.toString().length * 5 + 60; else return nodeCount.toString().length * 5 + 60; }; @@ -122,12 +107,7 @@ export const calcWidthRelationNodeBox = ( * @param height Height of the component. * @returns {BoundingBox} The bounding box of the component. */ -export const makeBoundingBox = ( - x: number, - y: number, - width: number, - height: number -): BoundingBox => { +export const makeBoundingBox = (x: number, y: number, width: number, height: number): BoundingBox => { let boundingBox: BoundingBox; boundingBox = { topLeft: { x: x, y: y }, @@ -142,21 +122,10 @@ export const makeBoundingBox = ( * @param boundingBoxTwo bounding box of the second component. * @returns {boolean} True if boxes overlap and false if there is no overlap. */ -export const doBoxesOverlap = ( - boundingBoxOne: BoundingBox, - boundingBoxTwo: BoundingBox -): boolean => { - if ( - boundingBoxOne.topLeft.x >= boundingBoxTwo.bottomRight.x || - boundingBoxTwo.topLeft.x >= boundingBoxOne.bottomRight.x - ) - return false; - - if ( - boundingBoxOne.topLeft.y >= boundingBoxTwo.bottomRight.y || - boundingBoxTwo.topLeft.y >= boundingBoxOne.bottomRight.y - ) - return false; +export const doBoxesOverlap = (boundingBoxOne: BoundingBox, boundingBoxTwo: BoundingBox): boolean => { + if (boundingBoxOne.topLeft.x >= boundingBoxTwo.bottomRight.x || boundingBoxTwo.topLeft.x >= boundingBoxOne.bottomRight.x) return false; + + if (boundingBoxOne.topLeft.y >= boundingBoxTwo.bottomRight.y || boundingBoxTwo.topLeft.y >= boundingBoxOne.bottomRight.y) return false; return true; }; diff --git a/libs/shared/lib/schema/schema-utils/schema-usecases.spec.ts b/libs/shared/lib/schema/schema-utils/schema-usecases.spec.ts index 72c9a59cf..15a3cbb72 100644 --- a/libs/shared/lib/schema/schema-utils/schema-usecases.spec.ts +++ b/libs/shared/lib/schema/schema-utils/schema-usecases.spec.ts @@ -1,47 +1,31 @@ -import { assert, describe, expect, it, test } from "vitest"; -import { SchemaFromBackend } from '@graphpolaris/shared/lib/model/backend'; +import { describe, expect, it, test } from 'vitest'; import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils'; -import { MultiGraph } from 'graphology'; -import { Attributes } from 'graphology-types'; -import { - movieSchemaRaw, - northwindSchemaRaw, - simpleSchemaRaw, - twitterSchemaRaw, -} from '@graphpolaris/shared/lib/mock-data'; -import { SchemaGraphology } from "../model"; +import { movieSchemaRaw, northwindSchemaRaw, simpleSchemaRaw, twitterSchemaRaw } from '@graphpolaris/shared/lib/mock-data'; +import { SchemaFromBackend, SchemaGraphology } from '../model'; describe('SchemaUsecases', () => { - test.each([ - { data: simpleSchemaRaw }, - { data: movieSchemaRaw }, - { data: northwindSchemaRaw }, - { data: twitterSchemaRaw }, - ])('parseSchemaFromBackend parsing should work', ({ data }) => { - // console.log('testinput', input); + test.each([{ data: simpleSchemaRaw }, { data: movieSchemaRaw }, { data: northwindSchemaRaw }, { data: twitterSchemaRaw }])( + 'parseSchemaFromBackend parsing should work', + ({ data }) => { + // console.log('testinput', input); - const parsed = SchemaUtils.schemaBackend2Graphology( - data as SchemaFromBackend - ); - expect(parsed).toBeDefined(); - }); + const parsed = SchemaUtils.schemaBackend2Graphology(data as SchemaFromBackend); + expect(parsed).toBeDefined(); + } + ); it('should export and reimport', () => { - const parsed = SchemaUtils.schemaBackend2Graphology( - simpleSchemaRaw as SchemaFromBackend - ); + const parsed = SchemaUtils.schemaBackend2Graphology(simpleSchemaRaw as SchemaFromBackend); const reload = SchemaGraphology.from(parsed.export()); expect(parsed).toEqual(reload); }); - test.each([ - { data: simpleSchemaRaw }, - { data: movieSchemaRaw }, - { data: northwindSchemaRaw }, - { data: twitterSchemaRaw }, - ])('should load my test json $data', ({ data }) => { - expect(data).toBeDefined(); - expect(data.nodes).toBeDefined(); - }); + test.each([{ data: simpleSchemaRaw }, { data: movieSchemaRaw }, { data: northwindSchemaRaw }, { data: twitterSchemaRaw }])( + 'should load my test json $data', + ({ data }) => { + expect(data).toBeDefined(); + expect(data.nodes).toBeDefined(); + } + ); }); diff --git a/libs/shared/lib/schema/schema-utils/schema-usecases.ts b/libs/shared/lib/schema/schema-utils/schema-usecases.ts index 09b7ed3a7..780870b8b 100644 --- a/libs/shared/lib/schema/schema-utils/schema-usecases.ts +++ b/libs/shared/lib/schema/schema-utils/schema-usecases.ts @@ -1,8 +1,9 @@ -import { SchemaGraphNodeWithFunctions, SchemaGraphRelation, SchemaGraphRelationWithFunctions } from '../model/reactflow'; +import { SchemaReactflowNodeWithFunctions, SchemaReactflowRelation, SchemaReactflowRelationWithFunctions } from '../model/reactflow'; import Graph from 'graphology'; import { Attributes } from 'graphology-types'; import { MarkerType, Edge, Node } from 'reactflow'; -import { SchemaGraphNode } from '../model/reactflow'; +import { SchemaReactflowNode } from '../model/reactflow'; +import { QueryElementTypes } from '../../querybuilder'; //TODO does not belong here; maybe should go into the GraphPolarisThemeProvider const ANIMATEDEDGES = false; @@ -12,7 +13,7 @@ export function schemaExpandRelation(graph: Graph): Graph { newGraph.forEachNode((node, attributes) => { // console.log(node, attributes); - newGraph.mergeNodeAttributes(node, { type: 'entity' }); + newGraph.mergeNodeAttributes(node, { type: QueryElementTypes.Entity }); }); //makeNewRelationNodes @@ -30,7 +31,7 @@ export function schemaExpandRelation(graph: Graph): Graph { ...attributes, x: 0, y: 0, - type: 'relation', + type: QueryElementTypes.Relation, }); const id = 'RelationEdge' + source + '->' + newID; @@ -50,7 +51,7 @@ export function schemaExpandRelation(graph: Graph): Graph { // Takes the schema as an input and creates basic react flow elements for them. export function schemaGraphology2Reactflow(graph: Graph): { - nodes: Array<Node<SchemaGraphNodeWithFunctions | SchemaGraphRelationWithFunctions>>; + nodes: Array<Node<SchemaReactflowNodeWithFunctions | SchemaReactflowRelationWithFunctions>>; edges: Array<Edge>; } { const initialElements: { nodes: Array<Node>; edges: Array<Edge> } = { @@ -73,7 +74,7 @@ export function createReactFlowNodes(graph: Graph): Array<Node> { const newNode: Node = { id: node, data: { - ...attributes + ...attributes, }, position: { x: attributes.x, y: attributes.y }, type: attributes.type, @@ -94,7 +95,7 @@ export function createReactFlowEdges(graph: Graph): Array<Edge> { source: source, target: target, data: { - ...attributes + ...attributes, }, // label: edge, type: attributes?.type || 'smoothstep', @@ -117,7 +118,7 @@ export function createReactFlowEdges(graph: Graph): Array<Edge> { // name: edge, // }, // position: { x: attributes.x, y: attributes.y }, -// type: 'relation', +// type: QueryElementTypes.Relation, // }; // nodeElements.push(newRelationNode); // }); diff --git a/libs/shared/lib/schema/schema-utils/schema-utils.spec.ts b/libs/shared/lib/schema/schema-utils/schema-utils.spec.ts index 09202f4c0..b1559b33a 100644 --- a/libs/shared/lib/schema/schema-utils/schema-utils.spec.ts +++ b/libs/shared/lib/schema/schema-utils/schema-utils.spec.ts @@ -1,5 +1,5 @@ import { SchemaUtils } from './schema-utils'; -import { assert, describe, expect, it } from "vitest"; +import { assert, describe, expect, it } from 'vitest'; describe('Schema Utils', () => { it('should expose a class SchemaUtils', () => { diff --git a/libs/shared/lib/schema/schema-utils/schema-utils.ts b/libs/shared/lib/schema/schema-utils/schema-utils.ts index e0cd75daf..4d5c864b8 100644 --- a/libs/shared/lib/schema/schema-utils/schema-utils.ts +++ b/libs/shared/lib/schema/schema-utils/schema-utils.ts @@ -1,16 +1,13 @@ import { SchemaAttribute, SchemaFromBackend, SchemaGraphology, SchemaGraphologyNode } from '../model'; export class SchemaUtils { - public static schemaBackend2Graphology( - schemaFromBackend: SchemaFromBackend - ): SchemaGraphology { + public static schemaBackend2Graphology(schemaFromBackend: SchemaFromBackend): SchemaGraphology { const { nodes, edges } = schemaFromBackend; // Instantiate a directed graph that allows self loops and parallel edges const schemaGraphology = new SchemaGraphology({ allowSelfLoops: true }); // console.log('parsing schema'); // The graph schema needs a node for each node AND edge. These need then be connected - nodes.forEach((node) => { const attributes: SchemaGraphologyNode = { ...node, @@ -18,19 +15,14 @@ export class SchemaUtils { nodeCount: 0, summedNullAmount: 0, connectedRatio: 1, - handles: ['entitySourceLeft', 'entityTargetLeft'], - attributes: [ - ...node.attributes, - ], + attributes: [...node.attributes], x: 0, y: 0, - } + }; schemaGraphology.addNode(node.name, attributes); // console.log(node, schemaGraph.nodes()); }); - - // The name of the edge will be name + from + to, since edge names are not unique edges.forEach((edge) => { const edgeID = [edge.name, '_', edge.from, edge.to].join(''); //ensure that all interpreted as string @@ -41,7 +33,6 @@ export class SchemaUtils { summedNullAmount: 0, fromRatio: 0, toRatio: 0, - handles: [], // width: 10, // height: 20, ...edge, @@ -51,4 +42,3 @@ export class SchemaUtils { return schemaGraphology; } } - diff --git a/libs/shared/lib/vis/geovis/GeoNodeLinkMap.stories.tsx b/libs/shared/lib/vis/geovis/GeoNodeLinkMap.stories.tsx new file mode 100644 index 000000000..501880b4b --- /dev/null +++ b/libs/shared/lib/vis/geovis/GeoNodeLinkMap.stories.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import GeoNodeLinkMap from './GeoNodeLinkMap'; +import { Provider } from 'react-redux'; +import { configureStore } from '@reduxjs/toolkit'; +import { GraphPolarisThemeProvider } from '../../data-access/theme'; +import { colorPaletteConfigSlice, graphQueryResultSlice, assignNewGraphQueryResult } from '../../data-access/store'; +import { flights } from '../../mock-data/query-result/geo-mock-data'; +import { Meta } from '@storybook/react'; + +const Mockstore = configureStore({ + reducer: { + colorPaletteConfig: colorPaletteConfigSlice.reducer, + graphQueryResult: graphQueryResultSlice.reducer, + }, +}); + +const Component: Meta<typeof GeoNodeLinkMap> = { + title: 'Components/Visualizations/GeospatialKG', + component: GeoNodeLinkMap, + decorators: [ + (story) => ( + <Provider store={Mockstore}> + <GraphPolarisThemeProvider> + <div + style={{ + width: '100%', + height: '100vh', + }} + > + {story()} + </div> + </GraphPolarisThemeProvider> + </Provider> + ), + ], +}; + +export const Flights = { + args: { + isAggregated: true, + isFanned: true, + }, + play: async () => { + const dispatch = Mockstore.dispatch; + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: flights })); + }, +}; + +export const FlightsAggregated = { + args: { + nodelink: 'faceting', + isAggregated: true, + isFanned: true, + }, + play: async () => { + const dispatch = Mockstore.dispatch; + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: flights })); + }, +}; + +export const FlightsClustered = { + args: { + nodelink: 'cluster', + isAggregated: true, + isFanned: true, + }, + play: async () => { + const dispatch = Mockstore.dispatch; + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: flights })); + }, +}; + +export default Component; diff --git a/libs/shared/lib/vis/geovis/GeoNodeLinkMap.tsx b/libs/shared/lib/vis/geovis/GeoNodeLinkMap.tsx new file mode 100644 index 000000000..127b5fc3c --- /dev/null +++ b/libs/shared/lib/vis/geovis/GeoNodeLinkMap.tsx @@ -0,0 +1,117 @@ +/** + * This program has been developed by a student from the bachelor Information Science at + * Utrecht University within the Research Project course. + * © Copyright Utrecht University (Department of Information and Computing Sciences) + */ + +import React, { useState, useEffect, useMemo } from 'react'; +import { NodeLinkViewModel } from './GeoNodeLinkViewModel'; +import { useGraphQueryResult } from '../../data-access/store'; +import { MapView, DeckGL } from 'deck.gl/typed'; +import { getTooltip } from './Tooltip'; +import { Node } from './types'; +import * as d3 from 'd3'; +import { generateBaseLayer } from './layers/BaseLayer'; +import { FacetingLayer } from './layers/FacetingLayer'; +import { ClusterLayer } from './layers/ClusterLayer'; +import { NodeLinkLayer } from './layers/NodeLinkLayer'; +import { useTheme } from '@mui/material'; + +type Props = { + mapType: number; + nodelink: 'cluster' | 'faceting' | 'nodelink'; + isAggregated: boolean; + isFanned: boolean; + brushing: boolean; +}; + +export const GeoNodeLinkMap = React.memo(({ mapType = 0, isAggregated, isFanned, nodelink = 'nodelink', brushing = false }: Props) => { + const theme = useTheme(); + const [graph, setGraph] = useState<NodeLinkViewModel | null>(null); + const [isHovering, setIsHovering] = useState<boolean>(); + const [isLoading, setIsLoading] = useState<boolean>(true); + const [viewport, setViewport] = useState<{ [key: string]: number }>({ + longitude: -102.4, + latitude: 37.8, + zoom: 3, + }); + + const graphQueryResult = useGraphQueryResult(); + + useEffect(() => { + if (graphQueryResult) { + const graphModel = new NodeLinkViewModel({ + nodes: graphQueryResult.nodes as unknown as Node[], //TODO! must check if there is coordinates before accepting this cast + edges: graphQueryResult.edges, + }); + setGraph(graphModel); + setIsLoading(false); + } + }, [graphQueryResult]); + + const onViewStateChange = (viewState: { [key: string]: number }) => { + setViewport(viewState); + }; + + const edges = useMemo(() => graph?.getEdges(), [graph?.data]); + const nodes = useMemo(() => graph?.getNodes(), [graph?.data]); + + const colorScale: d3.ScaleLinear<string, string> = d3 + .scaleLinear<string>() + // .domain(d3.extent(edges, (d) => (d!.attributes!.prediction))) + .domain([0, 10000]) // TODO: this is not ideal and makes the color mean nothing + .range(['#ef8a62', '#1D6600', '#85327B']); // use theme? + + const layers: any = [generateBaseLayer(mapType)]; + + if (nodelink === 'cluster') { + layers.push( + new ClusterLayer({ + graph: graph, + edges: edges, + nodes: nodes, + colorScale: colorScale, + brushing: brushing, + }) + ); + } else if (nodelink === 'faceting') { + layers.push( + new FacetingLayer({ + graph: graph, + edges: edges, + nodes: nodes, + colorScale: colorScale, + isAggregated: isAggregated, + isFanned: isFanned, + brushing: brushing, + }) + ); + } else { + layers.push( + new NodeLinkLayer({ + edges: edges, + nodes: nodes, + colorScale: colorScale, + brushing: brushing, + }) + ); + } + + return ( + <div> + {!isLoading && ( + <DeckGL + layers={layers} + views={new MapView({ repeat: true, controller: true })} + initialViewState={viewport} + getTooltip={getTooltip} + onViewStateChange={({ viewState }) => onViewStateChange(viewState)} + onHover={({ object }) => setIsHovering(Boolean(object))} + getCursor={({ isDragging }) => (isDragging ? 'grabbing' : isHovering ? 'pointer' : 'grab')} + /> + )} + </div> + ); +}); + +export default GeoNodeLinkMap; diff --git a/libs/shared/lib/vis/geovis/NodeLinkViewModel.tsx b/libs/shared/lib/vis/geovis/GeoNodeLinkViewModel.tsx similarity index 83% rename from libs/shared/lib/vis/geovis/NodeLinkViewModel.tsx rename to libs/shared/lib/vis/geovis/GeoNodeLinkViewModel.tsx index 054c1ce05..f729d3bc2 100644 --- a/libs/shared/lib/vis/geovis/NodeLinkViewModel.tsx +++ b/libs/shared/lib/vis/geovis/GeoNodeLinkViewModel.tsx @@ -10,7 +10,7 @@ export class NodeLinkViewModel implements ViewModel { data: Graph; private original_data: Graph; - constructor(data) { + constructor(data: Graph) { this.data = data; this.addCoordinatesToEdges(); this.original_data = this.data; @@ -22,18 +22,15 @@ export class NodeLinkViewModel implements ViewModel { addCoordinatesToEdges() { // Add coordinates to edges based on nodes - const edges = {}; + const edges: Record<string, any> = {}; this.data.edges.forEach((edge: Edge) => { const key = `${edge.from}_${edge.to}`; if (!edges[key]) { edges[key] = { ...edge, - from_coordinates: this.data.nodes.find( - (node) => node.id === edge.from - )?.attributes.coordinates, - to_coordinates: this.data.nodes.find((node) => node.id === edge.to) - ?.attributes.coordinates, + from_coordinates: this.data.nodes.find((node) => node.id === edge.from)?.attributes.coordinates, + to_coordinates: this.data.nodes.find((node) => node.id === edge.to)?.attributes.coordinates, count: 1, }; } else { diff --git a/libs/shared/lib/vis/geovis/NodeLinkMap.stories.tsx b/libs/shared/lib/vis/geovis/NodeLinkMap.stories.tsx deleted file mode 100644 index b9ff97b74..000000000 --- a/libs/shared/lib/vis/geovis/NodeLinkMap.stories.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import NodeLinkMap from './NodeLinkMap'; -import { Provider } from 'react-redux'; -import { configureStore } from '@reduxjs/toolkit'; -import { GraphPolarisThemeProvider } from '../../data-access/theme'; -import { - colorPaletteConfigSlice, - graphQueryResultSlice, - assignNewGraphQueryResult, -} from '../../data-access/store'; -import { flights } from './mock-data'; - -const Mockstore = configureStore({ - reducer: { - colorPaletteConfig: colorPaletteConfigSlice.reducer, - graphQueryResult: graphQueryResultSlice.reducer, - }, -}); - -export default { - title: 'Components/Visualizations/GeospatialKG', - component: NodeLinkMap, - decorators: [ - (story) => ( - <Provider store={Mockstore}> - <GraphPolarisThemeProvider> - <div - style={{ - width: '100%', - height: '100vh', - }} - > - {story()} - </div> - </GraphPolarisThemeProvider> - </Provider> - ), - ], -}; - -const Template = (args) => <NodeLinkMap {...args} />; - -export const Flights = Template.bind({}); -Flights.args = { - isAggregated: true, - isFanned: true, -}; -Flights.play = async () => { - const dispatch = Mockstore.dispatch; - dispatch(assignNewGraphQueryResult(flights)); -}; - -export const FlightsAggregated = Template.bind({}); -FlightsAggregated.args = { - nodelink: 'faceting', - isAggregated: true, - isFanned: true, -}; -FlightsAggregated.play = async () => { - const dispatch = Mockstore.dispatch; - dispatch(assignNewGraphQueryResult(flights)); -}; - -export const FlightsClustered = Template.bind({}); -FlightsClustered.args = { - nodelink: 'cluster', - isAggregated: false, - isFanned: true, -}; -FlightsClustered.play = async () => { - const dispatch = Mockstore.dispatch; - dispatch(assignNewGraphQueryResult(flights)); -}; diff --git a/libs/shared/lib/vis/geovis/NodeLinkMap.tsx b/libs/shared/lib/vis/geovis/NodeLinkMap.tsx deleted file mode 100644 index 9094957dd..000000000 --- a/libs/shared/lib/vis/geovis/NodeLinkMap.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import React, { useState, useEffect, useMemo } from 'react'; -import { NodeLinkViewModel } from './NodeLinkViewModel'; -import { useGraphQueryResult } from '../../data-access/store'; -import { MapView, DeckGL } from 'deck.gl/typed'; -import { getTooltip } from './Tooltip'; -import { Node } from './types'; -import * as d3 from 'd3'; -import { generateBaseLayer } from './layers/BaseLayer'; -import { FacetingLayer } from './layers/FacetingLayer'; -import { ClusterLayer } from './layers/ClusterLayer'; -import { NodeLinkLayer } from './layers/NodeLinkLayer'; - -type Props = { - mapType: number; - nodelink: string; - isAggregated: boolean; - isFanned: boolean; - brushing: boolean; -}; - -export const NodeLinkMap = React.memo( - ({ - mapType = 0, - nodelink, - isAggregated, - isFanned, - brushing = false, - }: Props) => { - const [graph, setGraph] = useState<NodeLinkViewModel | null>(null); - const [isLoading, setIsLoading] = useState<boolean>(true); - const [viewport, setViewport] = useState<{ [key: string]: number }>({ - longitude: -102.4, - latitude: 37.8, - zoom: 3, - }); - - const graphQueryResult = useGraphQueryResult(); - - useEffect(() => { - if (graphQueryResult) { - const graphModel = new NodeLinkViewModel({ - nodes: graphQueryResult.nodes, - edges: graphQueryResult.edges, - }); - setGraph(graphModel); - setIsLoading(false); - } - }, [graphQueryResult]); - - const onViewStateChange = (viewState) => { - setViewport(viewState); - }; - - const edges = useMemo(() => graph?.getEdges(), [graph?.data]); - const nodes = useMemo(() => graph?.getNodes(), [graph?.data]); - - const colorScale: d3.ScaleLinear<string, string> = d3 - .scaleLinear<string>() - .domain([0, 6000]) - .range(['blue', 'red']); - - const layers = [generateBaseLayer(mapType)]; - - if (nodelink === 'cluster') { - layers.push( - new ClusterLayer({ - graph: graph, - edges: edges, - nodes: nodes, - colorScale: colorScale, - brushing: brushing, - }) - ); - } else if (nodelink === 'faceting') { - layers.push( - new FacetingLayer({ - graph: graph, - edges: edges, - nodes: nodes, - colorScale: colorScale, - isAggregated: isAggregated, - isFanned: isFanned, - brushing: brushing, - }) - ); - } else { - layers.push( - new NodeLinkLayer({ - edges: edges, - nodes: nodes, - colorScale: colorScale, - brushing: brushing, - }) - ); - } - - return ( - <div> - {!isLoading && ( - <DeckGL - layers={layers} - views={new MapView({ repeat: true, controller: true })} - initialViewState={viewport} - getTooltip={getTooltip} - onViewStateChange={({ viewState }) => onViewStateChange(viewState)} - /> - )} - </div> - ); - } -); - -export default NodeLinkMap; diff --git a/libs/shared/lib/vis/geovis/Tooltip.tsx b/libs/shared/lib/vis/geovis/Tooltip.tsx index 8301dfed9..af10c5f92 100644 --- a/libs/shared/lib/vis/geovis/Tooltip.tsx +++ b/libs/shared/lib/vis/geovis/Tooltip.tsx @@ -4,10 +4,22 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -export const getTooltip = ({ object }) => { +import { PickingInfo } from 'deck.gl/typed'; + +export const getTooltip = ({ object }: PickingInfo): any => { + const ret = { + style: { + backgroundColor: 'white', + color: 'black', + fontSize: '15px', + fontFamily: 'Calibri, monospace', + border: '1px solid gray', + }, + }; if (object && object.hasOwnProperty('type')) { const { id, geometry, properties } = object; return { + ...ret, html: ` <div> <h3>Cluster id: ${id}</h3> @@ -19,6 +31,7 @@ export const getTooltip = ({ object }) => { } else if (object && object.hasOwnProperty('geometry')) { const { geometry, properties } = object; return { + ...ret, html: ` <div> <h3>Airport: ${properties.id}</h3> @@ -26,13 +39,10 @@ export const getTooltip = ({ object }) => { </div> `, }; - } else if ( - object && - object.hasOwnProperty('attributes') && - object.attributes.hasOwnProperty('locations') - ) { + } else if (object && object.hasOwnProperty('attributes') && object.attributes.hasOwnProperty('locations')) { const { id, attributes } = object; return { + ...ret, html: ` <div> <h3>${id}</h3> @@ -42,13 +52,10 @@ export const getTooltip = ({ object }) => { </div> `, }; - } else if ( - object && - object.hasOwnProperty('id') && - object.attributes.hasOwnProperty('prediction') - ) { + } else if (object && object.hasOwnProperty('id') && object.attributes.hasOwnProperty('prediction')) { const { from, to, count, attributes } = object; return { + ...ret, html: ` <div> <h3>${from}-${to}</h3> @@ -61,6 +68,7 @@ export const getTooltip = ({ object }) => { } else if (object && object.attributes.hasOwnProperty('airport')) { const { id, attributes } = object; return { + ...ret, html: ` <div> <h3>Airport: ${id}</h3> diff --git a/libs/shared/lib/vis/geovis/aggregateCommand.tsx b/libs/shared/lib/vis/geovis/aggregateCommand.tsx index f7497bac6..c6ed214b1 100644 --- a/libs/shared/lib/vis/geovis/aggregateCommand.tsx +++ b/libs/shared/lib/vis/geovis/aggregateCommand.tsx @@ -29,10 +29,7 @@ export class AggregateCommand implements Command { } const aggregateNodes = (graph: Graph, dimension: string): Node[] => { - const nodesByAttribute: Map<string, Node[]> = d3.group( - graph.nodes, - (node: Node) => eval(`node.${dimension}`) - ); + const nodesByAttribute: Map<string, Node[]> = d3.group(graph.nodes, (node: Node) => eval(`node.${dimension}`)); const bundleByAttribute: Node[] = []; @@ -64,35 +61,24 @@ const aggregateNodes = (graph: Graph, dimension: string): Node[] => { }; const aggregateEdges = (data: Graph): Edge[] => { - const locations: { [key: string]: string[] } = data.nodes.reduce( - (locations, node) => { - locations[node.id] = node.attributes.locations; - return locations; - }, - {} - ); + const locations: { [key: string]: string[] } = data.nodes.reduce((locations: any, node) => { + locations[node.id] = node.attributes.locations; + return locations; + }, {}); const bundles: Edge[] = []; data.edges.forEach((edge: Edge) => { - const from_location: string | undefined = Object.keys(locations).find( - (state) => locations[state].includes(edge.from) - ); + const from_location: string | undefined = Object.keys(locations).find((state) => locations[state].includes(edge.from)); - const to_location: string | undefined = Object.keys(locations).find( - (state) => locations[state].includes(edge.to) - ); + const to_location: string | undefined = Object.keys(locations).find((state) => locations[state].includes(edge.to)); if (from_location && to_location) { if (from_location === to_location) return; // edge is within group - const from_coordinates: Coordinates | undefined = data.nodes.find( - (node: Node) => node.id === from_location - )?.attributes.coordinates; + const from_coordinates: Coordinates | undefined = data.nodes.find((node: Node) => node.id === from_location)?.attributes.coordinates; - const to_coordinates: Coordinates | undefined = data.nodes.find( - (node: Node) => node.id === to_location - )?.attributes.coordinates; + const to_coordinates: Coordinates | undefined = data.nodes.find((node: Node) => node.id === to_location)?.attributes.coordinates; const newEdge: Edge = { ...edge, diff --git a/libs/shared/lib/vis/geovis/layers/BaseLayer.tsx b/libs/shared/lib/vis/geovis/layers/BaseLayer.tsx index c00f461eb..7a09a53b6 100644 --- a/libs/shared/lib/vis/geovis/layers/BaseLayer.tsx +++ b/libs/shared/lib/vis/geovis/layers/BaseLayer.tsx @@ -4,7 +4,7 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { TileLayer, BitmapLayer } from 'deck.gl'; +import { TileLayer, BitmapLayer } from 'deck.gl/typed'; import { mapTypes } from '../config'; export const generateBaseLayer = (mapType: number) => { @@ -22,13 +22,13 @@ export const generateBaseLayer = (mapType: number) => { maxZoom: 19, tileSize: 256, - renderSubLayers: (props) => { + renderSubLayers: (props: any) => { const { bbox: { west, south, east, north }, } = props.tile; return new BitmapLayer(props, { - data: null, + data: undefined, image: props.data, bounds: [west, south, east, north], }); diff --git a/libs/shared/lib/vis/geovis/layers/ClusterLayer.tsx b/libs/shared/lib/vis/geovis/layers/ClusterLayer.tsx index 362d63f14..4f59d9cbb 100644 --- a/libs/shared/lib/vis/geovis/layers/ClusterLayer.tsx +++ b/libs/shared/lib/vis/geovis/layers/ClusterLayer.tsx @@ -11,6 +11,7 @@ import { LayerProps } from '../types'; import { calcNodeDistance, getColor } from '../utils'; import { BrushingExtension } from '@deck.gl/extensions/typed'; import { EdgeCrossings } from '../readability'; +import Color from 'color'; export class ClusterLayer extends CompositeLayer<LayerProps> { /** @@ -20,11 +21,11 @@ export class ClusterLayer extends CompositeLayer<LayerProps> { */ static componentName = 'Cluster Layer'; - shouldUpdateState({ changeFlags }) { + shouldUpdateState({ changeFlags }: any) { return changeFlags.somethingChanged; } - updateState({ props, oldProps, changeFlags }) { + updateState({ props, oldProps, changeFlags }: any) { const rebuildIndex = changeFlags.dataChanged; const zoom = Math.floor(this.context.viewport.zoom); @@ -39,7 +40,7 @@ export class ClusterLayer extends CompositeLayer<LayerProps> { }, }); index.load( - props.graph.data.nodes.map((node) => ({ + props.graph.data.nodes.map((node: any) => ({ geometry: { coordinates: node.attributes.coordinates }, properties: node, })) @@ -51,7 +52,7 @@ export class ClusterLayer extends CompositeLayer<LayerProps> { const nodes = this.state.index.getClusters([-180, -85, 180, 85], zoom); // Create node index for faster coordinate search - const node_index = {}; + const node_index: Record<string, any> = {}; for (const node of nodes) { const coordinates = node.geometry.coordinates; if (node.properties.hasOwnProperty('attributes')) { @@ -60,7 +61,7 @@ export class ClusterLayer extends CompositeLayer<LayerProps> { coordinates: coordinates, }; } else { - const ids = node.properties.ids; + const ids = node.properties.ids as string[]; for (const id of ids) { if (!node_index[id]) { node_index[id] = { @@ -73,16 +74,13 @@ export class ClusterLayer extends CompositeLayer<LayerProps> { } // Add updated coordinates to edges - const edges = {}; - props.graph.data.edges.forEach((edge) => { + const edges: Record<string, any> = {}; + props.graph.data.edges.forEach((edge: any) => { const source = node_index[edge.from]; const target = node_index[edge.to]; if (source && target) { - if ( - source.coordinates[0] === target.coordinates[0] && - source.coordinates[1] === target.coordinates[1] - ) { + if (source.coordinates[0] === target.coordinates[0] && source.coordinates[1] === target.coordinates[1]) { return; } else { const key = `${source.id}_${target.id}`; @@ -122,26 +120,25 @@ export class ClusterLayer extends CompositeLayer<LayerProps> { brushingEnabled: this.props.brushing, brushingRadius: 500000, brushingTarget: 'source_target', - getWidth: (d) => Math.sqrt(d.count) * 0.5, + getWidth: (d) => Math.sqrt(d.count) * 0.7, getSourcePosition: (d) => d.from_coordinates, getTargetPosition: (d) => d.to_coordinates, - getColor: (d) => - getColor(d.attributes.prediction, this.props.colorScale), + opacity: 0.3, + getColor: (d) => getColor(d.attributes.prediction, this.props.colorScale), }), new ScatterplotLayer({ id: 'nodes', data: this.state.nodes, - opacity: 1, pickable: true, + opacity: 0.5, interactive: true, filled: true, - radiusScale: 3, - radiusMinPixels: 5, + radiusScale: 4, + radiusMinPixels: 6, radiusMaxPixels: 20, - getRadius: (d) => - d.hasOwnProperty('type') ? d.properties.ids.length * 1000 : 10, - getFillColor: (d) => (d.properties.cluster ? [250, 0, 0] : [0, 0, 255]), getPosition: (d) => d.geometry.coordinates, + getFillColor: (d) => (d.properties.cluster ? (new Color('#7D1128').rgb() as any).color : (new Color('#000000').rgb() as any).color), + getRadius: (d) => (d.hasOwnProperty('type') ? d.properties.ids.length * 1000 : 10), }), ]; } diff --git a/libs/shared/lib/vis/geovis/layers/FacetingLayer.tsx b/libs/shared/lib/vis/geovis/layers/FacetingLayer.tsx index a9ca68463..1c4a85797 100644 --- a/libs/shared/lib/vis/geovis/layers/FacetingLayer.tsx +++ b/libs/shared/lib/vis/geovis/layers/FacetingLayer.tsx @@ -19,26 +19,20 @@ export class FacetingLayer extends CompositeLayer<LayerProps> { */ static componentName = 'FacetingLayer Layer'; - shouldUpdateState({ changeFlags }) { + shouldUpdateState({ changeFlags }: any) { return changeFlags.somethingChanged; } - updateState({ props, oldProps, changeFlags }) { + updateState({ props, oldProps, changeFlags }: any) { const rebuildIndex = changeFlags.dataChanged; const zoom = Math.floor(this.context.viewport.zoom); if (rebuildIndex || zoom !== this.state.zoom) { // Aggregate based on predefined levels - if (zoom > 5) - props.graph.executeCommand(new AggregateCommand('attributes.city')); - if (zoom <= 5 && zoom > 3) - props.graph.executeCommand(new AggregateCommand('attributes.region')); - if (zoom < 3 && zoom > 2) - props.graph.executeCommand(new AggregateCommand('attributes.country')); - if (zoom < 2) - props.graph.executeCommand( - new AggregateCommand('attributes.continent') - ); + if (zoom > 5) props.graph.executeCommand(new AggregateCommand('attributes.city')); + if (zoom <= 5 && zoom > 3) props.graph.executeCommand(new AggregateCommand('attributes.region')); + if (zoom < 3 && zoom > 2) props.graph.executeCommand(new AggregateCommand('attributes.country')); + if (zoom < 2) props.graph.executeCommand(new AggregateCommand('attributes.continent')); this.setState({ nodes: props.graph.data.nodes, @@ -61,8 +55,7 @@ export class FacetingLayer extends CompositeLayer<LayerProps> { getWidth: (d) => d.count, getSourcePosition: (d) => d.from_coordinates, getTargetPosition: (d) => d.to_coordinates, - getColor: (d) => - getColor(d.attributes.prediction, this.props.colorScale), + getColor: (d) => getColor(d.attributes.prediction, this.props.colorScale), }), new ScatterplotLayer({ id: 'nodes', @@ -71,11 +64,11 @@ export class FacetingLayer extends CompositeLayer<LayerProps> { opacity: 0.6, interactive: true, filled: true, - radiusScale: 3, - radiusMinPixels: 3, + radiusScale: 1, + radiusMinPixels: 4, radiusMaxPixels: 20, getPosition: (d) => d.attributes.coordinates, - getFillColor: [250, 0, 0], + getFillColor: [0, 0, 0], }), ]; } diff --git a/libs/shared/lib/vis/geovis/layers/NodeLinkLayer.tsx b/libs/shared/lib/vis/geovis/layers/NodeLinkLayer.tsx index ef57df92f..183f72df0 100644 --- a/libs/shared/lib/vis/geovis/layers/NodeLinkLayer.tsx +++ b/libs/shared/lib/vis/geovis/layers/NodeLinkLayer.tsx @@ -16,39 +16,38 @@ export class NodeLinkLayer extends CompositeLayer<LayerProps> { */ static componentName = 'Knowledge Graph Layer'; - shouldUpdateState({ changeFlags }) { + shouldUpdateState({ changeFlags }: any) { return changeFlags.somethingChanged; } renderLayers() { - const { edges, nodes, colorScale, brushing } = this.props; - return [ new LineLayer({ id: 'edges', - data: edges, + data: this.props.edges, pickable: true, extensions: [new BrushingExtension()], - brushingEnabled: brushing, + brushingEnabled: this.props.brushing, brushingRadius: 500000, brushingTarget: 'source_target', getWidth: (d) => d.count, getSourcePosition: (d) => d.from_coordinates, getTargetPosition: (d) => d.to_coordinates, - getColor: (d) => getColor(d.attributes.prediction, colorScale), + opacity: 0.3, + getColor: (d) => getColor(d.attributes.prediction, this.props.colorScale), }), new ScatterplotLayer({ id: 'nodes', - data: nodes, + data: this.props.nodes, pickable: true, - opacity: 0.6, + opacity: 0.5, interactive: true, filled: true, - radiusScale: 3, - radiusMinPixels: 3, + radiusScale: 1, + radiusMinPixels: 4, radiusMaxPixels: 20, getPosition: (d) => d.attributes.coordinates, - getFillColor: [250, 0, 0], + getFillColor: [0, 0, 0], }), ]; } diff --git a/libs/shared/lib/vis/geovis/readability.tsx b/libs/shared/lib/vis/geovis/readability.tsx index de267f6e5..6a152eeb7 100644 --- a/libs/shared/lib/vis/geovis/readability.tsx +++ b/libs/shared/lib/vis/geovis/readability.tsx @@ -39,7 +39,7 @@ export const EdgeCrossings = (edges: Edge[]) => { return n_crossings; }; -const doEdgesCross = (edge_one, edge_two) => { +const doEdgesCross = (edge_one: any, edge_two: any) => { const from1 = edge_one.from_coordinates; const to1 = edge_one.to_coordinates; const from2 = edge_two.from_coordinates; @@ -50,22 +50,19 @@ const doEdgesCross = (edge_one, edge_two) => { // Inspiration: https://github.com/rpgove/greadability/blob/master/greadability.js -const direction = (pi, pj, pk) => { +const direction = (pi: any, pj: any, pk: any) => { var p1 = [pk[0] - pi[0], pk[1] - pi[1]]; var p2 = [pj[0] - pi[0], pj[1] - pi[1]]; return p1[0] * p2[1] - p2[0] * p1[1]; }; -const onSegment = (pi, pj, pk) => { +const onSegment = (pi: any, pj: any, pk: any) => { return ( - Math.min(pi[0], pj[0]) <= pk[0] && - pk[0] <= Math.max(pi[0], pj[0]) && - Math.min(pi[1], pj[1]) <= pk[1] && - pk[1] <= Math.max(pi[1], pj[1]) + Math.min(pi[0], pj[0]) <= pk[0] && pk[0] <= Math.max(pi[0], pj[0]) && Math.min(pi[1], pj[1]) <= pk[1] && pk[1] <= Math.max(pi[1], pj[1]) ); }; -const linesCross = (line1, line2) => { +const linesCross = (line1: any, line2: any) => { var d1, d2, d3, d4; d1 = direction(line2[0], line2[1], line1[0]); @@ -73,10 +70,7 @@ const linesCross = (line1, line2) => { d3 = direction(line1[0], line1[1], line2[0]); d4 = direction(line1[0], line1[1], line2[1]); - if ( - ((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && - ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0)) - ) { + if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) { return true; } else if (d1 === 0 && onSegment(line2[0], line2[1], line1[0])) { return true; diff --git a/libs/shared/lib/vis/geovis/unfoldingEdges.tsx b/libs/shared/lib/vis/geovis/unfoldingEdges.tsx index 98e1a5bf1..7a5e34497 100644 --- a/libs/shared/lib/vis/geovis/unfoldingEdges.tsx +++ b/libs/shared/lib/vis/geovis/unfoldingEdges.tsx @@ -8,11 +8,7 @@ import { ViewModel, Command, Edge, Graph, Coordinates } from './types'; import { calcNodeDistance } from './utils'; import * as d3 from 'd3'; -export const fanEdges = ( - graph: Graph, - selectedNode: string | null, - maxWidth: number -) => { +export const fanEdges = (graph: Graph, selectedNode: string | null, maxWidth: number) => { const edgeGroups: { [key: string]: Edge[] } = {}; graph.data.edges.forEach((edge: Edge) => { @@ -26,17 +22,11 @@ export const fanEdges = ( const fanned: Edge[] = []; - const colorScale: d3.ScaleLinear<string, string> = d3 - .scaleLinear<string>() - .domain([0, 2000]) - .range(['yellow', 'red']); + const colorScale: d3.ScaleLinear<string, string> = d3.scaleLinear<string>().domain([0, 2000]).range(['yellow', 'red']); Object.values(edgeGroups).forEach((bundle) => { let adjacentNodeSelected: boolean = false; - if ( - (selectedNode !== null && selectedNode === bundle[0].from) || - selectedNode === bundle[0].to - ) { + if ((selectedNode !== null && selectedNode === bundle[0].from) || selectedNode === bundle[0].to) { adjacentNodeSelected = true; } diff --git a/libs/shared/lib/vis/geovis/utils.tsx b/libs/shared/lib/vis/geovis/utils.tsx index b7034acc1..465f4bff2 100644 --- a/libs/shared/lib/vis/geovis/utils.tsx +++ b/libs/shared/lib/vis/geovis/utils.tsx @@ -8,45 +8,26 @@ import { Graph, Viewport, Coordinates } from './types'; export const calcMaxWidth = (viewport: Viewport): number => { const vpBounds: number[] = viewport.getBounds(); - return Math.sqrt( - Math.pow(vpBounds[0] - vpBounds[2], 2) + - Math.pow(vpBounds[1] - vpBounds[3], 2) - ); + return Math.sqrt(Math.pow(vpBounds[0] - vpBounds[2], 2) + Math.pow(vpBounds[1] - vpBounds[3], 2)); }; -export const getColor = (prediction, colorScale): [number, number, number] => { +export const getColor = (prediction: any, colorScale: any): [number, number, number] => { return colorScale(prediction) .replace(/[^\d,]/g, '') .split(',') .map(Number); }; -export const calcAverageCoordinates = ( - lat: number[], - long: number[] -): [number, number] => { - return [ - lat.reduce((p, c) => p + c, 0) / lat.length, - long.reduce((p, c) => p + c, 0) / long.length, - ]; +export const calcAverageCoordinates = (lat: number[], long: number[]): [number, number] => { + return [lat.reduce((p, c) => p + c, 0) / lat.length, long.reduce((p, c) => p + c, 0) / long.length]; }; -export const calcNodeDistance = ( - source: Coordinates, - target: Coordinates -): number => { - return Math.sqrt( - Math.pow(source[0] - target[0], 2) + Math.pow(source[1] - target[1], 2) - ); +export const calcNodeDistance = (source: Coordinates, target: Coordinates): number => { + return Math.sqrt(Math.pow(source[0] - target[0], 2) + Math.pow(source[1] - target[1], 2)); }; -export const getInitialViewState = ( - graph: Graph, - coordinate_path: string = 'attributes.coordinates' -): { [key: string]: number } => { - const coordinates: Coordinates[] = graph.nodes.map((node) => - eval(`node.${coordinate_path}`) - ); +export const getInitialViewState = (graph: Graph, coordinate_path: string = 'attributes.coordinates'): { [key: string]: number } => { + const coordinates: Coordinates[] = graph.nodes.map((node) => eval(`node.${coordinate_path}`)); const viewBounds: { [key: string]: number } = { minLong: coordinates[0][0], @@ -66,10 +47,7 @@ export const getInitialViewState = ( const middleLat: number = (viewBounds.minLat + viewBounds.maxLat) / 2; const middleLong: number = (viewBounds.minLong + viewBounds.maxLong) / 2; - const maxSpan: number = Math.max( - viewBounds.maxLong - viewBounds.minLong, - viewBounds.maxLat - viewBounds.minLat - ); + const maxSpan: number = Math.max(viewBounds.maxLong - viewBounds.minLong, viewBounds.maxLat - viewBounds.minLat); const optimalZoom: number = Math.min(20, 20 * (maxSpan / 360)) - 3; @@ -80,11 +58,7 @@ export const getInitialViewState = ( }; }; -export const getKeys = ( - graph, - prefix = '', - paths = { nodes: new Set(), edges: new Set() } -) => { +export const getKeys = (graph: any, prefix = '', paths = { nodes: new Set(), edges: new Set() }) => { for (const key in graph) { if (typeof graph[key] === 'object' && graph[key] !== null) { if (Array.isArray(graph[key])) { diff --git a/libs/shared/lib/vis/index.ts b/libs/shared/lib/vis/index.ts index bf712986a..1d8457e76 100644 --- a/libs/shared/lib/vis/index.ts +++ b/libs/shared/lib/vis/index.ts @@ -1,2 +1,5 @@ export * from './rawjsonvis'; export * from './nodelink/nodelinkvis'; +export * from './paohvis/paohvis'; +export * from './semanticsubstrates/semanticsubstrates'; +// export * from './geovis/NodeLinkMap'; diff --git a/libs/shared/lib/vis/nodelink/NodeLinkViewModel.tsx b/libs/shared/lib/vis/nodelink/NodeLinkViewModel.tsx index 55e0e6b14..fa2a2ffc8 100644 --- a/libs/shared/lib/vis/nodelink/NodeLinkViewModel.tsx +++ b/libs/shared/lib/vis/nodelink/NodeLinkViewModel.tsx @@ -3,14 +3,13 @@ * Utrecht University within the Software Project course. * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { GraphType, LinkType, NodeType } from './nodelinkviz.types'; +import { GraphType, LinkType, NodeType } from '../nodelink/Types'; + import * as PIXI from 'pixi.js'; import React from 'react'; import * as d3 from 'd3'; import { jsPDF } from 'jspdf'; -import ResultNodeLinkParserUseCase, { - isNodeLinkResult, -} from '../shared/ResultNodeLinkParserUseCase'; +import ResultNodeLinkParserUseCase, { isNodeLinkResult } from '../shared/ResultNodeLinkParserUseCase'; import { AttributeData, NodeAttributeData } from '../shared/InputDataTypes'; import { AttributeCategory } from '../shared/Types'; import { GraphQueryResult } from '../../data-access/store'; // TODO remove @@ -131,7 +130,7 @@ export default class NodeLinkViewModel { this.graph = this.resultNodeLinkParserUseCase.parseQueryResult(jsonObject); this.SetNodeGraphics(this.graph, this.radius); this.simulation.restart(); - console.log('simulation restarted', jsonObject, this.graph); + console.debug('simulation restarted', jsonObject, this.graph); // this.notifyViewAboutChanges(); // if (isNodeLinkResult(jsonObject)) { @@ -150,23 +149,11 @@ export default class NodeLinkViewModel { /** Exports the nodelink diagram as a pdf for downloading. */ public async exportToPDF(): Promise<void> { - const b = await this.renderer.extract - .canvas(this.stage) - .convertToBlob?.({ type: 'image/png' }); + const b = await this.renderer.extract.canvas(this.stage).convertToBlob?.({ type: 'image/png' }); if (!b) return; const pdf = new jsPDF(); if (b) { - pdf.addImage( - URL.createObjectURL(b), - 'JPEG', - 0, - 0, - 100, - 100, - '', - 'NONE', - 0 - ); + pdf.addImage(URL.createObjectURL(b), 'JPEG', 0, 0, 100, 100, '', 'NONE', 0); pdf.save('diagram.pdf'); } else { console.log('null blob in exportToPDF'); @@ -175,9 +162,7 @@ export default class NodeLinkViewModel { /** Exports the nodelink diagram as a png for downloading. */ public async exportToPNG(): Promise<void> { - const b = await this.renderer.extract - .canvas(this.stage) - .convertToBlob?.({ type: 'image/png' }); + const b = await this.renderer.extract.canvas(this.stage).convertToBlob?.({ type: 'image/png' }); if (b) { const a = document.createElement('a'); document.body.append(a); @@ -195,9 +180,7 @@ export default class NodeLinkViewModel { const radius = this.radius; const radiusoffset = 5; // Set event listeners for drag and drop behavior - d3.select<HTMLCanvasElement, any>( - this.renderer.view as HTMLCanvasElement - ).call( + d3.select<HTMLCanvasElement, any>(this.renderer.view as HTMLCanvasElement).call( d3 .drag<HTMLCanvasElement, any>() .container(this.renderer.view as HTMLCanvasElement) @@ -214,9 +197,9 @@ export default class NodeLinkViewModel { ); // Eventlisteners for zooming (mousewheel events) - d3.select<HTMLCanvasElement, any>( - this.renderer.view as HTMLCanvasElement - ).call(d3.zoom<HTMLCanvasElement, any>().on('zoom', this.zoom)); + d3.select<HTMLCanvasElement, any>(this.renderer.view as HTMLCanvasElement).call( + d3.zoom<HTMLCanvasElement, any>().on('zoom', this.zoom) + ); } /** StartSimulation starts the d3 forcelink simulation. This has to be called after the simulation is initialised. */ @@ -313,9 +296,7 @@ export default class NodeLinkViewModel { // Set the nodes for the d3 simulation this.simulation.nodes(this.graph.nodes); - this.simulation - .force<d3.ForceLink<NodeType, LinkType>>('link') - ?.links(this.graph.links); + this.simulation.force<d3.ForceLink<NodeType, LinkType>>('link')?.links(this.graph.links); // Clear the stage and add the new nodes and links this.simulation.alpha(0.5).restart(); @@ -343,16 +324,8 @@ export default class NodeLinkViewModel { // Get the mouse position in the container coördinates const mousePos = { - x: - (event.sourceEvent.x - - event.sourceEvent.target.offsetLeft - - this.panOffset.x) / - this.scalexy, - y: - (event.sourceEvent.y - - event.sourceEvent.target.offsetTop - - this.panOffset.y) / - this.scalexy, + x: (event.sourceEvent.x - event.sourceEvent.target.offsetLeft - this.panOffset.x) / this.scalexy, + y: (event.sourceEvent.y - event.sourceEvent.target.offsetTop - this.panOffset.y) / this.scalexy, }; this.simulation.restart(); @@ -377,20 +350,8 @@ export default class NodeLinkViewModel { }); // Calculate the difference in position, to zoom at the mouse - const dx = - ((event.sourceEvent.x - - event.sourceEvent.target.offsetLeft - - this.panOffset.x) / - this.scalexy - - mousePos.x) * - this.scalexy; - const dy = - ((event.sourceEvent.y - - event.sourceEvent.target.offsetTop - - this.panOffset.y) / - this.scalexy - - mousePos.y) * - this.scalexy; + const dx = ((event.sourceEvent.x - event.sourceEvent.target.offsetLeft - this.panOffset.x) / this.scalexy - mousePos.x) * this.scalexy; + const dy = ((event.sourceEvent.y - event.sourceEvent.target.offsetTop - this.panOffset.y) / this.scalexy - mousePos.y) * this.scalexy; if (dx && dy) { this.panOffset.x += dx; this.panOffset.y += dy; @@ -410,9 +371,7 @@ export default class NodeLinkViewModel { private createNode = (node: NodeType, radius: number, selected?: boolean) => { node.gfx = new PIXI.Graphics(); node.selected = selected; - const lineColour = selected - ? this.theme.palette.custom.nodeHighlightedEdge - : this.theme.palette.custom.visBackground; + const lineColour = selected ? this.theme.palette.custom.nodeHighlightedEdge : this.theme.palette.custom.visBackground; const lineWidth = selected ? 3 : 1.5; node.gfx.lineStyle(lineWidth, Number('0x' + lineColour.replace('#', ''))); //check if not undefined. @@ -426,7 +385,7 @@ export default class NodeLinkViewModel { } } node.gfx.drawCircle(0, 0, radius); - node.gfx.interactive = true; + node.gfx.eventMode = 'auto'; node.gfx.hitArea = new PIXI.Circle(0, 0, 4); node.gfx.name = 'node_' + node.id; this.stage.addChild(node.gfx); @@ -471,16 +430,10 @@ export default class NodeLinkViewModel { if (node.attributes) { let index = 0; for (const key in node.attributes) { - const attributes = this.visibleAttributes[node.id.split('/')[0]]; + const attributes = this.visibleAttributes[node?.label || node.id]; const attributeHidden = attributes ? attributes[key] === false : false; if (attributeHidden) continue; // if attribute should be hidden, then skip - this.addAttribute( - keys, - values, - key, - String(node.attributes[key]), - index - ); + this.addAttribute(keys, values, key, String(node.attributes[key]), index); index++; } } @@ -507,13 +460,7 @@ export default class NodeLinkViewModel { /** * Helper function of createAttributes: adds an attribute to the new attributes graphic */ - private addAttribute( - keys: PIXI.Container, - values: PIXI.Container, - key: string, - value: string, - index: number - ): void { + private addAttribute(keys: PIXI.Container, values: PIXI.Container, key: string, value: string, index: number): void { // add text for key const keyText = new PIXI.Text(); keyText.style.fontSize = 14; @@ -548,10 +495,7 @@ export default class NodeLinkViewModel { /** * Helper function of calcAttrWidth: calculates the width of a child element of the attributes graphic */ - private calcObjectWidth( - object: PIXI.Text | PIXI.Graphics, - minWidth?: number - ): number { + private calcObjectWidth(object: PIXI.Text | PIXI.Graphics, minWidth?: number): number { const newWidth = object.width; return minWidth ? Math.max(newWidth, minWidth) : newWidth; } @@ -565,14 +509,6 @@ export default class NodeLinkViewModel { event.subject.fy = event.subject.y; this.dragOffset.x = event.subject.x; this.dragOffset.y = event.subject.y; - - // Toggle display of the attributes of the node - const node = this.simulation.find(event.x, event.y, 15); - - // Null check - if (node) { - this.ToggleInformationOnNode(node); - } }; /** @@ -583,9 +519,9 @@ export default class NodeLinkViewModel { public ToggleInformationOnNode(node: NodeType) { this.simulation.alphaTarget(0).restart(); // renderer will not always update without this line this.showAttributes(node); - this.highlightNode(node); - this.highlightLinks(node); - this.showShortestPath(); + // this.highlightNode(node); + // this.highlightLinks(node); + // this.showShortestPath(); } /** @@ -612,8 +548,7 @@ export default class NodeLinkViewModel { if (shortestPathData === undefined) { console.warn('Something went wrong with shortest path calculation'); } else { - const path: string[] = - shortestPathData[shortestPathNodes[index + 1].id]; + const path: string[] = shortestPathData[shortestPathNodes[index + 1].id]; allPaths = allPaths.concat(this.getShortestPathEdges(path)); } index++; @@ -643,11 +578,9 @@ export default class NodeLinkViewModel { this.graph.links.forEach((link: any) => { const { source, target } = link; if ( - (pathString[index] == source.id && - pathString[index + 1] == target.id) || + (pathString[index] == source.id && pathString[index + 1] == target.id) || (pathString[index] == source && pathString[index + 1] == target) || - (pathString[index + 1] == source.id && - pathString[index] == target.id) || + (pathString[index + 1] == source.id && pathString[index] == target.id) || (pathString[index + 1] == source && pathString[index] == target) ) { newPath.push(link); @@ -655,9 +588,7 @@ export default class NodeLinkViewModel { } }); if (!edgeFound) { - console.warn( - 'skipped path: ' + pathString[index] + ' ' + pathString[index + 1] - ); + console.warn('skipped path: ' + pathString[index] + ' ' + pathString[index + 1]); } index++; } @@ -680,10 +611,8 @@ export default class NodeLinkViewModel { // Moving the node if (this.scalexy != 1) { - event.subject.fx = - this.dragOffset.x - (this.dragOffset.x - event.x) / this.scalexy; - event.subject.fy = - this.dragOffset.y - (this.dragOffset.y - event.y) / this.scalexy; + event.subject.fx = this.dragOffset.x - (this.dragOffset.x - event.x) / this.scalexy; + event.subject.fy = this.dragOffset.y - (this.dragOffset.y - event.y) / this.scalexy; } else { event.subject.fx = event.x; event.subject.fy = event.y; @@ -701,6 +630,13 @@ export default class NodeLinkViewModel { } event.subject.fx = null; event.subject.fy = null; + + // Toggle display of the attributes of the node + const node = this.simulation.find(event.x, event.y, 15); + // Null check + if (!!node) { + this.ToggleInformationOnNode(node); + } }; /** @@ -719,9 +655,7 @@ export default class NodeLinkViewModel { //Else add it to the list. else this.currentHightlightedNodes.push(node); //Update the list of edges that need to be highlighted - this.currentlyHighlightedLinks = this.getRelatedLinks( - this.currentHightlightedNodes - ); + this.currentlyHighlightedLinks = this.getRelatedLinks(this.currentHightlightedNodes); } /** @@ -768,12 +702,7 @@ export default class NodeLinkViewModel { const { source, target } = link; if (this.isLinkVisiable(link)) { nodes.forEach((node: NodeType) => { - if ( - source == node || - target == node || - source == node.id || - target == node.id - ) { + if (source == node || target == node || source == node.id || target == node.id) { relatedLinks.push(link); } }); @@ -794,15 +723,12 @@ export default class NodeLinkViewModel { /** Ticked is the updater function of the simulation. Every 'tick' all the new positions of the nodes (and thus the edges) are recalculated and updated. */ public ticked = () => { this.graph.nodes.forEach((node: any) => { - if (node === undefined) return; - if (node?.gfx === undefined) node.gfx = {}; + if (!node || !node.x || !node.y) return; + // if (node?.gfx === undefined) node.gfx = {}; node.gfx.position = new PIXI.Point(node.x, node.y); // Update attributes position if they exist if (node.gfxAttributes) { - node.gfxAttributes.position = new PIXI.Point( - node.x - node.gfxAttributes.width / 2, - node.y - node.gfxAttributes.height - 20 - ); + node.gfxAttributes.position = new PIXI.Point(node.x - node.gfxAttributes.width / 2, node.y - node.gfxAttributes.height - 20); } }); @@ -892,10 +818,7 @@ export default class NodeLinkViewModel { * @param attributeData NodeAttributeData returned by graph scheme analytics. * @param attributeDataType Routing key. */ - public initializeUniqueAttributes( - attributeData: AttributeData, - attributeDataType: string - ): void { + public initializeUniqueAttributes(attributeData: AttributeData, attributeDataType: string): void { if (attributeDataType === 'gsa_node_result') { const entity = attributeData as NodeAttributeData; entity.attributes.forEach((attribute) => { diff --git a/libs/shared/lib/vis/nodelink/Types.tsx b/libs/shared/lib/vis/nodelink/Types.tsx index 591ef18c8..377847dc5 100644 --- a/libs/shared/lib/vis/nodelink/Types.tsx +++ b/libs/shared/lib/vis/nodelink/Types.tsx @@ -21,6 +21,7 @@ export interface NodeType extends d3.SimulationNodeDatum { id: string; // Number to determine the color of the node + label?: string; type: number; attributes?: Record<string, any>; cluster?: number; diff --git a/libs/shared/lib/vis/nodelink/nodelinkvis.stories.tsx b/libs/shared/lib/vis/nodelink/nodelinkvis.stories.tsx index f8f03714a..113e73554 100644 --- a/libs/shared/lib/vis/nodelink/nodelinkvis.stories.tsx +++ b/libs/shared/lib/vis/nodelink/nodelinkvis.stories.tsx @@ -12,11 +12,7 @@ import { import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '../../data-access/theme'; -import { - big2ndChamberQueryResult, - smallFlightsQueryResults, - mockLargeQueryResults -} from '../../mock-data'; +import { big2ndChamberQueryResult, smallFlightsQueryResults, mockLargeQueryResults } from '../../mock-data'; const Component: Meta<typeof NodeLinkVis> = { /* 👇 The title prop is optional. @@ -58,49 +54,57 @@ export const TestWithData = { const dispatch = Mockstore.dispatch; dispatch( assignNewGraphQueryResult({ - nodes: [ - { id: 'agent/007', attributes: { name: 'Daniel Craig' } }, - { id: 'villain', attributes: { name: 'Le Chiffre' } }, - ], - edges: [ - { from: 'agent/007', to: 'villain', attributes: { name: 'Escape' } }, - ], + type: 'nodelink', + payload: { + nodes: [ + { id: 'agent/007', attributes: { name: 'Daniel Craig' } }, + { id: 'villain', attributes: { name: 'Le Chiffre' } }, + ], + edges: [{ id: 'escape/escape', from: 'agent/007', to: 'villain', attributes: { name: 'Escape' } }], + }, }) - ) - } -} + ); + }, +}; export const TestWithNoData = { - args: { loading: false }, play: async () => { + args: { loading: false }, + play: async () => { const dispatch = Mockstore.dispatch; dispatch( assignNewGraphQueryResult({ - nodes: [], - edges: [], + type: 'nodelink', + payload: { + nodes: [], + edges: [], + }, }) ); - } + }, }; export const TestWithBig2ndChamber = { - args: { loading: false }, play: async () => { + args: { loading: false }, + play: async () => { const dispatch = Mockstore.dispatch; - dispatch(assignNewGraphQueryResult(big2ndChamberQueryResult)); - } + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: big2ndChamberQueryResult })); + }, }; export const TestWithSmallFlights = { - args: { loading: false }, play: async () => { + args: { loading: false }, + play: async () => { const dispatch = Mockstore.dispatch; - dispatch(assignNewGraphQueryResult(smallFlightsQueryResults)); - } + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: smallFlightsQueryResults })); + }, }; export const TestWithLargeQueryResult = { - args: { loading: false }, play: async () => { + args: { loading: false }, + play: async () => { const dispatch = Mockstore.dispatch; - dispatch(assignNewGraphQueryResult(mockLargeQueryResults)); - } + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: mockLargeQueryResults })); + }, }; // export const Loading = Template.bind({}); diff --git a/libs/shared/lib/vis/nodelink/nodelinkvis.tsx b/libs/shared/lib/vis/nodelink/nodelinkvis.tsx index 09e3295b2..8a4b6aa11 100644 --- a/libs/shared/lib/vis/nodelink/nodelinkvis.tsx +++ b/libs/shared/lib/vis/nodelink/nodelinkvis.tsx @@ -1,8 +1,4 @@ -import { - changePrimary, - useAppDispatch, - useGraphQueryResult, -} from '../../data-access/store'; +import { changePrimary, useAppDispatch, useGraphQueryResult } from '../../data-access/store'; import { useTheme } from '@mui/material'; import React, { LegacyRef, useEffect, useRef, useState } from 'react'; import styled from 'styled-components'; @@ -13,7 +9,6 @@ import { GraphType, LinkType, NodeType } from './Types'; import NodeLinkViewModel from './NodeLinkViewModel'; import ResultNodeLinkParserUseCase from '../shared/ResultNodeLinkParserUseCase'; import VisConfigPanelComponent from '../shared/VisConfigPanel/VisConfigPanel'; -import NodeLinkConfigPanelComponent from './config-panel/NodeConfigPanelComponent'; import { use } from 'cytoscape'; interface Props { @@ -67,17 +62,16 @@ export const NodeLinkVis = React.memo((props: Props) => { const graphQueryResult = useGraphQueryResult(); const dispatch = useAppDispatch(); const theme = useTheme(); - console.log('update nodelink'); useEffect(() => { - console.log('update nodelink useEffect', graphQueryResult); + console.debug('update nodelink useEffect', graphQueryResult); nodeLinkViewModelRef?.current?.consumeMessageFromBackend(graphQueryResult); nodeLinkViewModelRef?.current?.startSimulation(); nodeLinkViewModelRef?.current?.selectD3Elements(); }, [graphQueryResult]); useEffect(() => { - console.log('loaded NodeLinkVis'); + console.debug('loaded NodeLinkVis'); const resultNodeLinkParserUseCase = new ResultNodeLinkParserUseCase(); const nodeLinkViewModel = new NodeLinkViewModel( resultNodeLinkParserUseCase, @@ -119,12 +113,8 @@ export const NodeLinkVis = React.memo((props: Props) => { if (nodeLinkViewModelRef?.current) { // Detach Viewmodel Observer // nodeLinkViewModel.detachView(); - window.removeEventListener( - 'resize', - nodeLinkViewModelRef.current.handleResize - ); + window.removeEventListener('resize', nodeLinkViewModelRef.current.handleResize); } - }; }, []); @@ -181,7 +171,7 @@ export const NodeLinkVis = React.memo((props: Props) => { width: '100%', height: '100%', overflow: 'hidden', - backgroundColor: '#ffffff', // + props.currentColours.visBackground, TODO + backgroundColor: theme.palette.custom.visBackground, }} className="renderer" ref={myRef} diff --git a/libs/shared/lib/vis/nodelink/nodelinkviz.types.tsx b/libs/shared/lib/vis/nodelink/nodelinkviz.types.tsx deleted file mode 100644 index 591ef18c8..000000000 --- a/libs/shared/lib/vis/nodelink/nodelinkviz.types.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import * as PIXI from 'pixi.js'; - -/** Types for the nodes and links in the node-link diagram. */ -export type GraphType = { - nodes: NodeType[]; - links: LinkType[]; - linkPrediction: boolean; - shortestPath: boolean; - communityDetection: boolean; - numberOfMlClusters?: number; -}; - -/** The interface for a node in the node-link diagram */ -export interface NodeType extends d3.SimulationNodeDatum { - id: string; - - // Number to determine the color of the node - type: number; - attributes?: Record<string, any>; - cluster?: number; - clusterAccoringToMLData?: number; - shortestPathData?: Record<string, string[]>; - - // Node that is drawn. - gfx?: PIXI.Graphics; - radius?: number; - // Text to be displayed on top of the node. - gfxtext?: PIXI.Text; - gfxAttributes?: PIXI.Graphics; - selected?: boolean; - index?: number; - - // The text that will be shown on top of the node if selected. - displayInfo?: string; - - // Node’s current x-position. - x?: number; - - // Node’s current y-position. - y?: number; - - // Node’s current x-velocity - vx?: number; - - // Node’s current y-velocity - vy?: number; - - // Node’s fixed x-position (if position was fixed) - fx?: number | null; - - // Node’s fixed y-position (if position was fixed) - fy?: number | null; -} - -/** The interface for a link in the node-link diagram */ -export interface LinkType extends d3.SimulationLinkDatum<NodeType> { - // The thickness of a line - value: number; - // To check if an edge is calculated based on a ML algorithm - mlEdge: boolean; -} - -/**collectionNode holds 1 entry per node kind (so for example a MockNode with name "parties" and all associated attributes,) */ -export type TypeNode = { - name: string; //Collection name - attributes: string[]; //attributes. This includes all attributes found in the collection - type: number | undefined; //number that represents collection of node, for colorscheme - visualisations: Visualisation[]; //The way to visualize attributes of this Node kind -}; - -export type CommunityDetectionNode = { - cluster: number; //group as used by colouring scheme -}; - -/**Visualisation holds the visualisation method for an attribute */ -export type Visualisation = { - attribute: string; //attribute type (e.g. 'age') - vis: string; //visualisation type (e.g. 'radius') -}; - -/** possible colors to pick from*/ -export type Colors = { - name: string; -}; - -/**AssignedColors is a simple holder for color selection */ -export type AssignedColors = { - collection: number | undefined; //number of the collection (type or group) - color: string; //color in hex - default: string; //default color, for easy switching back -}; diff --git a/libs/shared/lib/vis/paohvis/components/HyperEdgesRange.tsx b/libs/shared/lib/vis/paohvis/components/HyperEdgesRange.tsx index 6d46ce235..d36092229 100644 --- a/libs/shared/lib/vis/paohvis/components/HyperEdgesRange.tsx +++ b/libs/shared/lib/vis/paohvis/components/HyperEdgesRange.tsx @@ -44,7 +44,7 @@ export const HyperEdgeRange = (props: HyperEdgeRangeProps) => { fill={i % 2 === 0 ? '#e6e6e6' : '#f5f5f5'} style={{ zIndex: -1 }} /> - </g>, + </g> ); let hyperedges = props.hyperEdges.map((hyperEdge, i) => ( @@ -55,11 +55,7 @@ export const HyperEdgeRange = (props: HyperEdgeRangeProps) => { numbersOnRows={hyperEdge.frequencies} nameToShow={hyperEdge.nameToShow} rowHeight={props.rowHeight} - xOffset={ - props.hyperedgeColumnWidth / 2 + - i * props.hyperedgeColumnWidth + - props.gapBetweenRanges - } + xOffset={props.hyperedgeColumnWidth / 2 + i * props.hyperedgeColumnWidth + props.gapBetweenRanges} radius={8} onMouseEnter={props.onMouseEnter} onMouseLeave={props.onMouseLeave} @@ -67,10 +63,7 @@ export const HyperEdgeRange = (props: HyperEdgeRangeProps) => { )); // * 3 because we have a gapBetweenRanges as padding - const xOffset = - props.xOffset + - (props.index * 3 + 1) * props.gapBetweenRanges + - props.colOffset * props.hyperedgeColumnWidth; + const xOffset = props.xOffset + (props.index * 3 + 1) * props.gapBetweenRanges + props.colOffset * props.hyperedgeColumnWidth; return ( <g transform={'translate(' + xOffset + ', +' + props.yOffset + ')'}> @@ -79,7 +72,10 @@ export const HyperEdgeRange = (props: HyperEdgeRangeProps) => { x="10" y={props.rowHeight / 2} dy=".35em" - style={{ fontWeight: 600, transform: 'translate3d(-10px, 15px, 0px) rotate(-30deg)' }} + style={{ + fontWeight: 600, + transform: 'translate3d(-10px, 15px, 0px) rotate(-30deg)', + }} > {props.text} </text> @@ -89,7 +85,7 @@ export const HyperEdgeRange = (props: HyperEdgeRangeProps) => { </g> </g> ); -} +}; type HyperEdgeProps = { col: number; @@ -113,21 +109,13 @@ const HyperEdge = (props: HyperEdgeProps) => { .on('mouseover', () => props.onMouseEnter(props.col, props.nameToShow)) .on('mouseout', () => props.onMouseLeave(props.col)); } - }, [ref.current]) - + }, [ref.current]); // render all circles on the correct position. const circles = props.connectedRows.map((row, i) => { return ( <g key={'row' + row + ' col' + i}> - <circle - cx={0} - cy={row * props.rowHeight} - r={props.radius} - fill={'white'} - stroke={'black'} - strokeWidth={1} - /> + <circle cx={0} cy={row * props.rowHeight} r={props.radius} fill={'white'} stroke={'black'} strokeWidth={1} /> </g> ); }); @@ -138,22 +126,16 @@ const HyperEdge = (props: HyperEdgeProps) => { let y2; for (let i = 1; i < props.connectedRows.length; i++) { y2 = props.connectedRows[i] * props.rowHeight - props.radius; - lines.push( - <line key={'line' + i} x1={0} y1={y1} x2={0} y2={y2} stroke={'black'} strokeWidth={1} />, - ); + lines.push(<line key={'line' + i} x1={0} y1={y1} x2={0} y2={y2} stroke={'black'} strokeWidth={1} />); y1 = props.connectedRows[i] * props.rowHeight + props.radius; } const yOffset = props.rowHeight * 0.5; return ( - <g - ref={ref} - className={'col-' + props.col} - transform={'translate(' + props.xOffset + ',' + yOffset + ')'} - > + <g ref={ref} className={'col-' + props.col} transform={'translate(' + props.xOffset + ',' + yOffset + ')'}> {lines} {circles} </g> ); -} +}; diff --git a/libs/shared/lib/vis/paohvis/components/MakePaohvisMenu.scss b/libs/shared/lib/vis/paohvis/components/MakePaohvisMenu.scss index dd4f3bbc2..0db6bf0a1 100644 --- a/libs/shared/lib/vis/paohvis/components/MakePaohvisMenu.scss +++ b/libs/shared/lib/vis/paohvis/components/MakePaohvisMenu.scss @@ -4,10 +4,8 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ - /* istanbul ignore file */ - /* The comment above was added so the code coverage wouldn't count this file towards code coverage. * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ @@ -30,4 +28,4 @@ height: 40px; margin-right: 5em; font-weight: bold; -} \ No newline at end of file +} diff --git a/libs/shared/lib/vis/paohvis/components/MakePaohvisMenu.tsx b/libs/shared/lib/vis/paohvis/components/MakePaohvisMenu.tsx index d19e1292c..1d2f2a04e 100644 --- a/libs/shared/lib/vis/paohvis/components/MakePaohvisMenu.tsx +++ b/libs/shared/lib/vis/paohvis/components/MakePaohvisMenu.tsx @@ -38,7 +38,7 @@ type MakePaohvisMenuProps = { relationName: string, isEntityFromRelationFrom: boolean, chosenAttribute: Attribute, - nodeOrder: PaohvisNodeOrder, + nodeOrder: PaohvisNodeOrder ) => void; }; @@ -60,8 +60,8 @@ type MakePaohvisMenuState = { relationNameOptions: string[]; attributeNameOptions: Record<string, string[]>; - relationValue: string, - attributeValue: string, + relationValue: string; + attributeValue: string; }; /** React component that renders a menu with input fields for adding a new Paohvis visualisation. */ @@ -79,7 +79,11 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { entitiesFromQueryResult: [], sortOrder: NodeOrder.degree, isReverseOrder: false, - entitiesFromSchema: { entityNames: [], attributesPerEntity: {}, relationsPerEntity: {} }, + entitiesFromSchema: { + entityNames: [], + attributesPerEntity: {}, + relationsPerEntity: {}, + }, relationsFromSchema: { relationCollection: [], relationNames: {}, @@ -115,18 +119,12 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { let relationSplit = state.relationsFromSchema.relationNames[relation].split(':'); //check if relation is valid - if ( - draft.entitiesFromQueryResult.includes(relationSplit[0]) && - draft.entitiesFromQueryResult.includes(relationSplit[1]) - ) { + if (draft.entitiesFromQueryResult.includes(relationSplit[0]) && draft.entitiesFromQueryResult.includes(relationSplit[1])) { // check if relation is selfedge if (relationSplit[0] == relationSplit[1]) { const relationFrom = `${relation}:${EntityOrigin.from}`; const relationTo = `${relation}:${EntityOrigin.to}`; - if ( - !draft.relationNameOptions.includes(relationFrom) && - !draft.relationNameOptions.includes(relationTo) - ) { + if (!draft.relationNameOptions.includes(relationFrom) && !draft.relationNameOptions.includes(relationTo)) { draft.relationNameOptions.push(relationFrom); draft.relationNameOptions.push(relationTo); } @@ -134,9 +132,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { } }); // filter out duplicates - draft.relationNameOptions = draft.relationNameOptions.filter( - (n, i) => draft.relationNameOptions.indexOf(n) === i, - ); + draft.relationNameOptions = draft.relationNameOptions.filter((n, i) => draft.relationNameOptions.indexOf(n) === i); } draft.relationValue = ''; @@ -151,8 +147,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { draft.attributeNameOptions = {}; return draft; }); - }; - + } /** * Called when the relationName field is changed. @@ -184,9 +179,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { const entityOrigin = newRelationSplit[1]; if (!isValidEntityOrigin(entityOrigin)) - throw new Error( - `The entity "${entityVertical}" has an invalid entity origin: ${entityOrigin}.`, - ); + throw new Error(`The entity "${entityVertical}" has an invalid entity origin: ${entityOrigin}.`); draft.isEntityVerticalEqualToRelationFrom = entityOrigin == EntityOrigin.from; draft.entityHorizontal = relationSplit[0]; @@ -200,16 +193,12 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { else if (entityVertical == relationSplit[1]) { draft.isEntityVerticalEqualToRelationFrom = false; draft.entityHorizontal = relationSplit[0]; - } else - throw new Error( - `The relationNames from this.relationsFromSchema for ${newRelation} is invalid`, - ); + } else throw new Error(`The relationNames from this.relationsFromSchema for ${newRelation} is invalid`); draft.attributeNameOptions[AttributeOrigin.entity] = []; if (draft.entitiesFromSchema.attributesPerEntity[draft.entityHorizontal]) { - let allAttributesOfEntity: AttributeNames = - draft.entitiesFromSchema.attributesPerEntity[draft.entityHorizontal]; + let allAttributesOfEntity: AttributeNames = draft.entitiesFromSchema.attributesPerEntity[draft.entityHorizontal]; let attributeNamesOfEntity: string[] = allAttributesOfEntity.textAttributeNames .concat(allAttributesOfEntity.numberAttributeNames) @@ -220,8 +209,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { draft.attributeNameOptions[AttributeOrigin.relation] = []; if (draft.relationsFromSchema.attributesPerRelation[newRelation]) { - let allAttributesOfRelation: AttributeNames = - draft.relationsFromSchema.attributesPerRelation[newRelation]; + let allAttributesOfRelation: AttributeNames = draft.relationsFromSchema.attributesPerRelation[newRelation]; let attributeNamesOfRelation: string[] = allAttributesOfRelation.textAttributeNames .concat(allAttributesOfRelation.numberAttributeNames) @@ -231,8 +219,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { return draft; }); - }; - + } /** * Called when the attributeName field is changed. @@ -252,7 +239,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { draft.isButtonEnabled = true; return draft; }); - }; + } /** * Called when the sort order field is changed. @@ -272,8 +259,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { return draft; }); } else throw new Error(newSortOrder + ' is not a sort order.'); - }; - + } /** * Called when the user clicks the reverse order button. @@ -297,8 +283,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { } return draft; }); - }; - + } /** * Called when the user clicks the "Make" button. @@ -329,7 +314,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { { orderBy: state.sortOrder, isReverseOrder: state.isReverseOrder, - }, + } ); } else { setState((draft) => { @@ -338,7 +323,7 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { }); throw new Error('Error: chosen entity or relation is invalid.'); } - }; + } /** * Gets the type of the value of the specified attribute by . @@ -411,7 +396,6 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { }); }, [schema]); - /** This method filters and makes a new Paohvis table. */ useEffect(() => { if (isNodeLinkResult(graphQueryResult)) { @@ -421,11 +405,10 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { return draft; }); } else { - console.error('Invalid query result!') + console.error('Invalid query result!'); } }, [graphQueryResult]); - /** This resets the configuration. Should be called when the possible entities and relations change. */ function resetConfig(): void { setState((draft) => { @@ -450,13 +433,13 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { // Retrieve the possible entity options. If none available, set helper message. let entityMenuItems: ReactElement[]; - if (state.entitiesFromQueryResult.length > 0) + if (state.entitiesFromQueryResult.length > 0) { entityMenuItems = state.entitiesFromQueryResult.map((entity) => ( <MenuItem key={entity} value={entity}> {entity} </MenuItem> )); - else + } else entityMenuItems = [ <MenuItem key="placeholder" value="" disabled> No query data available @@ -484,29 +467,23 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { let attributeNameMenuItemsEntity: ReactElement[] = []; let attributeNameMenuItemsRelation: ReactElement[] = []; if (state.attributeNameOptions['Entity'] && state.attributeNameOptions['Relation']) { - attributeNameMenuItemsNoAttribute = state.attributeNameOptions['No attribute'].map( - (attribute) => ( - <MenuItem key={attribute} value={`${attribute}:NoAttribute`}> - {attribute} - </MenuItem> - ), - ); + attributeNameMenuItemsNoAttribute = state.attributeNameOptions['No attribute'].map((attribute) => ( + <MenuItem key={attribute} value={`${attribute}:NoAttribute`}> + {attribute} + </MenuItem> + )); attributeNameMenuItemsEntity = state.attributeNameOptions['Entity'].map((attribute) => ( <MenuItem key={`${attribute}:Entity`} value={`${attribute}:Entity`}> {`${attribute} : Entity`} </MenuItem> )); - attributeNameMenuItemsRelation = state.attributeNameOptions['Relation'].map( - (attribute) => ( - <MenuItem key={`${attribute}:Relation`} value={`${attribute}:Relation`}> - {`${attribute} : Relation`} - </MenuItem> - ), - ); + attributeNameMenuItemsRelation = state.attributeNameOptions['Relation'].map((attribute) => ( + <MenuItem key={`${attribute}:Relation`} value={`${attribute}:Relation`}> + {`${attribute} : Relation`} + </MenuItem> + )); - attributeNameMenuItems = attributeNameMenuItemsNoAttribute - .concat(attributeNameMenuItemsEntity) - .concat(attributeNameMenuItemsRelation); + attributeNameMenuItems = attributeNameMenuItemsNoAttribute.concat(attributeNameMenuItemsEntity).concat(attributeNameMenuItemsRelation); } else attributeNameMenuItems = [ <MenuItem key="placeholder" value="" disabled> @@ -577,24 +554,15 @@ export const MakePaohvisMenu = (props: MakePaohvisMenuProps) => { > {sortOrderMenuItems} </TextField> - <IconButton - className={'reverseIconButton'} - color="inherit" - onClick={onClickReverseOrder} - > + <IconButton className={'reverseIconButton'} color="inherit" onClick={onClickReverseOrder}> {reverseIcon} </IconButton> - <Button - id="makeButton" - variant="contained" - disabled={!state.isButtonEnabled} - onClick={onClickMakeButton} - > + <Button id="makeButton" variant="contained" disabled={!state.isButtonEnabled} onClick={onClickMakeButton}> <span>Make</span> </Button> </div> ); -} +}; export default MakePaohvisMenu; diff --git a/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.module.scss b/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.module.scss index d6ef0a52a..250f33779 100644 --- a/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.module.scss +++ b/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.module.scss @@ -4,10 +4,8 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ - /* istanbul ignore file */ - /* The comment above was added so the code coverage wouldn't count this file towards code coverage. * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ @@ -17,6 +15,7 @@ font-family: 'Open Sans', sans-serif; display: flex; flex-direction: column; + margin-bottom: 2rem; p { font-size: 13px; font-weight: 600; @@ -54,4 +53,16 @@ text-overflow: ellipsis; } } -} \ No newline at end of file + .selectGroup { + padding: 10; + display: flex; + flex-direction: column; + width: 80%; + gap: 0.6em; + margin-top: 1em; + } + .selectButtonGroup { + display: flex; + gap: 0.6em; + } +} diff --git a/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.module.scss.d.ts b/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.module.scss.d.ts index 0139761e2..b6cdff05a 100644 --- a/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.module.scss.d.ts +++ b/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.module.scss.d.ts @@ -3,5 +3,7 @@ declare const classNames: { readonly title: 'title'; readonly subtitle: 'subtitle'; readonly selectContainer: 'selectContainer'; + readonly selectGroup: 'selectGroup'; + readonly selectButtonGroup: 'selectButtonGroup'; }; export = classNames; diff --git a/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.tsx b/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.tsx index 6876e9363..76377ee7d 100644 --- a/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.tsx +++ b/libs/shared/lib/vis/paohvis/components/PaohvisFilterComponent.tsx @@ -15,10 +15,9 @@ import { useImmer } from 'use-immer'; import { Button, MenuItem, TextField } from '@mui/material'; import { useGraphQueryResult, useSchemaGraph } from '@graphpolaris/shared/lib/data-access'; import { isNodeLinkResult } from '../../shared/ResultNodeLinkParserUseCase'; -import { isSchemaResult } from '../../shared/SchemaResultType'; import { calculateAttributesAndRelations, calculateAttributesFromRelation } from '../utils/utils'; import { boolPredicates, numberPredicates, textPredicates } from '../models/FilterPredicates'; - +import { style } from 'd3'; type PaohvisFilterProps = { axis: FilterType; @@ -26,13 +25,7 @@ type PaohvisFilterProps = { entityHorizontal: string; relationName: string; - filterPaohvis( - isTargetEntity: boolean, - filterTarget: string, - attributeName: string, - predicate: string, - compareValue: string, - ): void; + filterPaohvis(isTargetEntity: boolean, filterTarget: string, attributeName: string, predicate: string, compareValue: string): void; resetFilter(isTargetEntity: boolean): void; }; @@ -48,7 +41,6 @@ type PaohvisFilterState = { namesPerEntityOrRelation: string[]; attributesPerEntityOrRelation: Record<string, AttributeNames>; - }; /** Component for rendering the filters for PAOHvis */ @@ -71,39 +63,38 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { }); /** - * Updates the list of attributes that can be chosen from for the filter component. - * Takes all attributes from the filter target and converts them to the format 'name:type'. - */ + * Updates the list of attributes that can be chosen from for the filter component. + * Takes all attributes from the filter target and converts them to the format 'name:type'. + */ function updateAttributeNameOptions() { const filterTarget = state.filterTarget; const namesPerEntityOrRelation = state.namesPerEntityOrRelation; const attributesPerEntityOrRelation = state.attributesPerEntityOrRelation; - if (!namesPerEntityOrRelation.includes(filterTarget)) - throw new Error('The filter target does not exist in the schema'); + if (!namesPerEntityOrRelation.includes(filterTarget)) throw new Error('The filter target does not exist in the schema'); - setState(draft => { + setState((draft) => { // Add all possible options for attributes to attributeNamesOptions // check all text attributes if (attributesPerEntityOrRelation[filterTarget].textAttributeNames.length > 0) attributesPerEntityOrRelation[filterTarget].textAttributeNames.map((attributeName) => - draft.attributeNamesOptions.push(`${attributeName}:text`), + draft.attributeNamesOptions.push(`${attributeName}:text`) ); // check all number attributes if (attributesPerEntityOrRelation[filterTarget].numberAttributeNames.length > 0) attributesPerEntityOrRelation[filterTarget].numberAttributeNames.map((attributeName) => - draft.attributeNamesOptions.push(`${attributeName}:number`), + draft.attributeNamesOptions.push(`${attributeName}:number`) ); // check all bool attributes if (attributesPerEntityOrRelation[filterTarget].boolAttributeNames.length > 0) attributesPerEntityOrRelation[filterTarget].boolAttributeNames.map((attributeName) => - draft.attributeNamesOptions.push(`${attributeName}:bool`), + draft.attributeNamesOptions.push(`${attributeName}:bool`) ); return draft; }); - }; + } /** * This is called when the name of the attribute is changed in a filter component. @@ -115,11 +106,9 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { const newAttributeSplit = event.target.value.split(':'); const newAttributeType = newAttributeSplit[1]; - setState(draft => { + setState((draft) => { if (!containsFilterTargetChosenAttribute(newAttributeNameAndType)) - throw new Error( - 'The chosen attribute does not exist in the entity/relation that will be filtered', - ); + throw new Error('The chosen attribute does not exist in the entity/relation that will be filtered'); switch (newAttributeType) { case 'text': @@ -140,24 +129,24 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { draft.isFilterButtonEnabled = false; return draft; }); - }; + } /** -* This is called when the value of the predicate is changed in the filter component. -* @param event that called this eventhandler. -*/ + * This is called when the value of the predicate is changed in the filter component. + * @param event that called this eventhandler. + */ function onChangePredicate(event: React.ChangeEvent<HTMLInputElement>): void { const newpredicate = event.target.value; if (!isPredicateValid(newpredicate)) throw new Error('The chosen predicate is invalid'); - setState(draft => { + setState((draft) => { draft.predicate = newpredicate; draft.compareValue = ''; draft.isFilterButtonEnabled = false; return draft; }); - }; + } /** * This is called when the value to compare the attribute with is changed in a filter component. @@ -166,35 +155,28 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { * @param event that called this eventhandler. */ function onChangeCompareValue(event: React.ChangeEvent<HTMLInputElement>): void { - setState(draft => { + setState((draft) => { draft.compareValue = event.target.value; draft.isFilterButtonEnabled = isFilterConfigurationValid(); return draft; }); - }; + } /** -* This is called when the "apply filter" button is clicked. -* It checks if the input is correct and sends the information for the filters to the PaohvisViewModelImpl. -*/ + * This is called when the "apply filter" button is clicked. + * It checks if the input is correct and sends the information for the filters to the PaohvisViewModelImpl. + */ function onClickFilterPaohvisButton(): void { - if (!isFilterConfigurationValid()) - throw new Error('Error: chosen attribute of predicate-value is invalid.'); - - props.filterPaohvis( - isTargetEntity, - state.filterTarget, - state.attributeNameAndType, - state.predicate, - state.compareValue, - ); - }; + if (!isFilterConfigurationValid()) throw new Error('Error: chosen attribute of predicate-value is invalid.'); + + props.filterPaohvis(isTargetEntity, state.filterTarget, state.attributeNameAndType, state.predicate, state.compareValue); + } /** - * This resets the filters for the chosen filter component. - */ + * This resets the filters for the chosen filter component. + */ function onClickResetFilter(): void { - setState(draft => { + setState((draft) => { draft.attributeNameAndType = ''; draft.attributeType = ''; draft.predicate = ''; @@ -204,12 +186,12 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { return draft; }); props.resetFilter(isTargetEntity); - }; + } /** - * Checks if the filter configuration is valid. - * @returns {boolean} true if the filter configuration is valid. - */ + * Checks if the filter configuration is valid. + * @returns {boolean} true if the filter configuration is valid. + */ function isFilterConfigurationValid(): boolean { return ( containsFilterTargetChosenAttribute(state.attributeNameAndType) && @@ -254,11 +236,11 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { switch (attributeType) { case 'text': - return attributesPerEntityOrRelation[filterTarget].textAttributeNames.includes(attributeName,); + return attributesPerEntityOrRelation[filterTarget].textAttributeNames.includes(attributeName); case 'number': - return attributesPerEntityOrRelation[filterTarget].numberAttributeNames.includes(attributeName,); + return attributesPerEntityOrRelation[filterTarget].numberAttributeNames.includes(attributeName); case 'bool': - return attributesPerEntityOrRelation[filterTarget].boolAttributeNames.includes(attributeName,); + return attributesPerEntityOrRelation[filterTarget].boolAttributeNames.includes(attributeName); default: return false; } @@ -270,16 +252,10 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { * @param entityHorizontal is the entity type that belongs to the X-axis of the Paohvis table. * @param relationName is the relation type that is that is displayed in the Paohvis table */ - function determineFilterTarget( - entityVertical: string, - entityHorizontal: string, - relationName: string, - ): void { - setState(draft => { - if (props.axis === FilterType.yaxis) - draft.filterTarget = entityVertical; - else if (props.axis === FilterType.xaxis) - draft.filterTarget = entityHorizontal; + function determineFilterTarget(entityVertical: string, entityHorizontal: string, relationName: string): void { + setState((draft) => { + if (props.axis === FilterType.yaxis) draft.filterTarget = entityVertical; + else if (props.axis === FilterType.xaxis) draft.filterTarget = entityHorizontal; else draft.filterTarget = relationName; return draft; }); @@ -291,19 +267,19 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { useEffect(() => { if (isNodeLinkResult(graphQueryResult)) { - setState(draft => { + setState((draft) => { draft.filterTarget = ''; draft.attributeNamesOptions = []; return draft; - }) + }); } else { - console.error('Invalid query result!') + console.error('Invalid query result!'); } }, [graphQueryResult]); useEffect(() => { // if (isSchemaResult(schema)) { - setState(draft => { + setState((draft) => { if (isTargetEntity) { const entitiesFromSchema = calculateAttributesAndRelations(schema); draft.namesPerEntityOrRelation = entitiesFromSchema.entityNames; @@ -323,11 +299,9 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { // } }, [schema]); - useEffect(() => { determineFilterTarget(props.entityVertical, props.entityHorizontal, props.relationName); - if (state.filterTarget !== '') - updateAttributeNameOptions(); + if (state.filterTarget !== '') updateAttributeNameOptions(); }, [props]); // @@ -367,62 +341,37 @@ export const PaohvisFilterComponent = (props: PaohvisFilterProps) => { ]; return ( - <div> - <div className={styles.container}> - <p className={styles.title}>PAOHVis filters{props.axis}:</p> - <div className={styles.selectContainer}> - <p className={styles.subtitle}>{state.filterTarget}</p> - <div style={{ padding: 10, display: 'block', width: '100%' }}> - <TextField - select - id="standard-select-entity" - style={{ minWidth: 120, marginRight: 20 }} - label="Attribute" - value={state.attributeNameAndType} - onChange={onChangeAttributeName} - > - {attributeNameMenuItems} - </TextField> - <TextField - select - id="standard-select-relation" - style={{ minWidth: 120, marginRight: 20 }} - label="Relation" - value={state.predicate} - onChange={onChangePredicate} - > - {predicateTypeList} - </TextField> - <TextField - id="standard-select-relation" - style={{ minWidth: 120, marginRight: 20 }} - label="Value" - value={state.compareValue} - onChange={onChangeCompareValue} - > - { } - </TextField> - - <div style={{ height: 40, paddingTop: 10, marginBottom: 10 }}> - <Button - variant="contained" - disabled={!state.isFilterButtonEnabled} - onClick={onClickFilterPaohvisButton} - > - <span style={{ fontWeight: 'bold' }}>Apply filter</span> - </Button> - </div> - <div style={{ height: 40, marginBottom: 20 }}> - <Button - variant="contained" - onClick={onClickResetFilter} - > - <span style={{ fontWeight: 'bold' }}>Reset filter</span> - </Button> - </div> + <div className={styles.container}> + <p className={styles.title}>PAOHVis filters{props.axis}:</p> + <div className={styles.selectContainer}> + <p className={styles.subtitle}>{state.filterTarget}</p> + <div className={styles.selectGroup}> + <TextField + select + id="standard-select-entity" + label="Attribute" + value={state.attributeNameAndType} + onChange={onChangeAttributeName} + > + {attributeNameMenuItems} + </TextField> + <TextField select id="standard-select-relation" label="Relation" value={state.predicate} onChange={onChangePredicate}> + {predicateTypeList} + </TextField> + <TextField id="standard-select-relation" label="Value" value={state.compareValue} onChange={onChangeCompareValue}> + {} + </TextField> + + <div className={styles.selectButtonGroup}> + <Button variant="contained" disabled={!state.isFilterButtonEnabled} onClick={onClickFilterPaohvisButton}> + <span style={{ fontWeight: 'bold' }}>Apply filter</span> + </Button> + <Button variant="contained" onClick={onClickResetFilter}> + <span style={{ fontWeight: 'bold' }}>Reset filter</span> + </Button> </div> </div> </div> </div> ); -} +}; diff --git a/libs/shared/lib/vis/paohvis/components/RowLabelColumn.tsx b/libs/shared/lib/vis/paohvis/components/RowLabelColumn.tsx index 82ca7b58b..29322ea96 100644 --- a/libs/shared/lib/vis/paohvis/components/RowLabelColumn.tsx +++ b/libs/shared/lib/vis/paohvis/components/RowLabelColumn.tsx @@ -36,7 +36,7 @@ export const RowLabelColumn = (props: RowLabelColumnProps) => { )); return <g>{titleRows}</g>; -} +}; type RowLabelProps = { onMouseEnter: (row: number) => void; @@ -63,20 +63,12 @@ const RowLabel = (props: RowLabelProps) => { <g ref={ref} className={'row-' + props.index} - transform={ - 'translate(0,' + - (props.yOffset + props.rowHeight + props.index * props.rowHeight) + - ')' - } + transform={'translate(0,' + (props.yOffset + props.rowHeight + props.index * props.rowHeight) + ')'} > - <rect - width={props.width} - height={props.rowHeight} - fill={props.index % 2 === 0 ? '#e6e6e6' : '#f5f5f5'} - ></rect> + <rect width={props.width} height={props.rowHeight} fill={props.index % 2 === 0 ? '#e6e6e6' : '#f5f5f5'}></rect> <text x="10" y={props.rowHeight / 2} dy=".35em" style={{ fontWeight: 600 }}> {props.title} </text> </g> ); -} +}; diff --git a/libs/shared/lib/vis/paohvis/components/Tooltip.tsx b/libs/shared/lib/vis/paohvis/components/Tooltip.tsx index faf885025..2ee467411 100644 --- a/libs/shared/lib/vis/paohvis/components/Tooltip.tsx +++ b/libs/shared/lib/vis/paohvis/components/Tooltip.tsx @@ -13,18 +13,18 @@ import React from 'react'; import './Tooltip.scss'; import { pointer, select } from 'd3'; -export default class Tooltip extends React.Component { - onMouseOver(e: any) { +export const Tooltip = () => { + function onMouseOver(e: any) { select('.tooltip') .style('top', pointer(e)[1]) .style('left', pointer(e)[0] + 5); } - render() { - return ( - <div style={{ position: 'absolute' }} className="tooltip"> - {' '} - </div> - ); - } -} + return ( + <div style={{ position: 'absolute' }} className="tooltip"> + {' '} + </div> + ); +}; + +export default Tooltip; diff --git a/libs/shared/lib/vis/paohvis/models/FilterPredicates.tsx b/libs/shared/lib/vis/paohvis/models/FilterPredicates.tsx index 71c4bf8cf..bee9f0f36 100644 --- a/libs/shared/lib/vis/paohvis/models/FilterPredicates.tsx +++ b/libs/shared/lib/vis/paohvis/models/FilterPredicates.tsx @@ -4,30 +4,30 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ - type TextPredicate = (a1: string, a2: string) => boolean; - type NumberPredicate = (a1: number, a2: number) => boolean; - type BoolPredicate = (a1: boolean, a2: boolean) => boolean; - - /** Predicates to be used on attributes that are strings. */ - export const textPredicates: Record<string, TextPredicate> = { - '==': (a1, a2) => a1 == a2, - '!=': (a1, a2) => a1 != a2, - contains: (a1, a2) => a1.includes(a2), - excludes: (a1, a2) => !a1.includes(a2), - }; - - /** Predicates to be used on attributes that are numbers. */ - export const numberPredicates: Record<string, NumberPredicate> = { - '==': (a1, a2) => a1 == a2, - '!=': (a1, a2) => a1 != a2, - '>=': (a1, a2) => a1 >= a2, - '>': (a1, a2) => a1 > a2, - '<=': (a1, a2) => a1 <= a2, - '<': (a1, a2) => a1 < a2, - }; - - /** Predicates to be used on attributes that are booleans. */ - export const boolPredicates: Record<string, BoolPredicate> = { - '==': (a1, a2) => a1 == a2, - '!=': (a1, a2) => a1 != a2, - }; \ No newline at end of file +type TextPredicate = (a1: string, a2: string) => boolean; +type NumberPredicate = (a1: number, a2: number) => boolean; +type BoolPredicate = (a1: boolean, a2: boolean) => boolean; + +/** Predicates to be used on attributes that are strings. */ +export const textPredicates: Record<string, TextPredicate> = { + '==': (a1, a2) => a1 == a2, + '!=': (a1, a2) => a1 != a2, + contains: (a1, a2) => a1.includes(a2), + excludes: (a1, a2) => !a1.includes(a2), +}; + +/** Predicates to be used on attributes that are numbers. */ +export const numberPredicates: Record<string, NumberPredicate> = { + '==': (a1, a2) => a1 == a2, + '!=': (a1, a2) => a1 != a2, + '>=': (a1, a2) => a1 >= a2, + '>': (a1, a2) => a1 > a2, + '<=': (a1, a2) => a1 <= a2, + '<': (a1, a2) => a1 < a2, +}; + +/** Predicates to be used on attributes that are booleans. */ +export const boolPredicates: Record<string, BoolPredicate> = { + '==': (a1, a2) => a1 == a2, + '!=': (a1, a2) => a1 != a2, +}; diff --git a/libs/shared/lib/vis/paohvis/models/PaohvisHolder.tsx b/libs/shared/lib/vis/paohvis/models/PaohvisHolder.tsx index fe857ef4d..4aa15a433 100644 --- a/libs/shared/lib/vis/paohvis/models/PaohvisHolder.tsx +++ b/libs/shared/lib/vis/paohvis/models/PaohvisHolder.tsx @@ -52,8 +52,6 @@ export default class PaohvisHolder { /** Notifies to all the listeners that a Paohvis table was made. */ private notifyListeners(): void { - this.paohvisListeners.forEach((listener) => - listener.onTableMade(this.entityVertical, this.entityHorizontal, this.relationName), - ); + this.paohvisListeners.forEach((listener) => listener.onTableMade(this.entityVertical, this.entityHorizontal, this.relationName)); } } diff --git a/libs/shared/lib/vis/paohvis/paohvis.module.scss b/libs/shared/lib/vis/paohvis/paohvis.module.scss index 009c546e9..b69ef02e3 100644 --- a/libs/shared/lib/vis/paohvis/paohvis.module.scss +++ b/libs/shared/lib/vis/paohvis/paohvis.module.scss @@ -30,8 +30,8 @@ $tableFontWeight: 600; position: relative; .visContainer { height: 100%; - overflow-y: auto; - transform: scale(-1, 1); + margin-right: 1em; + overflow: hidden; /** * this fixes the horizontal scrollbar to the bottom of the visContainer, * the transform of the visContainer and the transform of the child div should both be removed for it to work @@ -41,15 +41,27 @@ $tableFontWeight: 600; * display: flex; */ } + .full { + height: 100%; + // overflow: hidden; + // max-height: fit-content; + // width: 100%; + // max-width: 100vw; + } + .visContainerSvg { + height: calc(100% - 6em); + width: 100%; + overflow: auto; + } text { font-family: $tableFontFamily; font-size: $tableFontSize; font-weight: $tableFontWeight; } - #tableMessage { + .tableMessage { margin: 1em; } - #configPanelMessage { + .configPanelMessage { margin: 1em; } } diff --git a/libs/shared/lib/vis/paohvis/paohvis.module.scss.d.ts b/libs/shared/lib/vis/paohvis/paohvis.module.scss.d.ts index cfe7ee67e..cebc85c3f 100644 --- a/libs/shared/lib/vis/paohvis/paohvis.module.scss.d.ts +++ b/libs/shared/lib/vis/paohvis/paohvis.module.scss.d.ts @@ -5,5 +5,9 @@ declare const classNames: { readonly tableFontWeight: 'tableFontWeight'; readonly container: 'container'; readonly visContainer: 'visContainer'; + readonly full: 'full'; + readonly visContainerSvg: 'visContainerSvg'; + readonly tableMessage: 'tableMessage'; + readonly configPanelMessage: 'configPanelMessage'; }; export = classNames; diff --git a/libs/shared/lib/vis/paohvis/paohvis.stories.tsx b/libs/shared/lib/vis/paohvis/paohvis.stories.tsx index 8799d2ec2..a06fae382 100644 --- a/libs/shared/lib/vis/paohvis/paohvis.stories.tsx +++ b/libs/shared/lib/vis/paohvis/paohvis.stories.tsx @@ -1,39 +1,31 @@ -import { - assignNewGraphQueryResult, - colorPaletteConfigSlice, - graphQueryResultSlice, - schemaSlice, - setSchema, -} from "../../data-access/store"; -import { GraphPolarisThemeProvider } from "../../data-access/theme"; -import { configureStore } from "@reduxjs/toolkit"; -import { Meta, ComponentStory } from "@storybook/react"; -import { Provider } from "react-redux"; +import { assignNewGraphQueryResult, colorPaletteConfigSlice, graphQueryResultSlice, schemaSlice, setSchema } from '../../data-access/store'; +import { GraphPolarisThemeProvider } from '../../data-access/theme'; +import { configureStore } from '@reduxjs/toolkit'; +import { Meta, ComponentStory } from '@storybook/react'; +import { Provider } from 'react-redux'; -import Paohvis from "./paohvis"; -import { SchemaUtils } from "../../schema/schema-utils"; -import { bigMockQueryResults, simpleSchemaRaw, smallFlightsQueryResults } from "../../mock-data"; -import { simpleSchemaAirportRaw } from "../../mock-data/schema/simpleAirportRaw"; +import Paohvis from './paohvis'; +import { SchemaUtils } from '../../schema/schema-utils'; +import { bigMockQueryResults, simpleSchemaRaw, smallFlightsQueryResults } from '../../mock-data'; +import { simpleSchemaAirportRaw } from '../../mock-data/schema/simpleAirportRaw'; const Component: Meta<typeof Paohvis> = { /* 👇 The title prop is optional. * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ - title: "Components/Visualizations/Paohvis", + title: 'Components/Visualizations/Paohvis', component: Paohvis, decorators: [ (story) => ( <div style={{ - width: '80%', - height: '80vh', + width: '83%', + height: '95vh', }} > <Provider store={Mockstore}> - <GraphPolarisThemeProvider> - {story()} - </GraphPolarisThemeProvider> + <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider> </Provider> </div> ), @@ -51,7 +43,7 @@ const Mockstore = configureStore({ export const TestWithData = { args: { loading: false, - rowHeight: 50, + rowHeight: 30, hyperedgeColumnWidth: 50, gapBetweenRanges: 10, }, @@ -66,7 +58,11 @@ export const TestWithData = { ], edges: [ { - name: '12', from: '1', to: '1', collection: '1c', attributes: [{ name: 'a', type: 'string' }], + name: '12', + from: '1', + to: '1', + collection: '1c', + attributes: [{ name: 'a', type: 'string' }], }, ], }); @@ -74,22 +70,25 @@ export const TestWithData = { dispatch(setSchema(schema.export())); dispatch( assignNewGraphQueryResult({ - nodes: [ - { id: '1/a', attributes: { a: 's1' } }, - { id: '1/b1', attributes: { a: 's1' } }, - { id: '1/b2', attributes: { a: 's1' } }, - { id: '1/b3', attributes: { a: 's1' } }, - ], - edges: [ - { id: '1c/z1', from: '1/b1', to: '1/a', attributes: { a: 's1' } }, - // { from: 'b2', to: 'a', attributes: {} }, - // { from: 'b3', to: 'a', attributes: {} }, - { id: '1c/z1', from: '1/a', to: '1/b1', attributes: { a: 's1' } }, - ], + type: 'nodelink', + payload: { + nodes: [ + { id: '1/a', label: 'a', attributes: { a: 's1' } }, + { id: '1/b1', label: 'b1', attributes: { a: 's1' } }, + { id: '1/b2', label: 'b2', attributes: { a: 's1' } }, + { id: '1/b3', label: 'b3', attributes: { a: 's1' } }, + ], + edges: [ + { id: '1c/z1', label: 'z1', from: '1/b1', to: '1/a', attributes: { a: 's1' } }, + // { from: 'b2', to: 'a', attributes: {} }, + // { from: 'b3', to: 'a', attributes: {} }, + { id: '1c/z1', label: 'z1', from: '1/a', to: '1/b1', attributes: { a: 's1' } }, + ], + }, }) - ) - } -} + ); + }, +}; export const TestWithAirportSimple = { args: { @@ -103,28 +102,24 @@ export const TestWithAirportSimple = { const schema = SchemaUtils.schemaBackend2Graphology(simpleSchemaRaw); dispatch(setSchema(schema.export())); - dispatch( - assignNewGraphQueryResult(smallFlightsQueryResults) - ) - } -} + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: smallFlightsQueryResults })); + }, +}; export const TestWithAirport = { args: { loading: false, - rowHeight: 50, - hyperedgeColumnWidth: 50, - gapBetweenRanges: 10, + rowHeight: 30, + hyperedgeColumnWidth: 30, + gapBetweenRanges: 3, }, play: async () => { const dispatch = Mockstore.dispatch; const schema = SchemaUtils.schemaBackend2Graphology(simpleSchemaAirportRaw); dispatch(setSchema(schema.export())); - dispatch( - assignNewGraphQueryResult(bigMockQueryResults) - ) - } -} + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: bigMockQueryResults })); + }, +}; export default Component; diff --git a/libs/shared/lib/vis/paohvis/paohvis.tsx b/libs/shared/lib/vis/paohvis/paohvis.tsx index 3826c893d..4c2ab978d 100644 --- a/libs/shared/lib/vis/paohvis/paohvis.tsx +++ b/libs/shared/lib/vis/paohvis/paohvis.tsx @@ -1,13 +1,23 @@ - import { useEffect, useRef, useState } from 'react'; import styles from './paohvis.module.scss'; -import { Attribute, Relation, AttributeOrigin, EntitiesFromSchema, FilterInfo, FilterType, NodeOrder, PaohvisData, PaohvisNodeOrder, RelationsFromSchema, ValueType } from './Types'; +import { + Attribute, + Relation, + AttributeOrigin, + EntitiesFromSchema, + FilterInfo, + FilterType, + NodeOrder, + PaohvisData, + PaohvisNodeOrder, + RelationsFromSchema, + ValueType, +} from './Types'; import { useImmer } from 'use-immer'; import { getWidthOfText } from '../../schema/schema-utils'; import { useGraphQueryResult, useSchemaGraph } from '../../data-access'; import { isNodeLinkResult } from '../shared/ResultNodeLinkParserUseCase'; -import { isSchemaResult } from '../shared/SchemaResultType'; import { calculateAttributesAndRelations, calculateAttributesFromRelation } from './utils/utils'; import VisConfigPanelComponent from '../shared/VisConfigPanel/VisConfigPanel'; import { PaohvisFilterComponent } from './components/PaohvisFilterComponent'; @@ -32,15 +42,18 @@ type PaohvisViewModelState = { entityHorizontal: string; chosenRelation: string; isEntityVerticalEqualToRelationFrom: boolean; - nodeOrder: { orderBy: NodeOrder, isReverseOrder: boolean }; - paohvisFilters: { nodeFilters: FilterInfo[], edgeFilters: FilterInfo[] }; + nodeOrder: { orderBy: NodeOrder; isReverseOrder: boolean }; + paohvisFilters: { nodeFilters: FilterInfo[]; edgeFilters: FilterInfo[] }; axisInfo: { - selectedAttribute: { name: string, type: ValueType, origin: AttributeOrigin }, - relation: { collection: string, from: string, to: string }, - isYAxisEntityEqualToRelationFrom: boolean, + selectedAttribute: { + name: string; + type: ValueType; + origin: AttributeOrigin; + }; + relation: { collection: string; from: string; to: string }; + isYAxisEntityEqualToRelationFrom: boolean; }; - -} +}; type Props = { rowHeight: number; @@ -54,7 +67,11 @@ export const PaohVis = (props: Props) => { const graphQueryResult = useGraphQueryResult(); const schema = useSchemaGraph(); - const [data, setData] = useState<PaohvisData>({ rowLabels: [], hyperEdgeRanges: [], maxRowLabelWidth: 0 },); + const [data, setData] = useState<PaohvisData>({ + rowLabels: [], + hyperEdgeRanges: [], + maxRowLabelWidth: 0, + }); const [viewModel, setViewModel] = useImmer<PaohvisViewModelState>({ rowHeight: 30, hyperedgeColumnWidth: 20, @@ -62,24 +79,32 @@ export const PaohVis = (props: Props) => { hyperedgesOnRow: [], allHyperEdges: [], - entitiesFromSchema: { entityNames: [], attributesPerEntity: {}, relationsPerEntity: {} }, + entitiesFromSchema: { + entityNames: [], + attributesPerEntity: {}, + relationsPerEntity: {}, + }, relationsFromSchema: { - relationCollection: [], relationNames: {}, attributesPerRelation: {}, + relationCollection: [], + relationNames: {}, + attributesPerRelation: {}, }, entityVertical: '', entityHorizontal: '', chosenRelation: '', isEntityVerticalEqualToRelationFrom: true, - nodeOrder: { orderBy: NodeOrder.degree, isReverseOrder: false, }, + nodeOrder: { orderBy: NodeOrder.degree, isReverseOrder: false }, paohvisFilters: { nodeFilters: [], edgeFilters: [] }, axisInfo: { selectedAttribute: { - name: '', type: ValueType.noAttribute, origin: AttributeOrigin.noAttribute, + name: '', + type: ValueType.noAttribute, + origin: AttributeOrigin.noAttribute, }, relation: { collection: '', from: '', to: '' }, isYAxisEntityEqualToRelationFrom: true, - } + }, }); // const [state, setState] = useState({ @@ -89,8 +114,6 @@ export const PaohVis = (props: Props) => { // isEntityFromRelationFrom: true, // }); - - // // Methods // @@ -108,7 +131,7 @@ export const PaohVis = (props: Props) => { entityOrRelationType: string, attribute: string, predicate: string, - compareValue: string, + compareValue: string ): void { const attributeName: string = attribute.split(':')[0]; const attributeType: string = attribute.split(':')[1]; @@ -153,7 +176,7 @@ export const PaohVis = (props: Props) => { return draft; }); } - }; + } /** * Resets the current chosen filter. @@ -165,7 +188,7 @@ export const PaohVis = (props: Props) => { else draft.paohvisFilters.edgeFilters.pop(); return draft; }); - }; + } /** * SHOULD NOT BE HERE: move to tooltip @@ -176,7 +199,7 @@ export const PaohVis = (props: Props) => { select('.tooltip') .style('top', pointer(e)[1] - 25 + 'px') .style('left', pointer(e)[0] + 5 + 'px'); - }; + } /** * Handles the visual changes when you enter a certain hyperEdge on hovering. @@ -187,7 +210,7 @@ export const PaohVis = (props: Props) => { highlightAndFadeHyperEdges([colIndex]); highlightAndFadeRows(new Set(viewModel.allHyperEdges[colIndex])); showColName(nameToShow); - }; + } /** * Handles the visual changes when you leave a certain hyperEdge on hovering. @@ -197,8 +220,7 @@ export const PaohVis = (props: Props) => { unHighlightAndUnFadeHyperEdges([colIndex]); unHighlightAndUnFadeRows(new Set(viewModel.allHyperEdges[colIndex])); hideColName(); - }; - + } /** * This makes sure that the correct rows are highlighted and the correct rows are faded. @@ -232,8 +254,7 @@ export const PaohVis = (props: Props) => { hyperEdges.forEach((hyperEdge) => highlightHyperedge(hyperEdge)); const colsToFade = []; - for (let i = 0; i < viewModel.allHyperEdges.length; i++) - if (!hyperEdges.includes(i)) colsToFade.push(i); + for (let i = 0; i < viewModel.allHyperEdges.length; i++) if (!hyperEdges.includes(i)) colsToFade.push(i); colsToFade.forEach((col) => fadeHyperedge(col)); } @@ -245,8 +266,7 @@ export const PaohVis = (props: Props) => { hyperEdges.forEach((hyperEdge) => unHighlightHyperedge(hyperEdge)); const colsToUnFade = []; - for (let i = 0; i < viewModel.allHyperEdges.length; i++) - if (!hyperEdges.includes(i)) colsToUnFade.push(i); + for (let i = 0; i < viewModel.allHyperEdges.length; i++) if (!hyperEdges.includes(i)) colsToUnFade.push(i); colsToUnFade.forEach((col) => unFadeHyperedge(col)); } @@ -330,32 +350,31 @@ export const PaohVis = (props: Props) => { */ function showColName(nameToShow: string): void { select('.tooltip').text(nameToShow).style('visibility', 'visible'); - }; + } /** This hides the name when leaving a hyperEdge. */ function hideColName(): void { select('.tooltip').style('visibility', 'hidden'); - }; - + } /** - * Makes the new PAOHvis visualisation. - * @param {string} entityVertical This is the name of the vertical entity (so on the left). - * @param {string} entityHorizontal This is the name of the horizontal entity (so at the top). - * @param {string} relationName This is the (collection)-name of the relation. - * @param {boolean} isEntityVerticalEqualToRelationFrom Tells if the vertical entity is the from or to of the relation. - * @param {Attribute} chosenAttribute This is the attribute on which the PAOHvis must be grouped by. - * @param {PaohvisNodeOrder} nodeOrder Defines the sorting order of the PAOHvis visualisation. - */ + * Makes the new PAOHvis visualisation. + * @param {string} entityVertical This is the name of the vertical entity (so on the left). + * @param {string} entityHorizontal This is the name of the horizontal entity (so at the top). + * @param {string} relationName This is the (collection)-name of the relation. + * @param {boolean} isEntityVerticalEqualToRelationFrom Tells if the vertical entity is the from or to of the relation. + * @param {Attribute} chosenAttribute This is the attribute on which the PAOHvis must be grouped by. + * @param {PaohvisNodeOrder} nodeOrder Defines the sorting order of the PAOHvis visualisation. + */ function onClickMakeButton( entityVertical: string, entityHorizontal: string, relationName: string, isEntityVerticalEqualToRelationFrom: boolean, chosenAttribute: Attribute, - nodeOrder: PaohvisNodeOrder, + nodeOrder: PaohvisNodeOrder ): void { - setViewModel(draft => { + setViewModel((draft) => { draft.entityVertical = entityVertical; draft.entityHorizontal = entityHorizontal; draft.chosenRelation = relationName; @@ -365,7 +384,7 @@ export const PaohVis = (props: Props) => { return draft; }); setAxisInfo(relationName, isEntityVerticalEqualToRelationFrom, chosenAttribute); - }; + } /** * This method parses data, makes a new Paohvis Table and lets the view re-render. @@ -373,23 +392,18 @@ export const PaohVis = (props: Props) => { function makePaohvisTable() { // set new data const newData = new ToPaohvisDataParserUseCase(graphQueryResult).parseQueryResult(viewModel.axisInfo, viewModel.nodeOrder); - console.log(newData); + console.log(newData, graphQueryResult, viewModel); setData(newData); } - /** * This method makes and sets a new PaohvisAxisInfo. * @param relationName is the relation that will be used in the Paohvis table. * @param isEntityVerticalEqualToRelationFrom is true when the entity on the y-axis belongs to the 'from' part of the relation. * @param chosenAttribute is the chosen attribute that will be used to divide the HyperEdges into HyperEdgeRanges. */ - function setAxisInfo( - relationName: string, - isEntityVerticalEqualToRelationFrom: boolean, - chosenAttribute: Attribute, - ): void { + function setAxisInfo(relationName: string, isEntityVerticalEqualToRelationFrom: boolean, chosenAttribute: Attribute): void { const namesOfEntityTypes = viewModel.relationsFromSchema.relationNames[relationName].split(':'); const relation: Relation = { collection: relationName, @@ -398,7 +412,7 @@ export const PaohVis = (props: Props) => { }; // set axisInfo - setViewModel(draft => { + setViewModel((draft) => { draft.axisInfo = { selectedAttribute: chosenAttribute, relation: relation, @@ -425,7 +439,7 @@ export const PaohVis = (props: Props) => { highlightAndFadeRows(rowsToHighlight); highlightAndFadeHyperEdges(colsToHighlight); - }; + } /** * Handles the visual changes when you leave a certain row on hovering. @@ -443,41 +457,35 @@ export const PaohVis = (props: Props) => { }); unHighlightAndUnFadeRows(rowsToUnHighlight); unHighlightAndUnFadeHyperEdges(colsToUnHighlight); - }; + } // // Reactivity // useEffect(() => { - // if (isSchemaResult(schema)) { - setViewModel(draft => { + setViewModel((draft) => { // When a schema is received, extract the entity names, and attributes per data type draft.entitiesFromSchema = calculateAttributesAndRelations(schema); draft.relationsFromSchema = calculateAttributesFromRelation(schema); return draft; }); - // } else { - // console.error('Invalid schema!') - // } }, [schema]); - /** This method filters and makes a new Paohvis table. */ useEffect(() => { if (isNodeLinkResult(graphQueryResult)) { makePaohvisTable(); } else { - console.error('Invalid query result!') + console.error('Invalid query result!'); } }, [viewModel.paohvisFilters, graphQueryResult, viewModel.axisInfo, viewModel.nodeOrder]); - useEffect(() => { /** * Recalculates the hyperEdges and hyperEdgeRanges for the state. */ - setViewModel(draft => { + setViewModel((draft) => { // Create connecteHyperedgesInColumns, to quickly lookup which hyperedges are on a row draft.allHyperEdges = []; @@ -508,12 +516,7 @@ export const PaohVis = (props: Props) => { //calculate yOffset let maxColWidth = 0; hyperEdgeRanges.forEach((hyperEdgeRange) => { - const textLength = getWidthOfText( - hyperEdgeRange.rangeText, - styles.tableFontFamily, - styles.tableFontSize, - styles.tableFontWeight, - ); + const textLength = getWidthOfText(hyperEdgeRange.rangeText, styles.tableFontFamily, styles.tableFontSize, styles.tableFontWeight); if (textLength > maxColWidth) maxColWidth = textLength; }); const columnLabelAngleInRadians = Math.PI / 6; @@ -527,14 +530,8 @@ export const PaohVis = (props: Props) => { hyperEdgeRanges.forEach((hyperEdgeRange) => { const columnLabelWidth = Math.cos(columnLabelAngleInRadians) * - getWidthOfText( - hyperEdgeRange.rangeText, - styles.tableFontFamily, - styles.tableFontSize, - styles.tableFontWeight, - ); - const columnWidth = - hyperEdgeRange.hyperEdges.length * hyperedgeColumnWidth + props.gapBetweenRanges * 3; + getWidthOfText(hyperEdgeRange.rangeText, styles.tableFontFamily, styles.tableFontSize, styles.tableFontWeight); + const columnWidth = hyperEdgeRange.hyperEdges.length * hyperedgeColumnWidth + props.gapBetweenRanges * 3; tableWidth += columnWidth; @@ -543,8 +540,7 @@ export const PaohVis = (props: Props) => { if (currentTableWidthWithLabel > tableWidthWithExtraColumnLabelWidth) tableWidthWithExtraColumnLabelWidth = currentTableWidthWithLabel; } - if (tableWidth > tableWidthWithExtraColumnLabelWidth) - tableWidthWithExtraColumnLabelWidth = tableWidth; + if (tableWidth > tableWidthWithExtraColumnLabelWidth) tableWidthWithExtraColumnLabelWidth = tableWidth; }); tableWidthWithExtraColumnLabelWidth += rowLabelColumnWidth + expandButtonWidth + margin; @@ -567,7 +563,7 @@ export const PaohVis = (props: Props) => { gapBetweenRanges={props.gapBetweenRanges} onMouseEnter={onMouseEnterHyperEdge} onMouseLeave={onMouseLeaveHyperEdge} - />, + /> ); colOffset += hyperEdgeRange.hyperEdges.length; }); @@ -576,44 +572,39 @@ export const PaohVis = (props: Props) => { let tableMessage = <span></span>; let configPanelMessage = <div></div>; if (data.rowLabels.length === 0) { - tableMessage = ( - <div id={styles.tableMessage}> Please choose a valid PAOHvis configuration </div> - ); - configPanelMessage = ( - <div id={styles.configPanelMessage}> Please make a PAOHvis table first </div> - ); + tableMessage = <div id={styles.tableMessage}> Please choose a valid PAOHvis configuration </div>; + configPanelMessage = <div id={styles.configPanelMessage}> Please make a PAOHvis table first </div>; } // returns the whole PAOHvis visualisation panel return ( <div className={styles.container}> <div className={styles.visContainer}> - <div style={{ transform: 'scale(-1,1)' }}> - <div - style={{ width: '100%', height: '100%', overflow: 'auto' }} - onMouseMove={onMouseMoveToolTip} - > + <div className={styles.full}> + <div className={styles.full} onMouseMove={onMouseMoveToolTip}> <MakePaohvisMenu // render the MakePAOHvisMenu makePaohvis={onClickMakeButton} /> {tableMessage} - <svg - ref={svgRef} - style={{ - width: tableWidthWithExtraColumnLabelWidth, - height: yOffset + (data.rowLabels.length + 1) * props.rowHeight, - }} - > - <RowLabelColumn // render the PAOHvis itself - onMouseEnter={onMouseEnterRow} - onMouseLeave={onMouseLeaveRow} - titles={data.rowLabels} - width={rowLabelColumnWidth} - rowHeight={viewModel.rowHeight} - yOffset={yOffset} - /> - {hyperEdgeRangeColumns} - </svg> + <div className={styles.visContainerSvg}> + <svg + ref={svgRef} + style={{ + width: tableWidthWithExtraColumnLabelWidth, + height: yOffset + (data.rowLabels.length + 1) * props.rowHeight, + }} + > + <RowLabelColumn // render the PAOHvis itself + onMouseEnter={onMouseEnterRow} + onMouseLeave={onMouseLeaveRow} + titles={data.rowLabels} + width={rowLabelColumnWidth} + rowHeight={props.rowHeight} // viewModel.rowHeight? + yOffset={yOffset} + /> + {hyperEdgeRangeColumns} + </svg> + </div> <Tooltip /> </div> </div> @@ -649,5 +640,4 @@ export const PaohVis = (props: Props) => { ); }; - -export default PaohVis; \ No newline at end of file +export default PaohVis; diff --git a/libs/shared/lib/vis/paohvis/utils/AttributesFilterUseCase.tsx b/libs/shared/lib/vis/paohvis/utils/AttributesFilterUseCase.tsx index 725d7a88b..07f5fae8b 100644 --- a/libs/shared/lib/vis/paohvis/utils/AttributesFilterUseCase.tsx +++ b/libs/shared/lib/vis/paohvis/utils/AttributesFilterUseCase.tsx @@ -5,34 +5,24 @@ */ import { FilterInfo, PaohvisFilters } from '../Types'; -import { - AxisType, - isNotInGroup, -} from '../../shared/ResultNodeLinkParserUseCase'; +import { AxisType, isNotInGroup } from '../../shared/ResultNodeLinkParserUseCase'; import { boolPredicates, numberPredicates, textPredicates } from '../models/FilterPredicates'; import { Edge, GraphQueryResult, Node } from '@graphpolaris/shared/lib/data-access'; /** This is used to filter the data for Paohvis. */ export default class AttributeFilterUsecase { /** Applies all filters to the query result. */ - public static applyFilters( - queryResult: GraphQueryResult, - paohvisFilters: PaohvisFilters, - ): GraphQueryResult { + public static applyFilters(queryResult: GraphQueryResult, paohvisFilters: PaohvisFilters): GraphQueryResult { //apply the filters to the nodes let filteredNodes = queryResult.nodes; - paohvisFilters.nodeFilters.forEach( - (filter) => (filteredNodes = this.filterAxis(filteredNodes, filter)), - ); + paohvisFilters.nodeFilters.forEach((filter) => (filteredNodes = this.filterAxis(filteredNodes, filter))); //filter out the unused edges const nodeIds = getIds(filteredNodes); let filteredEdges = filterUnusedEdges(nodeIds, queryResult.edges); //apply the filters to the edges - paohvisFilters.edgeFilters.forEach( - (filter) => (filteredEdges = this.filterAxis(filteredEdges, filter)), - ); + paohvisFilters.edgeFilters.forEach((filter) => (filteredEdges = this.filterAxis(filteredEdges, filter))); //filter out unused nodes filteredNodes = filterUnusedNodes(filteredNodes, filteredEdges); @@ -65,9 +55,7 @@ function filterBoolAttributes<T extends Node | Edge>(axisNodesOrEdges: T[], filt const resultNodesOrEdges = axisNodesOrEdges.filter((nodeOrEdge) => { const currentAttribute = nodeOrEdge.attributes[filter.attributeName] as boolean; - return ( - isNotInGroup(nodeOrEdge, filter.targetGroup) || predicate(currentAttribute, filter.value) - ); + return isNotInGroup(nodeOrEdge, filter.targetGroup) || predicate(currentAttribute, filter.value); }); return resultNodesOrEdges; @@ -80,9 +68,7 @@ function filterNumberAttributes<T extends Node | Edge>(axisNodesOrEdges: T[], fi const resultNodesOrEdges = axisNodesOrEdges.filter((nodeOrEdge) => { const currentAttribute = nodeOrEdge.attributes[filter.attributeName] as number; - return ( - isNotInGroup(nodeOrEdge, filter.targetGroup) || predicate(currentAttribute, filter.value) - ); + return isNotInGroup(nodeOrEdge, filter.targetGroup) || predicate(currentAttribute, filter.value); }); return resultNodesOrEdges; @@ -95,9 +81,7 @@ function filterTextAttributes<T extends Node | Edge>(axisNodesOrEdges: T[], filt const resultNodesOrEdges = axisNodesOrEdges.filter((nodeOrEdge) => { const currentAttribute = nodeOrEdge.attributes[filter.attributeName] as string; - return ( - isNotInGroup(nodeOrEdge, filter.targetGroup) || predicate(currentAttribute, filter.value) - ); + return isNotInGroup(nodeOrEdge, filter.targetGroup) || predicate(currentAttribute, filter.value); }); return resultNodesOrEdges; @@ -118,9 +102,7 @@ export function getIds(nodesOrEdges: AxisType[]): string[] { */ export function filterUnusedEdges(ids: string[], edges: Edge[]): Edge[] { const filteredEdges: Edge[] = []; - edges.forEach( - (edge) => ids.includes(edge.from) && ids.includes(edge.to) && filteredEdges.push(edge), - ); + edges.forEach((edge) => ids.includes(edge.from) && ids.includes(edge.to) && filteredEdges.push(edge)); return filteredEdges; } diff --git a/libs/shared/lib/vis/paohvis/utils/CalcEntitiesFromQueryResult.tsx b/libs/shared/lib/vis/paohvis/utils/CalcEntitiesFromQueryResult.tsx index 3474abc6c..b9a274bd9 100644 --- a/libs/shared/lib/vis/paohvis/utils/CalcEntitiesFromQueryResult.tsx +++ b/libs/shared/lib/vis/paohvis/utils/CalcEntitiesFromQueryResult.tsx @@ -4,9 +4,8 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { GraphQueryResult } from "@graphpolaris/shared/lib/data-access"; -import { getGroupName } from "../../shared/ResultNodeLinkParserUseCase"; - +import { GraphQueryResult } from '@graphpolaris/shared/lib/data-access'; +import { getGroupName } from '../../shared/ResultNodeLinkParserUseCase'; /** * This calculates all entities from the query result. diff --git a/libs/shared/lib/vis/paohvis/utils/CalcEntityAttrAndRelNamesFromSchemaUseCase.tsx b/libs/shared/lib/vis/paohvis/utils/CalcEntityAttrAndRelNamesFromSchemaUseCase.tsx index 834ec22a6..b369cfa30 100644 --- a/libs/shared/lib/vis/paohvis/utils/CalcEntityAttrAndRelNamesFromSchemaUseCase.tsx +++ b/libs/shared/lib/vis/paohvis/utils/CalcEntityAttrAndRelNamesFromSchemaUseCase.tsx @@ -4,11 +4,7 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ import { SchemaGraph, SchemaGraphologyEdge } from '@graphpolaris/shared/lib/schema'; -import { - AttributeNames, - EntitiesFromSchema, - RelationsFromSchema, -} from '../Types'; +import { AttributeNames, EntitiesFromSchema, RelationsFromSchema } from '../Types'; /** Use case for retrieving entity names, relation names and attribute names from a schema result. */ export default class CalcEntityAttrAndRelNamesFromSchemaUseCase { @@ -19,11 +15,11 @@ export default class CalcEntityAttrAndRelNamesFromSchemaUseCase { * @returns {EntitiesFromSchema} All entity names, and relation names and attribute names per entity. */ public static calculateAttributesAndRelations(schemaResult: SchemaGraph): EntitiesFromSchema { - const attributesPerEntity: Record<string, AttributeNames> = this.calculateAttributes(schemaResult,); + const attributesPerEntity: Record<string, AttributeNames> = this.calculateAttributes(schemaResult); const relationsPerEntity: Record<string, string[]> = this.calculateRelations(schemaResult); return { - entityNames: schemaResult.nodes.filter(node => (node?.attributes?.name !== undefined)).map((node) => node.attributes!.name), + entityNames: schemaResult.nodes.filter((node) => node?.attributes?.name !== undefined).map((node) => node.attributes!.name), attributesPerEntity, relationsPerEntity, }; diff --git a/libs/shared/lib/vis/paohvis/utils/SortUseCase.tsx b/libs/shared/lib/vis/paohvis/utils/SortUseCase.tsx index 818384e13..2976b7ba5 100644 --- a/libs/shared/lib/vis/paohvis/utils/SortUseCase.tsx +++ b/libs/shared/lib/vis/paohvis/utils/SortUseCase.tsx @@ -4,7 +4,7 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { HyperEdgeI, HyperEdgeRange, NodeOrder, PaohvisNodeOrder, ValueType } from "../Types"; +import { HyperEdgeI, HyperEdgeRange, NodeOrder, PaohvisNodeOrder, ValueType } from '../Types'; import { Node } from '@graphpolaris/shared/lib/data-access'; /** @@ -28,10 +28,7 @@ export default class SortUseCase { * @param hyperEdgeRanges that should be sorted. * @param type of the HyperEdgeRange labels. */ - private static sortHyperEdgeRangesByLabels( - hyperEdgeRanges: HyperEdgeRange[], - type: ValueType, - ): void { + private static sortHyperEdgeRangesByLabels(hyperEdgeRanges: HyperEdgeRange[], type: ValueType): void { //sort all hyperedgeranges text if (type == ValueType.number) { //from lowest to highest @@ -48,7 +45,7 @@ export default class SortUseCase { */ private static sortHyperEdgeIndices(hyperEdgeRanges: HyperEdgeRange[]): void { hyperEdgeRanges.forEach((hyperEdgeRange) => - hyperEdgeRange.hyperEdges.forEach((hyperEdge) => hyperEdge.indices.sort((n1, n2) => n1 - n2)), + hyperEdgeRange.hyperEdges.forEach((hyperEdge) => hyperEdge.indices.sort((n1, n2) => n1 - n2)) ); } @@ -58,11 +55,7 @@ export default class SortUseCase { * @param nodes are the nodes that will be sorted * @param hyperEdgeDegree is the dictionary where you can find how many edges connected from the node. */ - public static sortNodes( - nodeOrder: PaohvisNodeOrder, - nodes: Node[], - hyperEdgeDegree: Record<string, number>, - ): void { + public static sortNodes(nodeOrder: PaohvisNodeOrder, nodes: Node[], hyperEdgeDegree: Record<string, number>): void { switch (nodeOrder.orderBy) { //sort nodes on their degree (# number of hyperedges) (entities with most hyperedges first) case NodeOrder.degree: diff --git a/libs/shared/lib/vis/paohvis/utils/ToPaohvisDataParserUsecase.tsx b/libs/shared/lib/vis/paohvis/utils/ToPaohvisDataParserUsecase.tsx index 454ffe6c0..de75b4d88 100644 --- a/libs/shared/lib/vis/paohvis/utils/ToPaohvisDataParserUsecase.tsx +++ b/libs/shared/lib/vis/paohvis/utils/ToPaohvisDataParserUsecase.tsx @@ -4,14 +4,25 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { AxisType, getGroupName } from '../../shared/ResultNodeLinkParserUseCase' +import { AxisType, getGroupName } from '../../shared/ResultNodeLinkParserUseCase'; import { Edge, GraphQueryResult, Node } from '@graphpolaris/shared/lib/data-access'; -import { AttributeOrigin, HyperEdgeI, HyperEdgeRange, PaohvisAxisInfo, PaohvisData, PaohvisFilters, PaohvisNodeInfo, PaohvisNodeOrder, Relation, ValueType } from '../Types'; +import { + AttributeOrigin, + HyperEdgeI, + HyperEdgeRange, + PaohvisAxisInfo, + PaohvisData, + PaohvisFilters, + PaohvisNodeInfo, + PaohvisNodeOrder, + Relation, + ValueType, +} from '../Types'; import AttributeFilterUsecase, { filterUnusedEdges, getIds } from './AttributesFilterUseCase'; import SortUseCase from './SortUseCase'; import { getWidthOfText, uniq } from './utils'; -import style from '../Paohvis.module.scss'; +import style from '../paohvis.module.scss'; type Index = number; @@ -39,27 +50,24 @@ export default class ToPaohvisDataParserUseCase { public parseQueryResult(axisInfo: PaohvisAxisInfo, nodeOrder: PaohvisNodeOrder): PaohvisData { this.setAxesNodeGroups(axisInfo); - const filteredData = AttributeFilterUsecase.applyFilters( - this.queryResult, - this.paohvisFilters, - ); + const filteredData = AttributeFilterUsecase.applyFilters(this.queryResult, this.paohvisFilters); // filter unnecessary node groups and relations const nodes = filteredData.nodes.filter((node) => { const nodeType = getGroupName(node); return nodeType === this.xAxisNodeGroup || nodeType === this.yAxisNodeGroup; }); - const edges = filteredData.edges.filter( - (edge) => getGroupName(edge) === axisInfo.relation.collection, - ); + + console.log(axisInfo.relation.collection, filteredData.edges); + + const edges = filteredData.edges.filter((edge) => edge.label === axisInfo.relation.collection); // get hyperEdgeDegree - const hyperEdgeDegree: Record<string, number> = - ToPaohvisDataParserUseCase.GetHyperEdgeDegreeDict( - nodes, - edges, - axisInfo.isYAxisEntityEqualToRelationFrom, - ); + const hyperEdgeDegree: Record<string, number> = ToPaohvisDataParserUseCase.GetHyperEdgeDegreeDict( + nodes, + edges, + axisInfo.isYAxisEntityEqualToRelationFrom + ); //parse nodes const rowInfo: PaohvisNodeInfo = ToPaohvisDataParserUseCase.parseNodes( @@ -67,18 +75,13 @@ export default class ToPaohvisDataParserUseCase { hyperEdgeDegree, nodeOrder, this.yAxisNodeGroup, - this.xAxisNodeGroup, + this.xAxisNodeGroup ); const maxLabelWidth = calcMaxRowLabelWidth(rowInfo.rowLabels); //parse hyperEdges const filteredEdges = filterUnusedEdges(getIds(nodes), edges); - const resultHyperEdgeRanges = ToPaohvisDataParserUseCase.parseHyperEdgeRanges( - nodes, - filteredEdges, - axisInfo, - rowInfo, - ); + const resultHyperEdgeRanges = ToPaohvisDataParserUseCase.parseHyperEdgeRanges(nodes, filteredEdges, axisInfo, rowInfo); SortUseCase.sortHyperEdges(resultHyperEdgeRanges); return { @@ -117,7 +120,7 @@ export default class ToPaohvisDataParserUseCase { hyperEdgeDegree: Record<string, number>, nodeOrder: PaohvisNodeOrder, yAxisNodeType: string, - xAxisNodeType: string, + xAxisNodeType: string ): PaohvisNodeInfo { const rowNodes = filterRowNodes(nodes, hyperEdgeDegree, yAxisNodeType); SortUseCase.sortNodes(nodeOrder, rowNodes, hyperEdgeDegree); @@ -131,11 +134,7 @@ export default class ToPaohvisDataParserUseCase { yNodeIndexCounter++; } - const xNodesAttributesDict: Record<string, any> = getXNodesAttributesDict( - yAxisNodeType, - xAxisNodeType, - nodes, - ); + const xNodesAttributesDict: Record<string, any> = getXNodesAttributesDict(yAxisNodeType, xAxisNodeType, nodes); return { rowLabels: rowLabels, xNodesAttributesDict: xNodesAttributesDict, @@ -150,11 +149,7 @@ export default class ToPaohvisDataParserUseCase { * @param isEntityRelationFrom is to decide if you need to count the from's or the to's of the edge. * @returns a dictionary where you can find how many edges are connected to the nodes. */ - private static GetHyperEdgeDegreeDict( - nodes: Node[], - edges: Edge[], - isEntityRelationFrom: boolean, - ): Record<string, number> { + private static GetHyperEdgeDegreeDict(nodes: Node[], edges: Edge[], isEntityRelationFrom: boolean): Record<string, number> { const hyperEdgeDegreeDict: Record<string, number> = {}; //initialize dictionary @@ -162,6 +157,7 @@ export default class ToPaohvisDataParserUseCase { //count node appearance frequencies edges.forEach((edge) => { + // console.log(isEntityRelationFrom, edge.from, edge.to); if (isEntityRelationFrom) hyperEdgeDegreeDict[edge.from]++; else hyperEdgeDegreeDict[edge.to]++; }); @@ -177,12 +173,7 @@ export default class ToPaohvisDataParserUseCase { * @param rowInfo is the information about the nodes that's needed to parse the edges to their respective hyperedge range. * @returns the hyperedge ranges that will be used by the Paohvis table. */ - private static parseHyperEdgeRanges( - nodes: Node[], - edges: Edge[], - axisInfo: PaohvisAxisInfo, - rowInfo: PaohvisNodeInfo, - ): HyperEdgeRange[] { + private static parseHyperEdgeRanges(nodes: Node[], edges: Edge[], axisInfo: PaohvisAxisInfo, rowInfo: PaohvisNodeInfo): HyperEdgeRange[] { if (nodes.length == 0 || edges.length == 0) return []; const resultHyperEdgeRanges: HyperEdgeRange[] = []; @@ -216,8 +207,7 @@ export default class ToPaohvisDataParserUseCase { // check if the chosen attribute is an attribute of the edge or the node let attribute: any; - if (axisInfo.selectedAttribute.origin == AttributeOrigin.relation) - attribute = edge.attributes[xAxisAttributeType]; + if (axisInfo.selectedAttribute.origin == AttributeOrigin.relation) attribute = edge.attributes[xAxisAttributeType]; else attribute = xNodesAttributesDict[edgeDirectionOpposite][xAxisAttributeType]; // if no edge attribute was selected, then all edges will be placed in one big hyperEdgeRange @@ -251,8 +241,7 @@ export default class ToPaohvisDataParserUseCase { hyperEdgesDict[attribute][edgeDirectionOpposite] = 0; let label: string; - if (xAxisAttributeType != ValueType.noAttribute && xAxisAttributeType != '') - label = attribute.toString(); + if (xAxisAttributeType != ValueType.noAttribute && xAxisAttributeType != '') label = attribute.toString(); else label = 'No attribute was selected'; const hyperEdge: HyperEdgeI = { indices: [edgeIndexInResult], @@ -299,12 +288,7 @@ function calcMaxRowLabelWidth(rowLabels: string[]) { const margin = 10; let maxLabelWidth = 0; rowLabels.forEach((rowLabel) => { - const textWidth: number = getWidthOfText( - rowLabel + ' ', - style.tableFontFamily, - style.tableFontSize, - style.tableFontWeight, - ); + const textWidth: number = getWidthOfText(rowLabel + ' ', style.tableFontFamily, style.tableFontSize, style.tableFontWeight); if (textWidth > maxLabelWidth) maxLabelWidth = textWidth; }); return maxLabelWidth + margin; @@ -313,12 +297,10 @@ function calcMaxRowLabelWidth(rowLabels: string[]) { /** Gets a dictionary where you can find the attributes that belong to the nodes on teh x-axis. */ function getXNodesAttributesDict(yAxisNodeType: string, xAxisNodeType: string, nodes: Node[]) { const resultXNodesAttributesDict: Record<string, any> = {}; - if (yAxisNodeType == xAxisNodeType) - nodes.forEach((node) => (resultXNodesAttributesDict[node!.id] = node.attributes)); + if (yAxisNodeType == xAxisNodeType) nodes.forEach((node) => (resultXNodesAttributesDict[node!.id] = node.attributes)); else nodes.forEach((node) => { - if (getGroupName(node) == xAxisNodeType) - resultXNodesAttributesDict[node.id] = node.attributes; + if (getGroupName(node) == xAxisNodeType) resultXNodesAttributesDict[node.id] = node.attributes; }); return resultXNodesAttributesDict; } @@ -346,10 +328,8 @@ function newFrequencies(rowLabels: string[]): number[] { } /** Filters out nodes that have no edges and nodes that are not the specified type. */ -function filterRowNodes( - nodes: Node[], - hyperEdgeDegree: Record<string, number>, - rowNodeType: string, -): Node[] { - return nodes.filter((node) => hyperEdgeDegree[node.id] > 0 && getGroupName(node) == rowNodeType); +function filterRowNodes(nodes: Node[], hyperEdgeDegree: Record<string, number>, rowNodeType: string): Node[] { + return nodes.filter((node) => { + return hyperEdgeDegree[node.id] > 0 && getGroupName(node) == rowNodeType; + }); } diff --git a/libs/shared/lib/vis/paohvis/utils/utils.tsx b/libs/shared/lib/vis/paohvis/utils/utils.tsx index 4476bb68f..0d41d4617 100644 --- a/libs/shared/lib/vis/paohvis/utils/utils.tsx +++ b/libs/shared/lib/vis/paohvis/utils/utils.tsx @@ -1,84 +1,81 @@ -import { log } from "console"; -import { SchemaAttribute, SchemaGraph, SchemaGraphologyEdge, SchemaGraphologyNode } from "../../../schema"; -import { AttributeNames, EntitiesFromSchema, RelationsFromSchema } from "../Types"; +import { log } from 'console'; +import { SchemaAttribute, SchemaGraph, SchemaGraphologyEdge, SchemaGraphologyNode } from '../../../schema'; +import { AttributeNames, EntitiesFromSchema, RelationsFromSchema } from '../Types'; /** - * Takes a schema result and calculates all the entity names, and relation names and attribute names per entity. - * Used by PAOHvis to show all possible options to choose from when adding a new PAOHvis visualisation or when filtering. - * @param {SchemaResultType} schemaResult A new schema result from the backend. - * @returns {EntitiesFromSchema} All entity names, and relation names and attribute names per entity. - */ + * Takes a schema result and calculates all the entity names, and relation names and attribute names per entity. + * Used by PAOHvis to show all possible options to choose from when adding a new PAOHvis visualisation or when filtering. + * @param {SchemaResultType} schemaResult A new schema result from the backend. + * @returns {EntitiesFromSchema} All entity names, and relation names and attribute names per entity. + */ export function calculateAttributesAndRelations(schemaResult: SchemaGraph): EntitiesFromSchema { - const attributesPerEntity: Record<string, AttributeNames> = calculateAttributes( - schemaResult, - ); - const relationsPerEntity: Record<string, string[]> = calculateRelations(schemaResult); + const attributesPerEntity: Record<string, AttributeNames> = calculateAttributes(schemaResult); + const relationsPerEntity: Record<string, string[]> = calculateRelations(schemaResult); - return { - entityNames: schemaResult.nodes.map((node) => node?.attributes?.name || 'ERROR'), - attributesPerEntity, - relationsPerEntity, - }; + return { + entityNames: schemaResult.nodes.map((node) => node?.attributes?.name || 'ERROR'), + attributesPerEntity, + relationsPerEntity, + }; } - /** * Takes a schema result and calculates all the attribute names per entity. * @param {SchemaResultType} schemaResult A new schema result from the backend. * @returns {Record<string, AttributeNames>} All attribute names per entity. */ export function calculateAttributes(schemaResult: SchemaGraph): Record<string, AttributeNames> { - const attributesPerEntity: Record<string, AttributeNames> = {}; - // Go through each entity. - schemaResult.nodes.forEach((node) => { - if (node?.attributes?.name === undefined) { - console.error('ERROR: Node has no name attribute or name.', node); - return; - } + const attributesPerEntity: Record<string, AttributeNames> = {}; + // Go through each entity. + schemaResult.nodes.forEach((node) => { + if (node?.attributes?.name === undefined) { + console.error('ERROR: Node has no name attribute or name.', node); + return; + } - // Extract the attribute names per datatype for each entity. - const textAttributeNames: string[] = []; - const boolAttributeNames: string[] = []; - const numberAttributeNames: string[] = []; + // Extract the attribute names per datatype for each entity. + const textAttributeNames: string[] = []; + const boolAttributeNames: string[] = []; + const numberAttributeNames: string[] = []; - node.attributes.attributes.forEach((attr) => { - if (attr.type == 'string') textAttributeNames.push(attr.name); - else if (attr.type == 'int' || attr.type == 'float') numberAttributeNames.push(attr.name); - else boolAttributeNames.push(attr.name); - }); - - // Create a new object with the arrays with attribute names per datatype. - attributesPerEntity[node.attributes.name] = { - textAttributeNames, - boolAttributeNames, - numberAttributeNames, - }; + node.attributes.attributes.forEach((attr) => { + if (attr.type == 'string') textAttributeNames.push(attr.name); + else if (attr.type == 'int' || attr.type == 'float') numberAttributeNames.push(attr.name); + else boolAttributeNames.push(attr.name); }); - return attributesPerEntity; + + // Create a new object with the arrays with attribute names per datatype. + attributesPerEntity[node.attributes.name] = { + textAttributeNames, + boolAttributeNames, + numberAttributeNames, + }; + }); + return attributesPerEntity; } /** -* Takes a schema result and calculates all the relation names per entity. -* @param {SchemaResultType} schemaResult A new schema result from the backend. -* @returns {Record<string, AttributeNames>} All relation (from and to) names per entity. -*/ + * Takes a schema result and calculates all the relation names per entity. + * @param {SchemaResultType} schemaResult A new schema result from the backend. + * @returns {Record<string, AttributeNames>} All relation (from and to) names per entity. + */ export function calculateRelations(schemaResult: SchemaGraph): Record<string, string[]> { - const relationsPerEntity: Record<string, string[]> = {}; - // Go through each relation. - schemaResult.edges.forEach((edge) => { - if (edge?.attributes === undefined) { - console.error('ERROR: Edge has no attribute.', edge); - return; - } + const relationsPerEntity: Record<string, string[]> = {}; + // Go through each relation. + schemaResult.edges.forEach((edge) => { + if (edge?.attributes === undefined) { + console.error('ERROR: Edge has no attribute.', edge); + return; + } - // Extract the from-node-name (collection name) from every relation. - if (relationsPerEntity[edge.attributes.from]) relationsPerEntity[edge.attributes.from].push(edge.attributes.collection); - else relationsPerEntity[edge.attributes.from] = [edge.attributes.collection]; - // Extract the to-node-name (collection name) from every relation. - if (relationsPerEntity[edge.attributes.to]) relationsPerEntity[edge.attributes.to].push(edge.attributes.collection); - else relationsPerEntity[edge.attributes.to] = [edge.attributes.collection]; - }); - return relationsPerEntity; + // Extract the from-node-name (collection name) from every relation. + if (relationsPerEntity[edge.attributes.from]) relationsPerEntity[edge.attributes.from].push(edge.attributes.collection); + else relationsPerEntity[edge.attributes.from] = [edge.attributes.collection]; + // Extract the to-node-name (collection name) from every relation. + if (relationsPerEntity[edge.attributes.to]) relationsPerEntity[edge.attributes.to].push(edge.attributes.collection); + else relationsPerEntity[edge.attributes.to] = [edge.attributes.collection]; + }); + return relationsPerEntity; } /** @@ -88,71 +85,66 @@ export function calculateRelations(schemaResult: SchemaGraph): Record<string, st * @returns {EntitiesFromSchema} All entity names, and relation names and attribute names per entity. */ export function calculateAttributesFromRelation(schemaResult: SchemaGraph): RelationsFromSchema { - const relationCollections: string[] = []; - const attributesPerRelation: Record<string, AttributeNames> = {}; - const nameOfCollectionPerRelation: Record<string, string> = {}; - // Go through each relation. - schemaResult.edges.forEach((edge) => { - if (edge?.attributes === undefined) { - console.error('ERROR: Edge has no attribute.', edge); - return; - } + const relationCollections: string[] = []; + const attributesPerRelation: Record<string, AttributeNames> = {}; + const nameOfCollectionPerRelation: Record<string, string> = {}; + // Go through each relation. + schemaResult.edges.forEach((edge) => { + if (edge?.attributes === undefined) { + console.error('ERROR: Edge has no attribute.', edge); + return; + } - if (!nameOfCollectionPerRelation[edge.attributes.collection]) { - relationCollections.push(edge.attributes.collection); - nameOfCollectionPerRelation[edge.attributes.collection] = `${edge.attributes.from}:${edge.attributes.to}:${edge.attributes.name}`; - } - // Extract the attribute names per datatype for each relation. - const textAttributeNames: string[] = []; - const boolAttributeNames: string[] = []; - const numberAttributeNames: string[] = []; - edge.attributes.attributes.forEach((attr: SchemaAttribute) => { - if (attr.type == 'string') textAttributeNames.push(attr.name); - else if (attr.type == 'int' || attr.type == 'float') numberAttributeNames.push(attr.name); - else boolAttributeNames.push(attr.name); - }); - - // Create a new object with the arrays with attribute names per datatype. - attributesPerRelation[edge.attributes.collection] = { - textAttributeNames, - boolAttributeNames, - numberAttributeNames, - }; + if (!nameOfCollectionPerRelation[edge.attributes.collection]) { + relationCollections.push(edge.attributes.collection); + nameOfCollectionPerRelation[edge.attributes.collection] = `${edge.attributes.from}:${edge.attributes.to}:${edge.attributes.name}`; + } + // Extract the attribute names per datatype for each relation. + const textAttributeNames: string[] = []; + const boolAttributeNames: string[] = []; + const numberAttributeNames: string[] = []; + edge.attributes.attributes.forEach((attr: SchemaAttribute) => { + if (attr.type == 'string') textAttributeNames.push(attr.name); + else if (attr.type == 'int' || attr.type == 'float') numberAttributeNames.push(attr.name); + else boolAttributeNames.push(attr.name); }); - return { - relationCollection: relationCollections, - relationNames: nameOfCollectionPerRelation, - attributesPerRelation: attributesPerRelation, + + // Create a new object with the arrays with attribute names per datatype. + attributesPerRelation[edge.attributes.collection] = { + textAttributeNames, + boolAttributeNames, + numberAttributeNames, }; + }); + return { + relationCollection: relationCollections, + relationNames: nameOfCollectionPerRelation, + attributesPerRelation: attributesPerRelation, + }; } /** * Filters duplicate elements from array with a hashtable. * From https://stackoverflow.com/questions/9229645/remove-duplicate-values-from-js-array */ export function uniq(element: number[]) { - const seen: Record<number, boolean> = {}; - return element.filter(function (item) { - return seen.hasOwnProperty(item) ? false : (seen[item] = true); - }); + const seen: Record<number, boolean> = {}; + return element.filter(function (item) { + return seen.hasOwnProperty(item) ? false : (seen[item] = true); + }); } /** -* Calculate the width of the specified text. -* @param txt Text input as string. -* @param fontname Name of the font. -* @param fontsize Size of the fond in px. -* @param fontWeight The weight of the font. -* @returns {number} Width of the textfield in px. -*/ -export const getWidthOfText = ( - txt: string, - fontname: string, - fontsize: string, - fontWeight = 'normal', -): number => { - let c = document.createElement('canvas'); - let ctx = c.getContext('2d') as CanvasRenderingContext2D; - let fontspec = fontWeight + ' ' + fontsize + ' ' + fontname; - if (ctx.font !== fontspec) ctx.font = fontspec; - return ctx.measureText(txt).width; -}; \ No newline at end of file + * Calculate the width of the specified text. + * @param txt Text input as string. + * @param fontname Name of the font. + * @param fontsize Size of the fond in px. + * @param fontWeight The weight of the font. + * @returns {number} Width of the textfield in px. + */ +export const getWidthOfText = (txt: string, fontname: string, fontsize: string, fontWeight = 'normal'): number => { + let c = document.createElement('canvas'); + let ctx = c.getContext('2d') as CanvasRenderingContext2D; + let fontspec = fontWeight + ' ' + fontsize + ' ' + fontname; + if (ctx.font !== fontspec) ctx.font = fontspec; + return ctx.measureText(txt).width; +}; diff --git a/libs/shared/lib/vis/rawjsonvis/index.ts b/libs/shared/lib/vis/rawjsonvis/index.ts index 89dd901fb..f62b40657 100644 --- a/libs/shared/lib/vis/rawjsonvis/index.ts +++ b/libs/shared/lib/vis/rawjsonvis/index.ts @@ -1 +1 @@ -export * from './rawjsonvis'; \ No newline at end of file +export * from './rawjsonvis'; diff --git a/libs/shared/lib/vis/rawjsonvis/rawjsonvis.spec.tsx b/libs/shared/lib/vis/rawjsonvis/rawjsonvis.spec.tsx index 2ca7d9116..338394ddb 100644 --- a/libs/shared/lib/vis/rawjsonvis/rawjsonvis.spec.tsx +++ b/libs/shared/lib/vis/rawjsonvis/rawjsonvis.spec.tsx @@ -1,10 +1,10 @@ -import { render } from "@testing-library/react"; -import { assert, describe, expect, it } from "vitest"; +import { render } from '@testing-library/react'; +import { assert, describe, expect, it } from 'vitest'; //import RawJSONVis from './rawjsonvis'; -describe("RawJSONVis", () => { - it("should render successfully", () => { +describe('RawJSONVis', () => { + it('should render successfully', () => { //const { baseElement } = render(<RawJSONVis />); expect(true).toBeTruthy(); }); diff --git a/libs/shared/lib/vis/rawjsonvis/rawjsonvis.stories.tsx b/libs/shared/lib/vis/rawjsonvis/rawjsonvis.stories.tsx index 69f6ca4ca..70492c16e 100644 --- a/libs/shared/lib/vis/rawjsonvis/rawjsonvis.stories.tsx +++ b/libs/shared/lib/vis/rawjsonvis/rawjsonvis.stories.tsx @@ -12,7 +12,7 @@ import { import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import { GraphPolarisThemeProvider } from '../../data-access/theme'; -import { mockLargeQueryResults } from '../../mock-data/query-result' +import { mockLargeQueryResults } from '../../mock-data/query-result'; const Component: Meta<typeof RawJSONVis> = { /* 👇 The title prop is optional. @@ -45,15 +45,18 @@ export const SimpleData = { const dispatch = Mockstore.dispatch; dispatch( assignNewGraphQueryResult({ - nodes: [ - { id: 'agent/007', attributes: { name: 'Daniel Craig' } }, - { id: 'villain', attributes: { name: 'Le Chiffre' } }, - ], - edges: [], + type: 'nodelink', + payload: { + nodes: [ + { id: 'agent/007', attributes: { name: 'Daniel Craig' } }, + { id: 'villain', attributes: { name: 'Le Chiffre' } }, + ], + edges: [], + }, }) ); - } -} + }, +}; export const LargeData = { args: { @@ -62,16 +65,19 @@ export const LargeData = { play: async () => { const dispatch = Mockstore.dispatch; dispatch( - assignNewGraphQueryResult(mockLargeQueryResults) + assignNewGraphQueryResult({ + type: 'nodelink', + payload: mockLargeQueryResults, + }) ); - } -} + }, +}; export const Loading = { args: { loading: true, }, -} +}; export const Empty = { args: { @@ -80,7 +86,7 @@ export const Empty = { play: async () => { const dispatch = store.dispatch; dispatch(resetGraphQueryResults()); - } -} + }, +}; export default Component; diff --git a/libs/shared/lib/vis/rawjsonvis/rawjsonvis.tsx b/libs/shared/lib/vis/rawjsonvis/rawjsonvis.tsx index 61ebc2364..302335135 100644 --- a/libs/shared/lib/vis/rawjsonvis/rawjsonvis.tsx +++ b/libs/shared/lib/vis/rawjsonvis/rawjsonvis.tsx @@ -1,8 +1,4 @@ -import { - changePrimary, - useAppDispatch, - useGraphQueryResult, -} from '../../data-access/store'; +import { changePrimary, useAppDispatch, useGraphQueryResult } from '../../data-access/store'; import { useTheme } from '@mui/material'; import React, { useEffect, useState } from 'react'; import ReactJSONView from 'react-json-view'; @@ -35,9 +31,7 @@ export const RawJSONVis = React.memo((props: RawJSONVisProps) => { return ( <div className="overflow-scroll"> <input - onChange={(v) => - dispatch(changePrimary({ main: v.currentTarget.value })) - } + onChange={(v) => dispatch(changePrimary({ main: v.currentTarget.value }))} type="color" name="head" value={theme.palette.primary.main} @@ -49,11 +43,7 @@ export const RawJSONVis = React.memo((props: RawJSONVisProps) => { paddingLeft: '30px', }} > - <svg - className={styles.rotating} - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 50 50" - > + <svg className={styles.rotating} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"> <circle className={styles.ring} cx="25" cy="25" r="20"></circle> <circle className={styles.ball} cx="25" cy="5" r="3.5"></circle> </svg> @@ -67,12 +57,7 @@ export const RawJSONVis = React.memo((props: RawJSONVisProps) => { paddingLeft: '30px', }} > - <ReactJSONView - src={graphQueryResult} - collapsed={1} - quotesOnKeys={false} - displayDataTypes={false} - /> + <ReactJSONView src={graphQueryResult} collapsed={1} quotesOnKeys={false} displayDataTypes={false} /> </div> </div> )} diff --git a/libs/shared/lib/vis/semanticsubstrates/SemanticSubstratesViewModel.t b/libs/shared/lib/vis/semanticsubstrates/SemanticSubstratesViewModel.t deleted file mode 100644 index 2b1b6b267..000000000 --- a/libs/shared/lib/vis/semanticsubstrates/SemanticSubstratesViewModel.t +++ /dev/null @@ -1,1156 +0,0 @@ -import Broker from '../../../../domain/entity/broker/broker'; -import SemanticSubstratesViewModelImpl from './SemanticSubstratesViewModelImpl'; -import mockNodeLinkResult from '../../../../data/mock-data/query-result/big2ndChamberQueryResult'; -import mockSchema from '../../../../data/mock-data/schema-result/2ndChamberSchemaMock'; -import { MinMaxType } from '../../../../domain/entity/semantic-substrates/structures/Types'; - -jest.mock('../../../view/result-visualisations/semantic-substrates/SemanticSubstratesStylesheet'); - -describe('SemanticSubstratesViewModelImpl: Broker subscriptions', () => { - it('should consume schema results when subscribed', () => { - const viewModel = new SemanticSubstratesViewModelImpl(); - - const mockConsumeMessages = jest.fn(); - viewModel.consumeMessageFromBackend = mockConsumeMessages; - - viewModel.subscribeToSchemaResult(); - Broker.instance().publish('test schema result', 'schema_result'); - expect(mockConsumeMessages.mock.calls[0][0]).toEqual('test schema result'); - - viewModel.unSubscribeFromSchemaResult(); - Broker.instance().publish('test schema result', 'schema_result'); - expect(mockConsumeMessages).toBeCalledTimes(1); - }); -}); - -describe('SemanticSubstratesViewModelImpl:OnPlotTitleChanged', () => { - it('should change the plot title', () => { - // Initialize a ViewModel with some plots. - const viewModelImpl = new SemanticSubstratesViewModelImpl(); - viewModelImpl.consumeMessageFromBackend(mockSchema); - viewModelImpl.consumeMessageFromBackend(mockNodeLinkResult); - - const onViewModelChangedMock = jest.fn(); - const baseViewMock = { - onViewModelChanged: onViewModelChangedMock, - }; - viewModelImpl.attachView(baseViewMock); - - const i = 1; - const oldPlotSpec = viewModelImpl.plotSpecifications[i]; - viewModelImpl.onPlotTitleChanged(i, 'commissies', 'naam', 'Defensie'); - - expect(viewModelImpl.plotSpecifications[i]).toEqual({ - ...oldPlotSpec, - entity: 'commissies', - labelAttributeType: 'naam', - labelAttributeValue: 'Defensie', - }); - - // And check if the NotifyViewAboutChanges is called. - expect(onViewModelChangedMock).toBeCalledTimes(1); - - viewModelImpl.onPlotTitleChanged(i, 'commissies', 'id', 'Defensie'); - expect(viewModelImpl.plotSpecifications[i]).toEqual({ - ...oldPlotSpec, - entity: 'commissies', - labelAttributeType: 'id', - labelAttributeValue: 'Defensie', - }); - }); -}); - -describe('SemanticSubstratesViewModelImpl', () => { - it('should apply default specs on consuming a new node link result', () => { - const viewModelImpl = new SemanticSubstratesViewModelImpl(); - - const onViewModelChangedMock = jest.fn(); - const baseViewMock = { - onViewModelChanged: onViewModelChangedMock, - }; - viewModelImpl.attachView(baseViewMock); - - viewModelImpl.consumeMessageFromBackend(mockSchema); - - // Consume a nodelink query result. - viewModelImpl.consumeMessageFromBackend(mockNodeLinkResult); - - let mockCalc = viewModelImpl.scaleCalculation; - let mockCalcColour = viewModelImpl.colourCalculation; - - const expectedPlots = [ - { - title: 'partij:VVD', - nodes: [ - { - id: 'kamerleden/148', - data: { text: 'kamerleden/148' }, - originalPosition: { x: 1, y: 0 }, - attributes: { - anc: 1526, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/9ab41898-332f-4c08-a834-d58b4669c990.jpg?itok=pbUPRriP', - leeftijd: 43, - naam: 'Dilan Yeilgz-Zegerius', - partij: 'VVD', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 114.28571428571432, y: 100 }, - }, - { - id: 'kamerleden/132', - data: { text: 'kamerleden/132' }, - originalPosition: { x: 2, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/b625d2bf-da74-40a2-b013-2069a09dcb6c.jpg?itok=zMgno8bi', - leeftijd: 36, - naam: 'Peter Valstar', - partij: 'VVD', - woonplaats: "'s-Gravenzande", - }, - scaledPosition: { x: 133.99014778325127, y: 100 }, - }, - { - id: 'kamerleden/131', - data: { text: 'kamerleden/131' }, - originalPosition: { x: 3, y: 0 }, - attributes: { - anc: 1304, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/97ffe642-648f-4dab-a0e8-0720e96e3e35.jpg?itok=eDllv9dS', - leeftijd: 49, - naam: 'Judith Tielen', - partij: 'VVD', - woonplaats: 'Utrecht', - }, - scaledPosition: { x: 153.6945812807882, y: 100 }, - }, - { - id: 'kamerleden/128', - data: { text: 'kamerleden/128' }, - originalPosition: { x: 4, y: 0 }, - attributes: { - anc: 3171, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/2538ca6a-8254-4c7e-af71-a20db82afd30.jpg?itok=GvwO8NYO', - leeftijd: 46, - naam: 'Ockje Tellegen', - partij: 'VVD', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 173.39901477832515, y: 100 }, - }, - { - id: 'kamerleden/117', - data: { text: 'kamerleden/117' }, - originalPosition: { x: 5, y: 0 }, - attributes: { - anc: 2004, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/8b3664bd-77e4-468b-af96-f3f4ec27fcce.jpg?itok=ofrc9cnP', - leeftijd: 54, - naam: 'Mark Rutte', - partij: 'VVD', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 193.1034482758621, y: 100 }, - }, - { - id: 'kamerleden/106', - data: { text: 'kamerleden/106' }, - originalPosition: { x: 6, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/0e34f732-337a-4339-961f-8c18d68e714d.jpg?itok=FTBMCT9a', - leeftijd: 54, - naam: 'Marille Paul', - partij: 'VVD', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 212.80788177339906, y: 100 }, - }, - { - id: 'kamerleden/100', - data: { text: 'kamerleden/100' }, - originalPosition: { x: 7, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/2b223ce1-e251-462b-a5fc-971c92bbf37c.jpg?itok=MD8qI-l1', - leeftijd: 43, - naam: 'Daan de Neef', - partij: 'VVD', - woonplaats: 'Breda', - }, - scaledPosition: { x: 232.512315270936, y: 100 }, - }, - { - id: 'kamerleden/97', - data: { text: 'kamerleden/97' }, - originalPosition: { x: 8, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/cd02edbc-106e-46d1-8d70-6594741356ea.jpg?itok=Q9q13ntf', - leeftijd: 33, - naam: 'Fahid Minhas', - partij: 'VVD', - woonplaats: 'Schiedam', - }, - scaledPosition: { x: 252.21674876847294, y: 100 }, - }, - { - id: 'kamerleden/81', - data: { text: 'kamerleden/81' }, - originalPosition: { x: 9, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/8767e9de-869e-4e8a-940f-1a63d15eab3c.jpg?itok=tdoE8yQX', - leeftijd: 28, - naam: 'Daan de Kort', - partij: 'VVD', - woonplaats: 'Veldhoven', - }, - scaledPosition: { x: 271.9211822660099, y: 100 }, - }, - { - id: 'kamerleden/79', - data: { text: 'kamerleden/79' }, - originalPosition: { x: 10, y: 0 }, - attributes: { - anc: 1526, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/88e0734b-8c03-4daa-8ec5-055e36c07030.jpg?itok=_ZteUeWk', - leeftijd: 39, - naam: 'Daniel Koerhuis', - partij: 'VVD', - woonplaats: 'Raalte', - }, - scaledPosition: { x: 291.6256157635468, y: 100 }, - }, - { - id: 'kamerleden/145', - data: { text: 'kamerleden/145' }, - originalPosition: { x: 11, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/8ee20f38-f3f0-4ff9-b5d9-84557b5ddcb1.jpg?itok=ExYCR1Ti', - leeftijd: 51, - naam: 'Hatte van der Woude', - partij: 'VVD', - woonplaats: 'Delft', - }, - scaledPosition: { x: 311.3300492610838, y: 100 }, - }, - { - id: 'kamerleden/70', - data: { text: 'kamerleden/70' }, - originalPosition: { x: 12, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/f23be4bd-7b2b-45dd-be84-4f76f11b624a.jpg?itok=DsfJ-8o0', - leeftijd: 43, - naam: 'Roelien Kamminga', - partij: 'VVD', - woonplaats: 'Groningen', - }, - scaledPosition: { x: 331.03448275862075, y: 100 }, - }, - { - id: 'kamerleden/127', - data: { text: 'kamerleden/127' }, - originalPosition: { x: 13, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/ce400601-651e-4ac9-a43e-ed8d4817b7fd.jpg?itok=wsISYKCx', - leeftijd: 44, - naam: 'Pim van Strien', - partij: 'VVD', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 350.7389162561577, y: 100 }, - }, - { - id: 'kamerleden/115', - data: { text: 'kamerleden/115' }, - originalPosition: { x: 14, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/4bf69aa9-29a7-44db-b7ee-8bfa40e343dd.jpg?itok=-BKeAg8F', - leeftijd: 32, - naam: 'Queeny Rajkowski', - partij: 'VVD', - woonplaats: 'Utrecht', - }, - scaledPosition: { x: 370.44334975369463, y: 100 }, - }, - { - id: 'kamerleden/64', - data: { text: 'kamerleden/64' }, - originalPosition: { x: 15, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/7954eb2c-1bf5-42af-80dc-85f29b367db0.jpg?itok=9ucjAERV', - leeftijd: 49, - naam: 'Folkert Idsinga', - partij: 'VVD', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 390.1477832512316, y: 100 }, - }, - { - id: 'kamerleden/60', - data: { text: 'kamerleden/60' }, - originalPosition: { x: 16, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/8509e44d-0f9b-413a-a2cd-f64d656c3955.jpg?itok=Iy2_G4Jx', - leeftijd: 53, - naam: 'Jacqueline van den Hil', - partij: 'VVD', - woonplaats: 'Goes', - }, - scaledPosition: { x: 409.8522167487685, y: 100 }, - }, - { - id: 'kamerleden/143', - data: { text: 'kamerleden/143' }, - originalPosition: { x: 17, y: 0 }, - attributes: { - anc: 1823, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/7d18b6e3-de0c-4aea-bef8-242bbc38e936.jpg?itok=zebZoGjq', - leeftijd: 43, - naam: 'Jeroen van Wijngaarden', - partij: 'VVD', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 429.55665024630554, y: 100 }, - }, - { - id: 'kamerleden/56', - data: { text: 'kamerleden/56' }, - originalPosition: { x: 18, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/a3b17348-54cf-4bca-82d4-e62bd46c2fbc.jpg?itok=MF1OOpeK', - leeftijd: 40, - naam: 'Eelco Heinen', - partij: 'VVD', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 449.26108374384245, y: 100 }, - }, - { - id: 'kamerleden/142', - data: { text: 'kamerleden/142' }, - originalPosition: { x: 19, y: 0 }, - attributes: { - anc: 1526, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/c3b1dca8-2cb4-4025-9527-36163a098c1f.jpg?itok=3BnXwQgy', - leeftijd: 35, - naam: 'Dennis Wiersma', - partij: 'VVD', - woonplaats: 'De Bilt', - }, - scaledPosition: { x: 468.96551724137936, y: 100 }, - }, - { - id: 'kamerleden/46', - data: { text: 'kamerleden/46' }, - originalPosition: { x: 20, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/a0b48551-42b7-4c55-9556-6b997d978ff2.jpg?itok=-A3mAzHe', - leeftijd: 41, - naam: 'Peter de Groot', - partij: 'VVD', - woonplaats: 'Harderwijk', - }, - scaledPosition: { x: 488.6699507389164, y: 100 }, - }, - { - id: 'kamerleden/38', - data: { text: 'kamerleden/38' }, - originalPosition: { x: 21, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/9c8bc5bc-8d13-4c11-be37-57a43a1d734b.jpg?itok=TqEaAJCz', - leeftijd: 30, - naam: 'Silvio Erkens', - partij: 'VVD', - woonplaats: 'Kerkrade', - }, - scaledPosition: { x: 508.3743842364533, y: 100 }, - }, - { - id: 'kamerleden/35', - data: { text: 'kamerleden/35' }, - originalPosition: { x: 22, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/ea21d788-7cfe-41ff-b22f-0664397139af.jpg?itok=4G8NOaak', - leeftijd: 32, - naam: 'Ulysse Ellian', - partij: 'VVD', - woonplaats: 'Almere', - }, - scaledPosition: { x: 528.0788177339903, y: 100 }, - }, - { - id: 'kamerleden/33', - data: { text: 'kamerleden/33' }, - originalPosition: { x: 23, y: 0 }, - attributes: { - anc: 1526, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/c4f7c468-721e-4953-97f2-bff05fe570c4.jpg?itok=MmXfOgJx', - leeftijd: 41, - naam: 'Zohair El Yassini', - partij: 'VVD', - woonplaats: 'Utrecht', - }, - scaledPosition: { x: 547.7832512315272, y: 100 }, - }, - { - id: 'kamerleden/25', - data: { text: 'kamerleden/25' }, - originalPosition: { x: 24, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/bf494e35-c009-4c74-a5c6-3cb2cd26bb51.jpg?itok=2CTfrPhP', - leeftijd: 31, - naam: 'Thom van Campen', - partij: 'VVD', - woonplaats: 'Zwolle', - }, - scaledPosition: { x: 567.4876847290642, y: 100 }, - }, - { - id: 'kamerleden/54', - data: { text: 'kamerleden/54' }, - originalPosition: { x: 25, y: 0 }, - attributes: { - anc: 2601, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/522d59a0-10ba-488f-a27a-a1dda7581900.jpg?itok=5b_3G5nE', - leeftijd: 43, - naam: 'Rudmer Heerema', - partij: 'VVD', - woonplaats: 'Alkmaar', - }, - scaledPosition: { x: 587.1921182266011, y: 100 }, - }, - { - id: 'kamerleden/96', - data: { text: 'kamerleden/96' }, - originalPosition: { x: 26, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/973da897-66c7-4010-a98d-505e0e97a60c.jpg?itok=HLNJtdsA', - leeftijd: 44, - naam: 'Ingrid Michon-Derkzen', - partij: 'VVD', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 606.896551724138, y: 100 }, - }, - { - id: 'kamerleden/9', - data: { text: 'kamerleden/9' }, - originalPosition: { x: 27, y: 0 }, - attributes: { - anc: 1414, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/a962ffcb-a300-45e1-8850-70c7173c233e.jpg?itok=0f-c2lGz', - leeftijd: 35, - naam: 'Bente Becker', - partij: 'VVD', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 626.600985221675, y: 100 }, - }, - { - id: 'kamerleden/23', - data: { text: 'kamerleden/23' }, - originalPosition: { x: 28, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/e5c4753f-d2d8-4a4a-ae9d-62118892a731.jpg?itok=iGnPsRMX', - leeftijd: 34, - naam: 'Ruben Brekelmans', - partij: 'VVD', - woonplaats: 'Oisterwijk', - }, - scaledPosition: { x: 646.3054187192118, y: 100 }, - }, - { - id: 'kamerleden/135', - data: { text: 'kamerleden/135' }, - originalPosition: { x: 29, y: 0 }, - attributes: { - anc: 3122, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/b8d002bc-ea6c-4f57-b36e-38415b205e09.jpg?itok=tHQU-Qrn', - leeftijd: 56, - naam: 'Aukje de Vries', - partij: 'VVD', - woonplaats: 'Leeuwarden', - }, - scaledPosition: { x: 666.0098522167489, y: 100 }, - }, - { - id: 'kamerleden/0', - data: { text: 'kamerleden/0' }, - originalPosition: { x: 30, y: 0 }, - attributes: { - anc: 987, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/397c857a-fda0-414d-8fdc-8288cd3284aa.jpg?itok=55l5zRvr', - leeftijd: 31, - naam: 'Thierry Aartsen', - partij: 'VVD', - woonplaats: 'Breda', - }, - scaledPosition: { x: 685.7142857142858, y: 100 }, - }, - ], - selectedAttributeCatecorigal: 'state', - selectedAttributeNumerical: 'long', - scaleCalculation: mockCalc, - colourCalculation: mockCalcColour, - minmaxXAxis: { min: -4.800000000000001, max: 35.8 }, - minmaxYAxis: { min: -1, max: 1 }, - width: 800, - height: 200, - yOffset: 0, - possibleTitleAttributeValues: [ - 'VVD', - 'D66', - 'GL', - 'PVV', - 'PvdD', - 'PvdA', - 'SGP', - 'BIJ1', - 'CU', - 'BBB', - 'CDA', - 'SP', - 'FVD', - 'DENK', - 'Fractie Den Haan', - 'Volt', - 'JA21', - ], - }, - { - title: 'partij:D66', - nodes: [ - { - id: 'kamerleden/141', - data: { text: 'kamerleden/141' }, - originalPosition: { x: 1, y: 0 }, - attributes: { - anc: 3171, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/d1bb566e-a111-43b7-8856-273ebac1f753.jpg?itok=-k-RCynx', - leeftijd: 48, - naam: 'Steven van Weyenberg', - partij: 'D66', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 114.2857142857143, y: 100 }, - }, - { - id: 'kamerleden/138', - data: { text: 'kamerleden/138' }, - originalPosition: { x: 2, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/6a54c095-9872-4322-81be-114b74233d40.jpg?itok=uf9jc1Gk', - leeftijd: 36, - naam: 'Hanneke van der Werf', - partij: 'D66', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 140.25974025974028, y: 100 }, - }, - { - id: 'kamerleden/123', - data: { text: 'kamerleden/123' }, - originalPosition: { x: 3, y: 0 }, - attributes: { - anc: 1304, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/da9f33b0-80ca-49eb-a1ec-def3b46538d9.jpg?itok=nL6HIBWH', - leeftijd: 38, - naam: 'Joost Sneller', - partij: 'D66', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 166.23376623376626, y: 100 }, - }, - { - id: 'kamerleden/114', - data: { text: 'kamerleden/114' }, - originalPosition: { x: 4, y: 0 }, - attributes: { - anc: 1414, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/6fc5878e-d76a-4e9d-a4e3-e5ef581bb2ff.jpg?itok=qf_oudwu', - leeftijd: 30, - naam: 'Rens Raemakers', - partij: 'D66', - woonplaats: 'Neer', - }, - scaledPosition: { x: 192.20779220779224, y: 100 }, - }, - { - id: 'kamerleden/147', - data: { text: 'kamerleden/147' }, - originalPosition: { x: 5, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/45ef5aae-a6d0-4bad-861e-dd5b883a9665.jpg?itok=h5aEZJVM', - leeftijd: 56, - naam: 'Jorien Wuite', - partij: 'D66', - woonplaats: 'Voorburg', - }, - scaledPosition: { x: 218.18181818181822, y: 100 }, - }, - { - id: 'kamerleden/107', - data: { text: 'kamerleden/107' }, - originalPosition: { x: 6, y: 0 }, - attributes: { - anc: 42, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/d89044ef-b7df-4a15-be10-d342fe381664.jpg?itok=gmiV1adi', - leeftijd: 42, - naam: 'Wieke Paulusma', - partij: 'D66', - woonplaats: 'Groningen', - }, - scaledPosition: { x: 244.1558441558442, y: 100 }, - }, - { - id: 'kamerleden/105', - data: { text: 'kamerleden/105' }, - originalPosition: { x: 7, y: 0 }, - attributes: { - anc: 1526, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/2690bccc-5463-459a-b1df-cb93e222b348.jpg?itok=UZOQRvuv', - leeftijd: 37, - naam: 'Jan Paternotte', - partij: 'D66', - woonplaats: 'Leiden', - }, - scaledPosition: { x: 270.1298701298702, y: 100 }, - }, - { - id: 'kamerleden/94', - data: { text: 'kamerleden/94' }, - originalPosition: { x: 8, y: 0 }, - attributes: { - anc: 3171, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/17f26a48-9f54-42a1-9890-c6da761a9a2f.jpg?itok=BA8JoO5G', - leeftijd: 65, - naam: 'Paul van Meenen', - partij: 'D66', - woonplaats: 'Leiden', - }, - scaledPosition: { x: 296.10389610389615, y: 100 }, - }, - { - id: 'kamerleden/86', - data: { text: 'kamerleden/86' }, - originalPosition: { x: 9, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/fb291eb2-962a-482f-971a-0b62673f9a86.jpg?itok=2J7OszgV', - leeftijd: 41, - naam: 'Jeanet van der Laan', - partij: 'D66', - woonplaats: 'Lisse', - }, - scaledPosition: { x: 322.0779220779221, y: 100 }, - }, - { - id: 'kamerleden/71', - data: { text: 'kamerleden/71' }, - originalPosition: { x: 10, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/8349fff0-99b3-449a-bd4d-932ae95b29da.jpg?itok=xYICmuaX', - leeftijd: 37, - naam: 'Hlya Kat', - partij: 'D66', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 348.0519480519481, y: 100 }, - }, - { - id: 'kamerleden/69', - data: { text: 'kamerleden/69' }, - originalPosition: { x: 11, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/64457f28-37b6-4948-860a-f28bc62b99ae.jpg?itok=Q2hiR0vj', - leeftijd: 59, - naam: 'Sigrid Kaag', - partij: 'D66', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 374.02597402597405, y: 100 }, - }, - { - id: 'kamerleden/68', - data: { text: 'kamerleden/68' }, - originalPosition: { x: 12, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/666f31ca-041f-466d-9e78-804317921d69.jpg?itok=G3ozh6C3', - leeftijd: 37, - naam: 'Romke de Jong', - partij: 'D66', - woonplaats: 'Gorredijk', - }, - scaledPosition: { x: 400.0000000000001, y: 100 }, - }, - { - id: 'kamerleden/66', - data: { text: 'kamerleden/66' }, - originalPosition: { x: 13, y: 0 }, - attributes: { - anc: 1527, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/49be3576-cea3-46c0-87eb-89beb108248d.jpg?itok=VfqikY8P', - leeftijd: 34, - naam: 'Rob Jetten', - partij: 'D66', - woonplaats: 'Ubbergen', - }, - scaledPosition: { x: 425.97402597402595, y: 100 }, - }, - { - id: 'kamerleden/134', - data: { text: 'kamerleden/134' }, - originalPosition: { x: 14, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/19c8d0fb-66ca-4b45-be82-057cb36e1e61.jpg?itok=Bkq8gaTU', - leeftijd: 57, - naam: 'Hans Vijlbrief', - partij: 'D66', - woonplaats: 'Woubrugge', - }, - scaledPosition: { x: 451.94805194805195, y: 100 }, - }, - { - id: 'kamerleden/52', - data: { text: 'kamerleden/52' }, - originalPosition: { x: 15, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/03d66e4a-8698-44fb-9c29-625630da268c.jpg?itok=hZ9q6Llc', - leeftijd: 39, - naam: 'Alexander Hammelburg', - partij: 'D66', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 477.9220779220779, y: 100 }, - }, - { - id: 'kamerleden/51', - data: { text: 'kamerleden/51' }, - originalPosition: { x: 16, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/425ddb12-0b71-44b6-9875-70b075dc99f7.jpg?itok=txVxBI0l', - leeftijd: 34, - naam: 'Kiki Hagen', - partij: 'D66', - woonplaats: 'Mijdrecht', - }, - scaledPosition: { x: 503.8961038961039, y: 100 }, - }, - { - id: 'kamerleden/47', - data: { text: 'kamerleden/47' }, - originalPosition: { x: 17, y: 0 }, - attributes: { - anc: 1525, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/762e0f40-f100-4896-85a2-b1d8818fae41.jpg?itok=YM5w7UBV', - leeftijd: 53, - naam: 'Tjeerd de Groot', - partij: 'D66', - woonplaats: 'Haarlem', - }, - scaledPosition: { x: 529.8701298701299, y: 100 }, - }, - { - id: 'kamerleden/121', - data: { text: 'kamerleden/121' }, - originalPosition: { x: 18, y: 0 }, - attributes: { - anc: 3171, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/bf030048-0434-42f1-bbed-8b0c04014db4.jpg?itok=G35Rmw4M', - leeftijd: 39, - naam: 'Sjoerd Sjoerdsma', - partij: 'D66', - woonplaats: "'s-Gravenhage", - }, - scaledPosition: { x: 555.8441558441559, y: 100 }, - }, - { - id: 'kamerleden/42', - data: { text: 'kamerleden/42' }, - originalPosition: { x: 19, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/49e7fc50-90f5-4476-9275-c31f88864082.jpg?itok=n7ZLWx8e', - leeftijd: 48, - naam: 'Lisa van Ginneken', - partij: 'D66', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 581.8181818181819, y: 100 }, - }, - { - id: 'kamerleden/22', - data: { text: 'kamerleden/22' }, - originalPosition: { x: 20, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/e85125f3-b7cf-426c-9355-412838893f55.jpg?itok=Qs1_f_yV', - leeftijd: 42, - naam: 'Faissal Boulakjar', - partij: 'D66', - woonplaats: 'Teteringen', - }, - scaledPosition: { x: 607.7922077922078, y: 100 }, - }, - { - id: 'kamerleden/21', - data: { text: 'kamerleden/21' }, - originalPosition: { x: 21, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/13e26587-88d8-440b-bb93-527a8e845342.jpg?itok=18OPNvjY', - leeftijd: 45, - naam: 'Raoul Boucke', - partij: 'D66', - woonplaats: 'Rotterdam', - }, - scaledPosition: { x: 633.7662337662338, y: 100 }, - }, - { - id: 'kamerleden/14', - data: { text: 'kamerleden/14' }, - originalPosition: { x: 22, y: 0 }, - attributes: { - anc: 3171, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/aa0272f6-6e3c-41f7-b040-b774b30d2196.jpg?itok=IQu5H4Bx', - leeftijd: 49, - naam: 'Vera Bergkamp', - partij: 'D66', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 659.7402597402597, y: 100 }, - }, - { - id: 'kamerleden/12', - data: { text: 'kamerleden/12' }, - originalPosition: { x: 23, y: 0 }, - attributes: { - anc: 1948, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/29124ef6-e1d5-4138-bf01-8433afde7269.jpg?itok=qw0TE4jl', - leeftijd: 42, - naam: 'Salima Belhaj', - partij: 'D66', - woonplaats: 'Rotterdam', - }, - scaledPosition: { x: 685.7142857142858, y: 100 }, - }, - ], - selectedAttributeCatecorigal: 'state', - selectedAttributeNumerical: 'long', - scaleCalculation: mockCalc, - colourCalculation: mockCalcColour, - minmaxXAxis: { min: -3.4000000000000004, max: 27.4 }, - minmaxYAxis: { min: -1, max: 1 }, - width: 800, - height: 200, - yOffset: 250, - possibleTitleAttributeValues: [ - 'VVD', - 'D66', - 'GL', - 'PVV', - 'PvdD', - 'PvdA', - 'SGP', - 'BIJ1', - 'CU', - 'BBB', - 'CDA', - 'SP', - 'FVD', - 'DENK', - 'Fractie Den Haan', - 'Volt', - 'JA21', - ], - }, - { - title: 'partij:GL', - nodes: [ - { - id: 'kamerleden/140', - data: { text: 'kamerleden/140' }, - originalPosition: { x: 1, y: 0 }, - attributes: { - anc: 1526, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/c7822b58-103f-4612-87ef-648be97192c6.jpg?itok=jDwKCC26', - leeftijd: 39, - naam: 'Lisa Westerveld', - partij: 'GL', - woonplaats: 'Nijmegen', - }, - scaledPosition: { x: 114.28571428571432, y: 100 }, - }, - { - id: 'kamerleden/89', - data: { text: 'kamerleden/89' }, - originalPosition: { x: 2, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/66b2be3d-10f8-46a4-90c6-b86005df0a50.jpg?itok=obcIgEe-', - leeftijd: 31, - naam: 'Senna Maatoug', - partij: 'GL', - woonplaats: 'Leiden', - }, - scaledPosition: { x: 209.52380952380958, y: 100 }, - }, - { - id: 'kamerleden/124', - data: { text: 'kamerleden/124' }, - originalPosition: { x: 3, y: 0 }, - attributes: { - anc: 1526, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/b50b3f0b-afc8-46e5-bfda-cb60e2935a5a.jpg?itok=7R0cTE10', - leeftijd: 55, - naam: 'Bart Snels', - partij: 'GL', - woonplaats: 'Utrecht', - }, - scaledPosition: { x: 304.7619047619048, y: 100 }, - }, - { - id: 'kamerleden/87', - data: { text: 'kamerleden/87' }, - originalPosition: { x: 4, y: 0 }, - attributes: { - anc: 1526, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/75a44c3a-5914-4c8e-9ca4-d987853f5844.jpg?itok=kA8ZaoNf', - leeftijd: 56, - naam: 'Tom van der Lee', - partij: 'GL', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 400.0000000000001, y: 100 }, - }, - { - id: 'kamerleden/24', - data: { text: 'kamerleden/24' }, - originalPosition: { x: 5, y: 0 }, - attributes: { - anc: 1085, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/ccea26dd-dcae-4174-8945-ec5cb568e951.jpg?itok=z7gsBQYd', - leeftijd: 51, - naam: 'Laura Bromet', - partij: 'GL', - woonplaats: 'Monnickendam', - }, - scaledPosition: { x: 495.23809523809535, y: 100 }, - }, - { - id: 'kamerleden/20', - data: { text: 'kamerleden/20' }, - originalPosition: { x: 6, y: 0 }, - attributes: { - anc: 57, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/dab4cf29-4843-4261-b09b-973fe98dbf65.jpg?itok=szVeNy9I', - leeftijd: 27, - naam: 'Kauthar Bouchallikh', - partij: 'GL', - woonplaats: 'Amsterdam', - }, - scaledPosition: { x: 590.4761904761906, y: 100 }, - }, - { - id: 'kamerleden/34', - data: { text: 'kamerleden/34' }, - originalPosition: { x: 7, y: 0 }, - attributes: { - anc: 1637, - img: 'https://www.tweedekamer.nl/sites/default/files/styles/member_parlement_profile_square/public/551f9a19-738c-4298-afe1-5ca7959ab74b.jpg?itok=24RXpYrd', - leeftijd: 45, - naam: 'Corinne Ellemeet', - partij: 'GL', - woonplaats: 'Abcoude', - }, - scaledPosition: { x: 685.7142857142859, y: 100 }, - }, - ], - selectedAttributeCatecorigal: 'state', - selectedAttributeNumerical: 'long', - scaleCalculation: mockCalc, - colourCalculation: mockCalcColour, - minmaxXAxis: { min: -0.20000000000000018, max: 8.2 }, - minmaxYAxis: { min: -1, max: 1 }, - width: 800, - height: 200, - yOffset: 500, - possibleTitleAttributeValues: [ - 'VVD', - 'D66', - 'GL', - 'PVV', - 'PvdD', - 'PvdA', - 'SGP', - 'BIJ1', - 'CU', - 'BBB', - 'CDA', - 'SP', - 'FVD', - 'DENK', - 'Fractie Den Haan', - 'Volt', - 'JA21', - ], - }, - ]; - - // Check the plots and the relations. - expect(viewModelImpl.plots).toEqual(expectedPlots); - expect(viewModelImpl.allRelations).toEqual(expectedRelations); - - // Also check if notifyViewAboutChanges is called. - expect(onViewModelChangedMock).toHaveBeenCalledTimes(2); - }); - - it('add a plot and delete itonDelete', () => { - const viewModelImpl = new SemanticSubstratesViewModelImpl(); - - const onViewModelChangedMock = jest.fn(); - const baseViewMock = { - onViewModelChanged: onViewModelChangedMock, - }; - viewModelImpl.attachView(baseViewMock); - - viewModelImpl.onDelete(1); - }); - - it('onBrush', () => { - const viewModelImpl = new SemanticSubstratesViewModelImpl(); - viewModelImpl.consumeMessageFromBackend(mockSchema); - viewModelImpl.consumeMessageFromBackend(mockNodeLinkResult); - - const onViewModelChangedMock = jest.fn(); - const baseViewMock = { - onViewModelChanged: onViewModelChangedMock, - }; - viewModelImpl.attachView(baseViewMock); - - let expectedFilters: { x: MinMaxType; y: MinMaxType } = { - x: { min: 30, max: 40 }, - y: { min: 30, max: 40 }, - }; - - viewModelImpl.onBrush(1, { min: 30, max: 40 }, { min: 30, max: 40 }); - - expect(viewModelImpl.filtersPerPlot[1]).toEqual(expectedFilters); - }); - - it('onAxisChanges', () => { - const viewModelImpl = new SemanticSubstratesViewModelImpl(); - viewModelImpl.consumeMessageFromBackend(mockSchema); - viewModelImpl.consumeMessageFromBackend(mockNodeLinkResult); - - const onViewModelChangedMock = jest.fn(); - const baseViewMock = { - onViewModelChanged: onViewModelChangedMock, - }; - viewModelImpl.attachView(baseViewMock); - - const expectedX = 'nieuweXaxis'; - const expectedY = 'nieuweYaxis'; - const expectedX2 = 'evenly spaced'; - const expectedY2 = '# outbound connections'; - - // change labels - viewModelImpl.onAxisLabelChanged(1, 'x', 'nieuweXaxis'); - viewModelImpl.onAxisLabelChanged(1, 'y', 'nieuweYaxis'); - - expect(viewModelImpl.plotSpecifications[1].xAxisAttributeType).toEqual(expectedX); - expect(viewModelImpl.plotSpecifications[1].yAxisAttributeType).toEqual(expectedY); - - // change labels again - viewModelImpl.onAxisLabelChanged(1, 'x', 'evenly spaced'); - viewModelImpl.onAxisLabelChanged(1, 'y', '# outbound connections'); - - expect(viewModelImpl.plotSpecifications[1].xAxis).toEqual(expectedX2); - expect(viewModelImpl.plotSpecifications[1].yAxis).toEqual(expectedY2); - }); - - it('onCheckboxChanges', () => { - const viewModelImpl = new SemanticSubstratesViewModelImpl(); - viewModelImpl.consumeMessageFromBackend(mockSchema); - viewModelImpl.consumeMessageFromBackend(mockNodeLinkResult); - - const onViewModelChangedMock = jest.fn(); - const baseViewMock = { - onViewModelChanged: onViewModelChangedMock, - }; - viewModelImpl.attachView(baseViewMock); - - viewModelImpl.onCheckboxChanged(1, 2, true); - - expect(viewModelImpl.visibleRelations[1][2]).toBeTruthy(); - }); - - it('addPlot', () => { - const viewModelImpl = new SemanticSubstratesViewModelImpl(); - viewModelImpl.consumeMessageFromBackend(mockSchema); - viewModelImpl.consumeMessageFromBackend(mockNodeLinkResult); - - const onViewModelChangedMock = jest.fn(); - const baseViewMock = { - onViewModelChanged: onViewModelChangedMock, - }; - viewModelImpl.attachView(baseViewMock); - - expect(viewModelImpl.plots).toHaveLength(3); - - viewModelImpl.addPlot('partij', 'naam', 'VVD'); - - expect(viewModelImpl.plots).toHaveLength(4); - }); -}); - -const deletePlot = [ - { - title: 'partij:VVD', - nodes: [ - { - id: 'kamerleden/148', - data: { - text: 'kamerleden/148', - }, - originalPosition: { - x: 1, - y: 0, - }, - scaledPosition: { - x: 114.28571428571432, - y: 100, - }, - }, - ], - }, -]; - -const expectedRelations = [ - [[], [], []], - [[], [], []], - [[], [], []], -]; diff --git a/libs/shared/lib/vis/semanticsubstrates/SemanticSubstratesViewModel.tsx b/libs/shared/lib/vis/semanticsubstrates/SemanticSubstratesViewModel.tsx deleted file mode 100644 index 04ea7bf0c..000000000 --- a/libs/shared/lib/vis/semanticsubstrates/SemanticSubstratesViewModel.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/** - * This program has been developed by students from the bachelor Computer Science at - * Utrecht University within the Software Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ -import { - MinMaxType, - PlotType, - RelationType, - AxisLabel, - PlotSpecifications, - EntitiesFromSchema, -} from './Types'; -import CalcScaledPosUseCase from './utils/CalcScaledPositionsUseCase'; -import FilterUseCase from './utils/FilterUseCase'; - -import CalcDefaultPlotSpecsUseCase from './utils/CalcDefaultPlotSpecsUseCase'; -import ToPlotDataParserUseCase from './utils/ToPlotDataParserUseCase'; -import CalcXYMinMaxUseCase from './utils/CalcXYMinMaxUseCase'; - -import { ClassNameMap } from '@mui/material'; -import { NodeLinkResultType, isNodeLinkResult } from '../shared/ResultNodeLinkParserUseCase'; -import CalcEntityAttrNamesFromSchemaUseCase from './utils/CalcEntityAttrNamesFromSchemaUseCase'; -import CalcEntityAttrNamesFromResultUseCase from './utils/CalcEntityAttrNamesFromResultUseCase'; -import { isSchemaResult } from '../shared/SchemaResultType'; - -/** An implementation of the `SemanticSubstratesViewModel` to be used by the semantic-substrates view. */ -export default class SemanticSubstratesViewModel { - /** - * These functions are mock function for now, but can be properly implemented later down the line. - */ - public scaleCalculation: (x: number) => number = (x: number) => { - return 3; - }; - public colourCalculation: (x: string) => string = (x: string) => { - return '#d56a50'; - }; - public relationColourCalculation: (x: string) => string = (x: string) => { - return '#d49350'; - }; - private relationScaleCalculation: (x: number) => number = (x: number) => { - return 1; - }; - - /** Initializes the plots and relations arrays as empty arrays. */ - public constructor(state: SemanticSubstratesState, setState) { - this.state = state; - this.setState = setState; - } - - - - - - // /** - // * Subscribe to the schema result routing key so that this view model is receiving results that are to be visualised. - // */ - // public subscribeToSchemaResult(): void { - // Broker.instance().subscribe(this, 'schema_result'); - // } - - // /** - // * Unsubscribe from the schema result routing key so that this view model no longer handles query results from the backend. - // */ - // public unSubscribeFromSchemaResult(): void { - // Broker.instance().unSubscribe(this, 'schema_result'); - // } -} diff --git a/libs/shared/lib/vis/semanticsubstrates/Types.tsx b/libs/shared/lib/vis/semanticsubstrates/Types.tsx index 28691f3ce..226319c65 100644 --- a/libs/shared/lib/vis/semanticsubstrates/Types.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/Types.tsx @@ -4,7 +4,7 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { XYPosition } from "reactflow"; +import { XYPosition } from 'reactflow'; /** Stores the minimum and maximum of the data. */ export type MinMaxType = { diff --git a/libs/shared/lib/vis/semanticsubstrates/configpanel/SemanticSubstrateConfigPanel.tsx b/libs/shared/lib/vis/semanticsubstrates/configpanel/SemanticSubstrateConfigPanel.tsx index 8270164c6..1f680d78a 100644 --- a/libs/shared/lib/vis/semanticsubstrates/configpanel/SemanticSubstrateConfigPanel.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/configpanel/SemanticSubstrateConfigPanel.tsx @@ -9,18 +9,14 @@ * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ import React, { useRef, useState } from 'react'; -import styles from '../SemanticSubstratesComponent.module.scss'; -import { - EntityWithAttributes, - FSSConfigPanelProps, -} from './Types'; +import styles from '../semanticsubstrates.module.scss'; +import { EntityWithAttributes, FSSConfigPanelProps } from './Types'; import SemanticSubstratesConfigPanelViewModelImpl from './SemanticSubstratesConfigPanelViewModel'; import SemanticSubstratesConfigPanelViewModel from './SemanticSubstratesConfigPanelViewModel'; /** Component for rendering config input fields for Faceted Semantic Subrate attributes. */ export default function FSSConfigPanel(props: FSSConfigPanelProps) { - let FSSConfigPanelViewModel: SemanticSubstratesConfigPanelViewModel = - new SemanticSubstratesConfigPanelViewModelImpl(props); + let FSSConfigPanelViewModel: SemanticSubstratesConfigPanelViewModel = new SemanticSubstratesConfigPanelViewModelImpl(props); FSSConfigPanelViewModel.makeNodeTypes(); FSSConfigPanelViewModel.makeRelationTypes(); diff --git a/libs/shared/lib/vis/semanticsubstrates/configpanel/SemanticSubstratesConfigPanelViewModel.tsx b/libs/shared/lib/vis/semanticsubstrates/configpanel/SemanticSubstratesConfigPanelViewModel.tsx index 4f257a6af..9e0d1fce2 100644 --- a/libs/shared/lib/vis/semanticsubstrates/configpanel/SemanticSubstratesConfigPanelViewModel.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/configpanel/SemanticSubstratesConfigPanelViewModel.tsx @@ -1,11 +1,7 @@ import { range } from 'd3'; -import { - EntityWithAttributes, - FSSConfigPanelProps, -} from './Types'; -import { Link, Node } from '../../shared/ResultNodeLinkParserUseCase'; -import SemanticSubstratesViewModel from '../SemanticSubstratesViewModel'; +import { EntityWithAttributes, FSSConfigPanelProps } from './Types'; +import { Edge, Node } from '@graphpolaris/shared/lib/data-access'; /** Viewmodel for rendering config input fields for Faceted Semantic Substrate attributes. */ export default class SemanticSubstratesConfigPanelViewModel { @@ -19,13 +15,12 @@ export default class SemanticSubstratesConfigPanelViewModel { maximalRelationWidth = 4; nodesloaded: Node[]; - relationsloaded: Link[]; + relationsloaded: Edge[]; public currentNode = ''; currentRelation = ''; currentAttribute = ''; currentRelationAttribute = ''; - // Faceted Semantic Substrates View Model. - fssViewModel: SemanticSubstratesViewModel; + /** * The constructor for the FSSConfigPanelViewModelImpl (FacetedSemanticSubstratesConfigPanelViewModelImpl). * This handles the view for changing how to display the attributes of the nodes and relations @@ -35,21 +30,19 @@ export default class SemanticSubstratesConfigPanelViewModel { */ public constructor(props: FSSConfigPanelProps) { let graph = props.graph; - let fssViewModel = props.fssViewModel; this.nodes = []; this.relations = []; this.nodesloaded = graph.nodes; this.relationsloaded = graph.edges; - this.fssViewModel = fssViewModel; } /** * Creates a list of unique node types based on the current list of nodes. */ public makeNodeTypes() { - this.nodes = MakeTypesFromGraphUseCase.makeNodeTypes(this.nodesloaded); + // this.nodes = MakeTypesFromGraphUseCase.makeNodeTypes(this.nodesloaded); if (!this.isNodeSet()) { if (this.nodes[0]) { this.currentNode = this.nodes[0].name; @@ -61,7 +54,7 @@ export default class SemanticSubstratesConfigPanelViewModel { * Creates a list of unique relation types based on the current list of relations. */ public makeRelationTypes() { - this.relations = MakeTypesFromGraphUseCase.makeRelationTypes(this.relationsloaded); + // this.relations = MakeTypesFromGraphUseCase.makeRelationTypes(this.relationsloaded); if (!this.isRelationSet()) { if (this.relations[0]) { this.currentRelation = this.relations[0].name; @@ -113,19 +106,13 @@ export default class SemanticSubstratesConfigPanelViewModel { //Retrieve the current visualisation and set the vis dropdown to this. this.currentAttribute = attributeSelected; if (this.isNodeAttributeNumber(attributeSelected)) { - this.fssViewModel.selectedAttributeNumerical = attributeSelected; - this.fssViewModel.changeSelectedAttributeNumerical( - this.getTheScaleCalculationForNodes( - attributeSelected, - this.minimalNodeSize, - this.maximalNodeSize, - ), - ); + // this.fssViewModel.selectedAttributeNumerical = attributeSelected; + // this.fssViewModel.changeSelectedAttributeNumerical( + // this.getTheScaleCalculationForNodes(attributeSelected, this.minimalNodeSize, this.maximalNodeSize) + // ); } else { - this.fssViewModel.selectedAttributeCatecorigal = attributeSelected; - this.fssViewModel.changeSelectedAttributeCatecorigal( - this.getTheColourCalculationForNodes(attributeSelected), - ); + // this.fssViewModel.selectedAttributeCatecorigal = attributeSelected; + // this.fssViewModel.changeSelectedAttributeCatecorigal(this.getTheColourCalculationForNodes(attributeSelected)); } } @@ -136,21 +123,19 @@ export default class SemanticSubstratesConfigPanelViewModel { //Retrieve the current visualisation and set the vis dropdown to this. this.currentRelationAttribute = attributeSelected; if (this.isRelationAttributeNumber(attributeSelected)) { - this.fssViewModel.relationSelectedAttributeNumerical = attributeSelected; - this.fssViewModel.changeRelationSelectedAttributeNumerical( - this.getTheScaleCalculationForRelations( - this.fssViewModel.relationSelectedAttributeNumerical, - this.minimalRelationWidth, - this.maximalRelationWidth, - ), - ); + // this.fssViewModel.relationSelectedAttributeNumerical = attributeSelected; + // this.fssViewModel.changeRelationSelectedAttributeNumerical( + // this.getTheScaleCalculationForRelations( + // this.fssViewModel.relationSelectedAttributeNumerical, + // this.minimalRelationWidth, + // this.maximalRelationWidth + // ) + // ); } else { - this.fssViewModel.relationSelectedAttributeCatecorigal = attributeSelected; - this.fssViewModel.changeRelationSelectedAttributeCatecorigal( - this.getTheColourCalculationForRelations( - this.fssViewModel.relationSelectedAttributeCatecorigal, - ), - ); + // this.fssViewModel.relationSelectedAttributeCatecorigal = attributeSelected; + // this.fssViewModel.changeRelationSelectedAttributeCatecorigal( + // this.getTheColourCalculationForRelations(this.fssViewModel.relationSelectedAttributeCatecorigal) + // ); } } @@ -201,7 +186,7 @@ export default class SemanticSubstratesConfigPanelViewModel { for (const node of this.nodesloaded) { if (node.attributes) { if (node.attributes[attribute] != undefined) { - if (isNaN(node.attributes[attribute])) return false; + // if (isNaN(node.attributes[attribute])) return false; } } } @@ -218,7 +203,7 @@ export default class SemanticSubstratesConfigPanelViewModel { for (const relation of this.relationsloaded) { if (relation.attributes) { if (relation.attributes[attribute] != undefined) { - if (isNaN(relation.attributes[attribute])) return false; + // if (isNaN(relation.attributes[attribute])) return false; } } } @@ -232,16 +217,12 @@ export default class SemanticSubstratesConfigPanelViewModel { * @param maxP Maximum node size. * @returns Scaling value as a number. */ - public getTheScaleCalculationForNodes( - attribute: string, - minP: number, - maxP: number, - ): (x: number) => number { + public getTheScaleCalculationForNodes(attribute: string, minP: number, maxP: number): (x: number) => number { let values: number[] = []; this.nodesloaded.forEach((node) => { if (node.attributes) { if (node.attributes[attribute] != undefined) { - values.push(node.attributes[attribute]); + // values.push(node.attributes[attribute]); } } }); @@ -267,22 +248,22 @@ export default class SemanticSubstratesConfigPanelViewModel { this.nodesloaded.forEach((node) => { if (node.attributes) { if (node.attributes[attribute] != undefined) { - uniqueValues.push(node.attributes[attribute]); + // uniqueValues.push(node.attributes[attribute]); } } }); - let colours = ColourPalettes['default'].nodes; + // let colours = ColourPalettes['default'].nodes; // Create the key value pairs. let valueToColour: Record<string, string> = {}; let i = 0; uniqueValues.forEach((uniqueValue) => { - valueToColour[uniqueValue] = '#' + colours[i]; - i++; - if (i > colours.length) { - i = 0; - } + // valueToColour[uniqueValue] = '#' + colours[i]; + // i++; + // if (i > colours.length) { + // i = 0; + // } }); //Get a colour for each attribute @@ -296,16 +277,12 @@ export default class SemanticSubstratesConfigPanelViewModel { * @param maxP Maximum width * @returns */ - public getTheScaleCalculationForRelations( - attribute: string, - minP: number, - maxP: number, - ): (x: number) => number { + public getTheScaleCalculationForRelations(attribute: string, minP: number, maxP: number): (x: number) => number { let values: number[] = []; this.relationsloaded.forEach((relation) => { if (relation.attributes) { if (relation.attributes[attribute] != undefined) { - values.push(relation.attributes[attribute]); + // values.push(relation.attributes[attribute]); } } }); @@ -331,22 +308,22 @@ export default class SemanticSubstratesConfigPanelViewModel { this.relationsloaded.forEach((relation) => { if (relation.attributes) { if (relation.attributes[attribute] != undefined) { - uniqueValues.push(relation.attributes[attribute]); + // uniqueValues.push(relation.attributes[attribute]); } } }); - let colours = ColourPalettes['default'].elements.relation; + // let colours = ColourPalettes['default'].elements.relation; // Create the key value pairs. let valueToColour: Record<string, string> = {}; let i = 0; uniqueValues.forEach((uniqueValue) => { - valueToColour[uniqueValue] = '#' + colours[i]; - i++; - if (i > colours.length) { - i = 0; - } + // valueToColour[uniqueValue] = '#' + colours[i]; + // i++; + // if (i > colours.length) { + // i = 0; + // } }); //Get a colour for each attribute diff --git a/libs/shared/lib/vis/semanticsubstrates/configpanel/Types.tsx b/libs/shared/lib/vis/semanticsubstrates/configpanel/Types.tsx index 3a7462d70..58e7f4dcc 100644 --- a/libs/shared/lib/vis/semanticsubstrates/configpanel/Types.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/configpanel/Types.tsx @@ -4,8 +4,7 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { NodeLinkResultType } from "../../shared/ResultNodeLinkParserUseCase"; -import SemanticSubstratesViewModel from "../SemanticSubstratesViewModel"; +import { GraphQueryResult } from '@graphpolaris/shared/lib/data-access'; /* An entity that has an attribute (Either a node with attributes or an edges with attributes) For the config-panel of semantic-substrates.*/ @@ -17,7 +16,6 @@ export type EntityWithAttributes = { /** Props for this component */ export type FSSConfigPanelProps = { - graph: NodeLinkResultType; + graph: GraphQueryResult; // currentColours: any; - fssViewModel: SemanticSubstratesViewModel; }; diff --git a/libs/shared/lib/vis/semanticsubstrates/index.ts b/libs/shared/lib/vis/semanticsubstrates/index.ts index 2347875d7..7b24906f9 100644 --- a/libs/shared/lib/vis/semanticsubstrates/index.ts +++ b/libs/shared/lib/vis/semanticsubstrates/index.ts @@ -1 +1 @@ -export * from './semanticsubstrates'; \ No newline at end of file +export * from './semanticsubstrates'; diff --git a/libs/shared/lib/vis/semanticsubstrates/semanticsubstrates.stories.tsx b/libs/shared/lib/vis/semanticsubstrates/semanticsubstrates.stories.tsx index 49af3a0c2..448b1edf9 100644 --- a/libs/shared/lib/vis/semanticsubstrates/semanticsubstrates.stories.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/semanticsubstrates.stories.tsx @@ -1,24 +1,20 @@ -import { - assignNewGraphQueryResult, - colorPaletteConfigSlice, - graphQueryResultSlice, - schemaSlice, - setSchema, -} from "../../data-access/store"; -import { GraphPolarisThemeProvider } from "../../data-access/theme"; -import { configureStore } from "@reduxjs/toolkit"; -import { Meta, ComponentStory } from "@storybook/react"; -import { Provider } from "react-redux"; +import { assignNewGraphQueryResult, colorPaletteConfigSlice, graphQueryResultSlice, schemaSlice, setSchema } from '../../data-access/store'; +import { GraphPolarisThemeProvider } from '../../data-access/theme'; +import { configureStore } from '@reduxjs/toolkit'; +import { Meta, ComponentStory } from '@storybook/react'; +import { Provider } from 'react-redux'; -import SemanticSubstrates from "./semanticsubstrates"; -import { SchemaUtils } from "../../schema/schema-utils"; +import SemanticSubstrates from './semanticsubstrates'; +import { SchemaUtils } from '../../schema/schema-utils'; +import { simpleSchemaAirportRaw } from '../../mock-data/schema/simpleAirportRaw'; +import { bigMockQueryResults } from '../../mock-data'; const Component: Meta<typeof SemanticSubstrates> = { /* 👇 The title prop is optional. * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ - title: "Components/Visualizations/SemanticSubstrates", + title: 'Components/Visualizations/SemanticSubstrates', component: SemanticSubstrates, decorators: [ (story) => ( @@ -52,7 +48,11 @@ export const TestWithData = { ], edges: [ { - name: '12', from: '1', to: '1', collection: '1c', attributes: [{ name: 'a', type: 'string' }], + name: '12', + from: '1', + to: '1', + collection: '1c', + attributes: [{ name: 'a', type: 'string' }], }, ], }); @@ -60,21 +60,40 @@ export const TestWithData = { dispatch(setSchema(schema.export())); dispatch( assignNewGraphQueryResult({ - nodes: [ - { id: '1/a', attributes: { a: 's1' } }, - { id: '1/b1', attributes: { a: 's1' } }, - { id: '1/b2', attributes: { a: 's1' } }, - { id: '1/b3', attributes: { a: 's1' } }, - ], - edges: [ - { id: '12/z1', from: '1/b1', to: '1/a', attributes: { a: 's1' } }, - // { from: 'b2', to: 'a', attributes: {} }, - // { from: 'b3', to: 'a', attributes: {} }, - { id: '12/z1', from: '1/a', to: '1/b1', attributes: { a: 's1' } }, - ], + type: 'nodelink', + payload: { + nodes: [ + { id: '1/a', attributes: { a: 's1' } }, + { id: '1/b1', attributes: { a: 's1' } }, + { id: '1/b2', attributes: { a: 's1' } }, + { id: '1/b3', attributes: { a: 's1' } }, + ], + edges: [ + { id: '12/z1', from: '1/b1', to: '1/a', attributes: { a: 's1' } }, + // { from: 'b2', to: 'a', attributes: {} }, + // { from: 'b3', to: 'a', attributes: {} }, + { id: '12/z1', from: '1/a', to: '1/b1', attributes: { a: 's1' } }, + ], + }, }) - ) - } -} + ); + }, +}; + +export const TestWithAirport = { + args: { + loading: false, + rowHeight: 30, + hyperedgeColumnWidth: 30, + gapBetweenRanges: 3, + }, + play: async () => { + const dispatch = Mockstore.dispatch; + const schema = SchemaUtils.schemaBackend2Graphology(simpleSchemaAirportRaw); + + dispatch(setSchema(schema.export())); + dispatch(assignNewGraphQueryResult({ type: 'nodelink', payload: bigMockQueryResults })); + }, +}; export default Component; diff --git a/libs/shared/lib/vis/semanticsubstrates/semanticsubstrates.tsx b/libs/shared/lib/vis/semanticsubstrates/semanticsubstrates.tsx index f5149f537..b05dcb58c 100644 --- a/libs/shared/lib/vis/semanticsubstrates/semanticsubstrates.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/semanticsubstrates.tsx @@ -1,10 +1,8 @@ import { useTheme } from '@mui/material'; -import { - useAppDispatch, useGraphQueryResult, useSchemaGraph, -} from '@graphpolaris/shared/lib/data-access/store'; +import { useAppDispatch, useGraphQueryResult, useSchemaGraph } from '@graphpolaris/shared/lib/data-access/store'; import { useEffect, useRef, useState } from 'react'; import { AxisLabel, EntitiesFromSchema, MinMaxType, PlotSpecifications, PlotType, RelationType } from './Types'; -import { NodeLinkResultType, isNodeLinkResult } from '../shared/ResultNodeLinkParserUseCase'; +import { isNodeLinkResult } from '../shared/ResultNodeLinkParserUseCase'; import styles from './semanticsubstrates.module.scss'; import AddPlotButtonComponent from './subcomponents/AddPlotButtonComponent'; import SVGCheckboxesWithSemanticSubstrLabel from './subcomponents/SVGCheckBoxComponent'; @@ -20,7 +18,7 @@ import CalcDefaultPlotSpecsUseCase from './utils/CalcDefaultPlotSpecsUseCase'; import ToPlotDataParserUseCase from './utils/ToPlotDataParserUseCase'; import CalcXYMinMaxUseCase from './utils/CalcXYMinMaxUseCase'; import CalcScaledPosUseCase from './utils/CalcScaledPositionsUseCase'; -import { useImmer } from "use-immer"; +import { useImmer } from 'use-immer'; import FilterUseCase from './utils/FilterUseCase'; export type SemanticSubstrateState = { @@ -37,14 +35,14 @@ export type SemanticSubstrateState = { selectedAttributeCatecorigal: string; relationSelectedAttributeNumerical: string; relationSelectedAttributeCatecorigal: string; - scaleCalculation: (x: number) => number, - colourCalculation: (x: string) => string, + scaleCalculation: (x: number) => number; + colourCalculation: (x: string) => string; - relationScaleCalculation: (x: number) => number, - relationColourCalculation: (x: string) => string, + relationScaleCalculation: (x: number) => number; + relationColourCalculation: (x: string) => string; addPlotButtonAnchor: (EventTarget & SVGRectElement) | null; -} +}; export type SemanticSubstratePlotState = { plots: PlotType[]; @@ -59,7 +57,7 @@ export type SemanticSubstratePlotState = { // Used for filtering the relations when brushing (brush is the square selection tool) filtersPerPlot: { x: MinMaxType; y: MinMaxType }[]; -} +}; /** * These functions are mock function for now, but can be properly implemented later down the line. @@ -77,7 +75,7 @@ const relationScaleCalculation: (x: number) => number = (x: number) => { return 1; }; -const SemanticSubstrates = () => { +export const SemanticSubstrates = () => { const dispatch = useAppDispatch(); const theme = useTheme(); const graphQueryResult = useGraphQueryResult(); @@ -87,8 +85,10 @@ const SemanticSubstrates = () => { const [state, setState] = useImmer<SemanticSubstrateState>({ plotSpecifications: [], entitiesFromSchemaPruned: { entityNames: [], attributesPerEntity: {} }, - selectedAttributeNumerical: 'long', selectedAttributeCatecorigal: 'state', - relationSelectedAttributeNumerical: 'Distance', relationSelectedAttributeCatecorigal: 'Day', + selectedAttributeNumerical: 'long', + selectedAttributeCatecorigal: 'state', + relationSelectedAttributeNumerical: 'Distance', + relationSelectedAttributeCatecorigal: 'Day', nodeRadius: 3, nodeColors: ['#D56A50', '#1E9797', '#d49350', '#1e974a', '#D49350'], gapBetweenPlots: 50, @@ -101,7 +101,11 @@ const SemanticSubstrates = () => { }); const [plotState, setPlotState] = useImmer<SemanticSubstratePlotState>({ - allRelations: [], filteredRelations: [], plots: [], filtersPerPlot: [], visibleRelations: [], + allRelations: [], + filteredRelations: [], + plots: [], + filtersPerPlot: [], + visibleRelations: [], }); useEffect(() => { @@ -120,7 +124,7 @@ const SemanticSubstrates = () => { setState((draft) => { draft.entitiesFromSchemaPruned = CalcEntityAttrNamesFromResultUseCase.CalcEntityAttrNamesFromResult( graphQueryResult, - entitiesFromSchema, + entitiesFromSchema ); // Generate default plots, if there aren't any currently @@ -129,18 +133,17 @@ const SemanticSubstrates = () => { } return draft; - }) - + }); } else { - console.error('Invalid query result!') + console.error('Invalid query result!'); } applyNewPlotSpecifications(); }, [graphQueryResult, entitiesFromSchema]); - useEffect(() => { applyNewPlotSpecifications(); }, [state]); - - + useEffect(() => { + applyNewPlotSpecifications(); + }, [state]); /** * Update the scaling of the nodes after a new attribute has been selected. This function is called when the new attribute has numbers as values. @@ -180,7 +183,6 @@ const SemanticSubstrates = () => { * @param {NodeLinkResultType} queryResult The query result to apply the plot specs to. */ function applyNewPlotSpecifications(): void { - // Parse the incoming data to plotdata with the auto generated plot specifications const { plots, relations } = ToPlotDataParserUseCase.parseQueryResult( graphQueryResult, @@ -188,7 +190,7 @@ const SemanticSubstrates = () => { state.relationSelectedAttributeNumerical, state.relationSelectedAttributeCatecorigal, state.relationScaleCalculation, - state.relationColourCalculation, + state.relationColourCalculation ); setPlotState((draft) => { @@ -203,19 +205,13 @@ const SemanticSubstrates = () => { const scaledPositions = CalcScaledPosUseCase.calculate(plot, minmaxAxis.x, minmaxAxis.y); // Collect all possible values for a certain attribute, used for the autocomplete title field - const possibleTitleAttributeValues = graphQueryResult.nodes.reduce( - (values: Set<string>, node) => { - // Filter on nodes which are of the entity type in the plotspec - // Use a Set so we only collect unique values - if ( - node.id.split('/')[0] == state.plotSpecifications[i].entity && - state.plotSpecifications[i].labelAttributeType in node.attributes - ) - values.add(node.attributes[state.plotSpecifications[i].labelAttributeType] as string); - return values; - }, - new Set<string>(), - ); + const possibleTitleAttributeValues = graphQueryResult.nodes.reduce((values: Set<string>, node) => { + // Filter on nodes which are of the entity type in the plotspec + // Use a Set so we only collect unique values + if (node.label == state.plotSpecifications[i].entity && state.plotSpecifications[i].labelAttributeType in node.attributes) + values.add(node.attributes[state.plotSpecifications[i].labelAttributeType] as string); + return values; + }, new Set<string>()); // Accumulate the yOffset for each plot, with the width and gapbetweenplots const thisYOffset = yOffset; @@ -248,9 +244,7 @@ const SemanticSubstrates = () => { // Initialize the visible relations with all false values if (draft.visibleRelations.length !== draft.plots.length) { - draft.visibleRelations = new Array(draft.plots.length) - .fill([]) - .map(() => new Array(draft.plots.length).fill(false)) + draft.visibleRelations = new Array(draft.plots.length).fill([]).map(() => new Array(draft.plots.length).fill(false)); } return draft; @@ -285,13 +279,7 @@ const SemanticSubstrates = () => { }; // Apply the new filter to the relations - FilterUseCase.filterRelations( - draft.allRelations, - draft.filteredRelations, - draft.plots, - draft.filtersPerPlot, - plotIndex, - ); + FilterUseCase.filterRelations(draft.allRelations, draft.filteredRelations, draft.plots, draft.filtersPerPlot, plotIndex); return draft; }); } @@ -349,7 +337,7 @@ const SemanticSubstrates = () => { draft.visibleRelations.splice(plotIndex, 1); draft.visibleRelations = draft.visibleRelations.map((r) => r.filter((_, i) => i != plotIndex)); - return draft + return draft; }); } @@ -378,7 +366,6 @@ const SemanticSubstrates = () => { } return draft; - }); } @@ -389,26 +376,17 @@ const SemanticSubstrates = () => { * @param {string} attrName The new attribute name for the plot filter, might be unchanged. * @param {string} attrValue The new attribute value for the plot filter. */ - function onPlotTitleChanged( - plotIndex: number, - entity: string, - attrName: string, - attrValue: string, - ): void { + function onPlotTitleChanged(plotIndex: number, entity: string, attrName: string, attrValue: string): void { // If the entity or the attrName changed, auto select a default attrValue if ( - (entity != state.plotSpecifications[plotIndex].entity || - attrName != state.plotSpecifications[plotIndex].labelAttributeType) && + (entity != state.plotSpecifications[plotIndex].entity || attrName != state.plotSpecifications[plotIndex].labelAttributeType) && attrValue == state.plotSpecifications[plotIndex].labelAttributeValue ) { - const firstValidNode = graphQueryResult.nodes.find( - (node) => node.id.split('/')[0] == entity && attrName in node.attributes, - ); + const firstValidNode = graphQueryResult.nodes.find((node) => node.label == entity && attrName in node.attributes); if (firstValidNode != undefined) attrValue = firstValidNode.attributes[attrName] as string; } setState((draft) => { - draft.plotSpecifications[plotIndex] = { ...draft.plotSpecifications[plotIndex], entity, @@ -418,8 +396,7 @@ const SemanticSubstrates = () => { return draft; }); - }; - + } const plotElements: JSX.Element[] = []; for (let i = 0; i < plotState.plots.length; i++) { @@ -444,13 +421,9 @@ const SemanticSubstrates = () => { onBrush(i, xRange, yRange) } onDelete={() => onDelete(i)} - onAxisLabelChanged={(axis: 'x' | 'y', value: string) => - onAxisLabelChanged(i, axis, value) - } - onTitleChanged={(entity: string, attrName: string, attrValue: string) => - onPlotTitleChanged(i, entity, attrName, attrValue) - } - />, + onAxisLabelChanged={(axis: 'x' | 'y', value: string) => onAxisLabelChanged(i, axis, value)} + onTitleChanged={(entity: string, attrName: string, attrValue: string) => onPlotTitleChanged(i, entity, attrName, attrValue)} + /> ); } @@ -470,26 +443,25 @@ const SemanticSubstrates = () => { relations={plotState.filteredRelations[i][j]} color={color.hex()} nodeRadius={state.nodeRadius} - />, + /> ); } } } - useEffect(() => { }, [state]); + useEffect(() => {}, [state]); - const heightOfAllPlots = plotState.plots.length > 0 - ? plotState.plots[plotState.plots.length - 1].yOffset + plotState.plots[plotState.plots.length - 1].height + 50 - : 0; + const heightOfAllPlots = + plotState.plots.length > 0 + ? plotState.plots[plotState.plots.length - 1].yOffset + plotState.plots[plotState.plots.length - 1].height + 50 + : 0; return ( <div className={styles.container}> <div style={{ width: '100%', height: '100%' }}> <svg style={{ width: '100%', height: heightOfAllPlots + 60 }}> <AddPlotButtonComponent x={750} - onClick={(event: React.MouseEvent<SVGRectElement>) => - setState({ ...state, addPlotButtonAnchor: event.currentTarget }) - } + onClick={(event: React.MouseEvent<SVGRectElement>) => setState({ ...state, addPlotButtonAnchor: event.currentTarget })} /> <g transform={'translate(60,60)'}> <SVGCheckboxesWithSemanticSubstrLabel @@ -497,9 +469,7 @@ const SemanticSubstrates = () => { relations={plotState.allRelations} visibleRelations={plotState.visibleRelations} nodeColors={state.nodeColors} - onCheckboxChanged={(fromPlot: number, toPlot: number, value: boolean) => - onCheckboxChanged(fromPlot, toPlot, value) - } + onCheckboxChanged={(fromPlot: number, toPlot: number, value: boolean) => onCheckboxChanged(fromPlot, toPlot, value)} /> {plotElements} {linesBetween}, @@ -512,9 +482,7 @@ const SemanticSubstrates = () => { entitiesFromSchema={entitiesFromSchema} nodeLinkResultNodes={graphQueryResult.nodes} handleClose={() => setState({ ...state, addPlotButtonAnchor: null })} - addPlot={(entity: string, attributeName: string, attributeValue: string) => - addPlot(entity, attributeName, attributeValue) - } + addPlot={(entity: string, attributeName: string, attributeValue: string) => addPlot(entity, attributeName, attributeValue)} /> <VisConfigPanelComponent> {/* <FSSConfigPanel diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.module.scss b/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.module.scss index 72574e223..4ab1febb1 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.module.scss +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.module.scss @@ -1,63 +1,63 @@ -root { - &:hover { - & .display { - & .background { - fill: '#009100'; - transition-delay: 0s; - } - & .plus { - transform: translate(-4px, 0); - transition-delay: 0s; - - & line { - transition: 0.2s; - transition-delay: 0s; - stroke-width: 3; - } - & .xAxis { - transform: translate(0, 0) scale(1, 1); - } - & .yAxis { - transform: translate(0, 0); - } - & .nodes { - opacity: 0; - transition-delay: 0s; - } - } - } - } -} - -.background { - transition: 0.1s; - transition-delay: 0.1s; -} - -.plus { - transform-box: fill-box; - transform-origin: center center; - transition: 0.2s; - transition-delay: 0.1s; - transform: translate(0, 0); - - & line { - transition: 0.2s; - transition-delay: 0.1s; - stroke-width: 1; - } - - & .nodes { - transition-delay: 0.1s; - } -} - -.xAxis { - transform-box: fill-box; - transform-origin: center right; - transform: translate(0, 5px) scale(1.4, 1); -} - -.yAxis { - transform: translate(-11px, 0); -} +.root { + &:hover { + & .display { + & .background { + fill: '#009100'; + transition-delay: 0s; + } + & .plus { + transform: translate(-4px, 0); + transition-delay: 0s; + + & line { + transition: 0.2s; + transition-delay: 0s; + stroke-width: 3; + } + & .xAxis { + transform: translate(0, 0) scale(1, 1); + } + & .yAxis { + transform: translate(0, 0); + } + & .nodes { + opacity: 0; + transition-delay: 0s; + } + } + } + } +} + +.background { + transition: 0.1s; + transition-delay: 0.1s; +} + +.plus { + transform-box: fill-box; + transform-origin: center center; + transition: 0.2s; + transition-delay: 0.1s; + transform: translate(0, 0); + + & line { + transition: 0.2s; + transition-delay: 0.1s; + stroke-width: 1; + } + + & .nodes { + transition-delay: 0.1s; + } +} + +.xAxis { + transform-box: fill-box; + transform-origin: center right; + transform: translate(0, 5px) scale(1.4, 1); +} + +.yAxis { + transform: translate(-11px, 0); +} diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.module.scss.d.ts b/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.module.scss.d.ts index 0d249f122..022b03b83 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.module.scss.d.ts +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.module.scss.d.ts @@ -1,4 +1,5 @@ declare const classNames: { + readonly root: 'root'; readonly display: 'display'; readonly background: 'background'; readonly plus: 'plus'; diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.tsx index d26288f8b..8700ebc2f 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotButtonComponent.tsx @@ -16,47 +16,18 @@ import styles from './AddPlotButtonComponent.module.scss'; * Contains a component that renders the "add plot" button with SVG elements for semantic substrates. * @param props The x and y, and an on click event function. */ -export default function AddPlotButtonComponent(props: { - x?: number; - y?: number; - onClick(event: React.MouseEvent<SVGRectElement>): void; -}) { - +export default function AddPlotButtonComponent(props: { x?: number; y?: number; onClick(event: React.MouseEvent<SVGRectElement>): void }) { return ( <g transform={`translate(${props.x || 0},${props.y || 0})`} className={styles.root}> <g className={'display'}> - <rect - className={styles.background} - x="5" - y="1" - width="108" - height="29" - rx="3" - fill="green" - /> + <rect className={styles.background} x="5" y="1" width="108" height="29" rx="3" fill="green" /> <path d="M20.986 18.264H17.318L16.73 20H14.224L17.78 10.172H20.552L24.108 20H21.574L20.986 18.264ZM20.37 16.416L19.152 12.818L17.948 16.416H20.37ZM24.7143 16.08C24.7143 15.2773 24.8636 14.5727 25.1623 13.966C25.4703 13.3593 25.8856 12.8927 26.4083 12.566C26.9309 12.2393 27.5143 12.076 28.1583 12.076C28.6716 12.076 29.1383 12.1833 29.5583 12.398C29.9876 12.6127 30.3236 12.902 30.5663 13.266V9.64H32.9603V20H30.5663V18.88C30.3423 19.2533 30.0203 19.552 29.6003 19.776C29.1896 20 28.7089 20.112 28.1583 20.112C27.5143 20.112 26.9309 19.9487 26.4083 19.622C25.8856 19.286 25.4703 18.8147 25.1623 18.208C24.8636 17.592 24.7143 16.8827 24.7143 16.08ZM30.5663 16.094C30.5663 15.4967 30.3983 15.0253 30.0623 14.68C29.7356 14.3347 29.3343 14.162 28.8583 14.162C28.3823 14.162 27.9763 14.3347 27.6403 14.68C27.3136 15.016 27.1503 15.4827 27.1503 16.08C27.1503 16.6773 27.3136 17.1533 27.6403 17.508C27.9763 17.8533 28.3823 18.026 28.8583 18.026C29.3343 18.026 29.7356 17.8533 30.0623 17.508C30.3983 17.1627 30.5663 16.6913 30.5663 16.094ZM34.2162 16.08C34.2162 15.2773 34.3656 14.5727 34.6642 13.966C34.9722 13.3593 35.3876 12.8927 35.9102 12.566C36.4329 12.2393 37.0162 12.076 37.6602 12.076C38.1736 12.076 38.6402 12.1833 39.0602 12.398C39.4896 12.6127 39.8256 12.902 40.0682 13.266V9.64H42.4622V20H40.0682V18.88C39.8442 19.2533 39.5222 19.552 39.1022 19.776C38.6916 20 38.2109 20.112 37.6602 20.112C37.0162 20.112 36.4329 19.9487 35.9102 19.622C35.3876 19.286 34.9722 18.8147 34.6642 18.208C34.3656 17.592 34.2162 16.8827 34.2162 16.08ZM40.0682 16.094C40.0682 15.4967 39.9002 15.0253 39.5642 14.68C39.2376 14.3347 38.8362 14.162 38.3602 14.162C37.8842 14.162 37.4782 14.3347 37.1422 14.68C36.8156 15.016 36.6522 15.4827 36.6522 16.08C36.6522 16.6773 36.8156 17.1533 37.1422 17.508C37.4782 17.8533 37.8842 18.026 38.3602 18.026C38.8362 18.026 39.2376 17.8533 39.5642 17.508C39.9002 17.1627 40.0682 16.6913 40.0682 16.094ZM49.555 13.294C49.7883 12.93 50.1103 12.636 50.521 12.412C50.9316 12.188 51.4123 12.076 51.963 12.076C52.607 12.076 53.1903 12.2393 53.713 12.566C54.2356 12.8927 54.6463 13.3593 54.945 13.966C55.253 14.5727 55.407 15.2773 55.407 16.08C55.407 16.8827 55.253 17.592 54.945 18.208C54.6463 18.8147 54.2356 19.286 53.713 19.622C53.1903 19.9487 52.607 20.112 51.963 20.112C51.4216 20.112 50.941 20 50.521 19.776C50.1103 19.552 49.7883 19.2627 49.555 18.908V23.724H47.161V12.188H49.555V13.294ZM52.971 16.08C52.971 15.4827 52.803 15.016 52.467 14.68C52.1403 14.3347 51.7343 14.162 51.249 14.162C50.773 14.162 50.367 14.3347 50.031 14.68C49.7043 15.0253 49.541 15.4967 49.541 16.094C49.541 16.6913 49.7043 17.1627 50.031 17.508C50.367 17.8533 50.773 18.026 51.249 18.026C51.725 18.026 52.131 17.8533 52.467 17.508C52.803 17.1533 52.971 16.6773 52.971 16.08ZM59.0569 9.64V20H56.6629V9.64H59.0569ZM64.3478 20.112C63.5825 20.112 62.8918 19.9487 62.2758 19.622C61.6692 19.2953 61.1885 18.8287 60.8338 18.222C60.4885 17.6153 60.3158 16.906 60.3158 16.094C60.3158 15.2913 60.4932 14.5867 60.8478 13.98C61.2025 13.364 61.6878 12.8927 62.3038 12.566C62.9198 12.2393 63.6105 12.076 64.3758 12.076C65.1412 12.076 65.8318 12.2393 66.4478 12.566C67.0638 12.8927 67.5492 13.364 67.9038 13.98C68.2585 14.5867 68.4358 15.2913 68.4358 16.094C68.4358 16.8967 68.2538 17.606 67.8898 18.222C67.5352 18.8287 67.0452 19.2953 66.4198 19.622C65.8038 19.9487 65.1132 20.112 64.3478 20.112ZM64.3478 18.04C64.8052 18.04 65.1925 17.872 65.5098 17.536C65.8365 17.2 65.9998 16.7193 65.9998 16.094C65.9998 15.4687 65.8412 14.988 65.5238 14.652C65.2158 14.316 64.8332 14.148 64.3758 14.148C63.9092 14.148 63.5218 14.316 63.2138 14.652C62.9058 14.9787 62.7518 15.4593 62.7518 16.094C62.7518 16.7193 62.9012 17.2 63.1998 17.536C63.5078 17.872 63.8905 18.04 64.3478 18.04ZM74.0599 17.97V20H72.8419C71.9739 20 71.2972 19.79 70.8119 19.37C70.3266 18.9407 70.0839 18.2453 70.0839 17.284V14.176H69.1319V12.188H70.0839V10.284H72.4779V12.188H74.0459V14.176H72.4779V17.312C72.4779 17.5453 72.5339 17.7133 72.6459 17.816C72.7579 17.9187 72.9446 17.97 73.2059 17.97H74.0599Z" fill="white" /> <g className={styles.plus}> - <line - className={styles.xAxis} - x1="103.987" - y1="15.6278" - x2="88.9872" - y2="15.5" - stroke="white" - strokeWidth="3" - /> - <line - className={styles.yAxis} - x1="96.5" - y1="8" - x2="96.5" - y2="23" - stroke="white" - strokeWidth="3" - /> + <line className={styles.xAxis} x1="103.987" y1="15.6278" x2="88.9872" y2="15.5" stroke="white" strokeWidth="3" /> + <line className={styles.yAxis} x1="96.5" y1="8" x2="96.5" y2="23" stroke="white" strokeWidth="3" /> <g className={'nodes'}> <circle cx="89" cy="16" r="1" fill="white" /> <circle cx="94" cy="14" r="1" fill="white" /> @@ -65,15 +36,7 @@ export default function AddPlotButtonComponent(props: { </g> </g> <g> - <rect - x="5" - y="1" - width="108" - height="29" - rx="3" - fill="transparent" - onClick={(e) => props.onClick(e)} - /> + <rect x="5" y="1" width="108" height="29" rx="3" fill="transparent" onClick={(e) => props.onClick(e)} /> </g> </g> ); diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotPopup.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotPopup.tsx index f53468bc4..d770915c3 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotPopup.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/AddPlotPopup.tsx @@ -8,11 +8,11 @@ /* The comment above was added so the code coverage wouldn't count this file towards code coverage. * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ -import { Link, Node } from '../../shared/ResultNodeLinkParserUseCase'; import { Button, MenuItem, Popover, TextField } from '@mui/material'; import React, { ReactElement } from 'react'; import { EntitiesFromSchema } from '../Types'; import OptimizedAutocomplete from './OptimizedAutocomplete'; +import { Node } from '@graphpolaris/shared/lib/data-access'; /** The typing for the props of the plot popups */ type AddPlotPopupProps = { @@ -61,11 +61,15 @@ export default class AddPlotPopup extends React.Component<AddPlotPopupProps, Add this.attributeNameOptions = []; if (this.props.entitiesFromSchema.attributesPerEntity[newEntity]) - this.attributeNameOptions = - this.props.entitiesFromSchema.attributesPerEntity[newEntity].textAttributeNames; + this.attributeNameOptions = this.props.entitiesFromSchema.attributesPerEntity[newEntity].textAttributeNames; this.attributeValue = ''; - this.setState({ ...this.state, entity: newEntity, attributeName: '', isButtonEnabled: false }); + this.setState({ + ...this.state, + entity: newEntity, + attributeName: '', + isButtonEnabled: false, + }); }; /** @@ -78,10 +82,9 @@ export default class AddPlotPopup extends React.Component<AddPlotPopupProps, Add this.possibleAttrValues = Array.from( this.props.nodeLinkResultNodes.reduce((values: Set<string>, node) => { - if (this.state.entity == node.id.split('/')[0] && newAttrName in node.attributes) - values.add(node.attributes[newAttrName]); + if (this.state.entity == node.label && newAttrName in node.attributes) values.add(node.attributes[newAttrName] as string); return values; - }, new Set<string>()), + }, new Set<string>()) ); // Reset attribute value. @@ -89,9 +92,7 @@ export default class AddPlotPopup extends React.Component<AddPlotPopupProps, Add // Filter the possible attribute values from the entity attributes from the schema. const isButtonDisabled = this.props.entitiesFromSchema.entityNames.includes(this.state.entity) && - this.props.entitiesFromSchema.attributesPerEntity[ - this.state.entity - ].textAttributeNames.includes(newAttrName); + this.props.entitiesFromSchema.attributesPerEntity[this.state.entity].textAttributeNames.includes(newAttrName); this.setState({ ...this.state, attributeName: newAttrName, @@ -106,16 +107,10 @@ export default class AddPlotPopup extends React.Component<AddPlotPopupProps, Add private addPlotButtonClicked = () => { if ( this.props.entitiesFromSchema.entityNames.includes(this.state.entity) && - this.props.entitiesFromSchema.attributesPerEntity[ - this.state.entity - ].textAttributeNames.includes(this.state.attributeName) + this.props.entitiesFromSchema.attributesPerEntity[this.state.entity].textAttributeNames.includes(this.state.attributeName) ) { this.props.handleClose(); - this.props.addPlot( - this.state.entity, - this.state.attributeName, - this.attributeValue == '' ? '?' : this.attributeValue, - ); + this.props.addPlot(this.state.entity, this.state.attributeName, this.attributeValue == '' ? '?' : this.attributeValue); } else this.setState({ ...this.state, @@ -200,11 +195,7 @@ export default class AddPlotPopup extends React.Component<AddPlotPopupProps, Add useMaterialStyle={{ label: 'Value', helperText: '' }} /> <div style={{ height: 40, paddingTop: 10, marginLeft: 30 }}> - <Button - variant="contained" - disabled={!this.state.isButtonEnabled} - onClick={this.addPlotButtonClicked} - > + <Button variant="contained" disabled={!this.state.isButtonEnabled} onClick={this.addPlotButtonClicked}> <span style={{ fontWeight: 'bold' }}>Add</span> </Button> </div> diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/BrushComponent.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/BrushComponent.tsx index 17b179640..839f6e6af 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/BrushComponent.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/BrushComponent.tsx @@ -9,10 +9,7 @@ * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ import React, { ReactElement } from 'react'; -import { - AxisLabel, - MinMaxType, -} from '../Types'; +import { AxisLabel, MinMaxType } from '../Types'; import { ScaleLinear, brush, scaleLinear, select } from 'd3'; /** The variables in the props of the brush component*/ @@ -72,15 +69,12 @@ export default class BrushComponent extends React.Component<BrushProps, BrushSta }) .on('end', (e) => { this.onBrushEnd(e.selection); - }), + }) ); } public componentDidUpdate(prevProps: BrushProps): void { - if ( - this.props.xAxisDomain != prevProps.xAxisDomain || - this.props.yAxisDomain != prevProps.yAxisDomain - ) { + if (this.props.xAxisDomain != prevProps.xAxisDomain || this.props.yAxisDomain != prevProps.yAxisDomain) { this.updateXYAxisScaleFunctions(); } } @@ -193,27 +187,11 @@ export default class BrushComponent extends React.Component<BrushProps, BrushSta if (this.state.brushActive && this.props.yAxisLabel != AxisLabel.evenlySpaced) { return ( <g> - <line - x1={0} - y1={yRangePositions.min} - x2={5} - y2={yRangePositions.min} - stroke={'red'} - strokeWidth={1} - fill={'transparent'} - /> + <line x1={0} y1={yRangePositions.min} x2={5} y2={yRangePositions.min} stroke={'red'} strokeWidth={1} fill={'transparent'} /> <text x={8} y={yRangePositions.min + 3} fontSize={10}> {+this.state.yBrushRange.min.toFixed(2)} </text> - <line - x1={0} - y1={yRangePositions.max} - x2={5} - y2={yRangePositions.max} - stroke={'red'} - strokeWidth={1} - fill={'transparent'} - /> + <line x1={0} y1={yRangePositions.max} x2={5} y2={yRangePositions.max} stroke={'red'} strokeWidth={1} fill={'transparent'} /> <text x={8} y={yRangePositions.max + 3} fontSize={10}> {+this.state.yBrushRange.max.toFixed(2)} </text> diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/LinesBetweenPlotsComponent.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/LinesBetweenPlotsComponent.tsx index 91adb4afc..eb40935b0 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/LinesBetweenPlotsComponent.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/LinesBetweenPlotsComponent.tsx @@ -9,11 +9,7 @@ * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ import React, { ReactElement } from 'react'; -import { - NodeType, - PlotType, - RelationType, -} from '../Types'; +import { NodeType, PlotType, RelationType } from '../Types'; import { XYPosition } from 'reactflow'; import CalcConnectionLinePositionsUseCase from '../utils/CalcConnectionLinePositionsUseCase'; @@ -33,9 +29,7 @@ export default class LinesBetweenPlots extends React.Component<LinesBetweenPlots // The JSX elements to render a connection line with arrow const lines = relations.map((relation) => ( <LineBetweenNodesComponent - key={ - fromPlot.nodes[relation.fromIndex].data.text + toPlot.nodes[relation.toIndex].data.text - } + key={fromPlot.nodes[relation.fromIndex].data.text + toPlot.nodes[relation.toIndex].data.text} fromNode={fromPlot.nodes[relation.fromIndex]} fromPlotYOffset={fromPlot.yOffset} toNode={toPlot.nodes[relation.toIndex]} @@ -65,8 +59,7 @@ type LineBetweenNodesProps = { /** React component for drawing a single connectionline between nodes for semantic substrates. */ class LineBetweenNodesComponent extends React.Component<LineBetweenNodesProps> { render(): ReactElement { - const { fromNode, toNode, fromPlotYOffset, toPlotYOffset, width, color, nodeRadius } = - this.props; + const { fromNode, toNode, fromPlotYOffset, toPlotYOffset, width, color, nodeRadius } = this.props; // The start and end position with their plot offset const startNodePos: XYPosition = { @@ -80,8 +73,11 @@ class LineBetweenNodesComponent extends React.Component<LineBetweenNodesProps> { }; // Get the positions to draw the arrow - const { start, end, controlPoint, arrowRStart, arrowLStart } = - CalcConnectionLinePositionsUseCase.calculatePositions(startNodePos, endNodePos, nodeRadius); + const { start, end, controlPoint, arrowRStart, arrowLStart } = CalcConnectionLinePositionsUseCase.calculatePositions( + startNodePos, + endNodePos, + nodeRadius + ); // Create the curved line path const path = `M ${start.x} ${start.y} Q ${controlPoint.x} ${controlPoint.y} ${end.x} ${end.y}`; @@ -89,24 +85,8 @@ class LineBetweenNodesComponent extends React.Component<LineBetweenNodesProps> { return ( <g pointerEvents={'none'}> <path d={path} stroke={color} strokeWidth={width} fill="transparent" /> - <line - x1={arrowRStart.x} - y1={arrowRStart.y} - x2={end.x} - y2={end.y} - stroke={color} - strokeWidth={width} - fill="transparent" - /> - <line - x1={arrowLStart.x} - y1={arrowLStart.y} - x2={end.x} - y2={end.y} - stroke={color} - strokeWidth={width} - fill="transparent" - /> + <line x1={arrowRStart.x} y1={arrowRStart.y} x2={end.x} y2={end.y} stroke={color} strokeWidth={width} fill="transparent" /> + <line x1={arrowLStart.x} y1={arrowLStart.y} x2={end.x} y2={end.y} stroke={color} strokeWidth={width} fill="transparent" /> </g> ); } diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/OptimizedAutocomplete.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/OptimizedAutocomplete.tsx index dba94dfc5..47d3f8c03 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/OptimizedAutocomplete.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/OptimizedAutocomplete.tsx @@ -8,7 +8,17 @@ /* The comment above was added so the code coverage wouldn't count this file towards code coverage. * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ -import { Autocomplete, AutocompleteRenderGroupParams, AutocompleteRenderInputParams, ListSubheader, TextField, Typography, makeStyles, useMediaQuery, useTheme } from '@mui/material'; +import { + Autocomplete, + AutocompleteRenderGroupParams, + AutocompleteRenderInputParams, + ListSubheader, + TextField, + Typography, + makeStyles, + useMediaQuery, + useTheme, +} from '@mui/material'; import React from 'react'; import { VariableSizeList, ListChildComponentProps } from 'react-window'; import styles from './OptimizedAutocomplete.module.scss'; @@ -44,7 +54,7 @@ function useResetCache(data: any) { } // Adapter for react-window -const ListboxComponent = React.forwardRef<HTMLDivElement>(function ListboxComponent(props, ref) { +const ListboxComponent = React.forwardRef<HTMLDivElement>(function ListboxComponent(props: any, ref) { const { children, ...other } = props; const itemData = React.Children.toArray(children); const theme = useTheme(); @@ -82,6 +92,7 @@ const ListboxComponent = React.forwardRef<HTMLDivElement>(function ListboxCompon itemSize={(index: number) => getChildSize(itemData[index])} overscanCount={5} itemCount={itemCount} + itemKey={(index: number) => 'autolist-key' + index} > {renderRow} </VariableSizeList> @@ -109,6 +120,7 @@ type OptimizedAutocomplete = { /** Renders the autocomplete input field with the given props. */ export default function OptimizedAutocomplete(props: OptimizedAutocomplete) { let newValue = props.currentValue; + console.log(props); return ( <Autocomplete @@ -138,8 +150,10 @@ export default function OptimizedAutocomplete(props: OptimizedAutocomplete) { } // defaultValue={props.currentValue} placeholder={props.currentValue} - renderOption={(props, option, state) => <Typography noWrap>{option}</Typography>} + // renderOption={(props, option, state) => <Typography noWrap>{option}</Typography>} onChange={(_: React.ChangeEvent<any>, value: string | null) => { + console.log(value); + newValue = value || '?'; if (props.onChange) props.onChange(newValue); }} diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotAxisLabelsComponent.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotAxisLabelsComponent.tsx index 9776b45d1..e9adc8352 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotAxisLabelsComponent.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotAxisLabelsComponent.tsx @@ -31,10 +31,7 @@ type PlotAxisLabelsState = { /** Component for rendering the axis labels of an semantic substrates plot. * With functionality to edit the labels. */ -export default class PlotAxisLabelsComponent extends React.Component< - PlotAxisLabelsProps, - PlotAxisLabelsState -> { +export default class PlotAxisLabelsComponent extends React.Component<PlotAxisLabelsProps, PlotAxisLabelsState> { constructor(props: PlotAxisLabelsProps) { super(props); this.state = { menuAnchor: null, menuOpenForAxis: 'x' }; @@ -78,14 +75,10 @@ export default class PlotAxisLabelsComponent extends React.Component< keepMounted // getContentAnchorEl={null} anchorOrigin={ - this.state.menuOpenForAxis == 'x' - ? { vertical: 'bottom', horizontal: 'center' } - : { vertical: 'center', horizontal: 'right' } + this.state.menuOpenForAxis == 'x' ? { vertical: 'bottom', horizontal: 'center' } : { vertical: 'center', horizontal: 'right' } } transformOrigin={ - this.state.menuOpenForAxis == 'x' - ? { vertical: 'top', horizontal: 'center' } - : { vertical: 'center', horizontal: 'left' } + this.state.menuOpenForAxis == 'x' ? { vertical: 'top', horizontal: 'center' } : { vertical: 'center', horizontal: 'left' } } open={Boolean(this.state.menuAnchor)} onClose={() => this.setState({ menuAnchor: null })} diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotComponent.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotComponent.tsx index 183fcbcc3..5e546c2d8 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotComponent.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotComponent.tsx @@ -9,14 +9,7 @@ * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ import React, { ReactElement } from 'react'; -import { - EntitiesFromSchema, - MinMaxType, - PlotType, - AxisLabel, - PlotSpecifications, - NodeType, -} from '../Types'; +import { EntitiesFromSchema, MinMaxType, PlotType, AxisLabel, PlotSpecifications, NodeType } from '../Types'; import BrushComponent from './BrushComponent'; import PlotTitleComponent from './PlotTitleComponent'; import PlotAxisLabelsComponent from './PlotAxisLabelsComponent'; @@ -160,12 +153,10 @@ export default class Plot extends React.Component<PlotProps, PlotState> { // Determine the x y axis label, if it's byAttribute, give it the attribute type. let xAxisLabel: string; - if (plotSpecification.xAxis == AxisLabel.byAttribute) - xAxisLabel = plotSpecification.xAxisAttributeType; + if (plotSpecification.xAxis == AxisLabel.byAttribute) xAxisLabel = plotSpecification.xAxisAttributeType; else xAxisLabel = plotSpecification.xAxis; let yAxisLabel: string; - if (plotSpecification.yAxis == AxisLabel.byAttribute) - yAxisLabel = plotSpecification.yAxisAttributeType; + if (plotSpecification.yAxis == AxisLabel.byAttribute) yAxisLabel = plotSpecification.yAxisAttributeType; else yAxisLabel = plotSpecification.yAxis; const axisLabelOptions = [ @@ -196,17 +187,10 @@ export default class Plot extends React.Component<PlotProps, PlotState> { axisLabelOptions={axisLabelOptions} onAxisLabelChanged={onAxisLabelChanged} /> - <g - onClick={() => onDelete()} - transform={`translate(${width + 3},${height - 30})`} - className="delete-plot-icon" - > + <g onClick={() => onDelete()} transform={`translate(${width + 3},${height - 30})`} className="delete-plot-icon"> <rect x1="-1" y1="-1" width="26" height="26" fill="transparent" /> <path d="M3,6V24H21V6ZM8,20a1,1,0,0,1-2,0V10a1,1,0,0,1,2,0Zm5,0a1,1,0,0,1-2,0V10a1,1,0,0,1,2,0Zm5,0a1,1,0,0,1-2,0V10a1,1,0,0,1,2,0Z" /> - <path - className="lid" - d="M22,2V4H2V2H7.711c.9,0,1.631-1.1,1.631-2h5.315c0,.9.73,2,1.631,2Z" - /> + <path className="lid" d="M22,2V4H2V2H7.711c.9,0,1.631-1.1,1.631-2h5.315c0,.9.73,2,1.631,2Z" /> </g> <BrushComponent width={width} diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotTitleComponent.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotTitleComponent.tsx index d21528294..6247214dd 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotTitleComponent.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/PlotTitleComponent.tsx @@ -10,7 +10,7 @@ * See testing plan for more details.*/ import { Menu, MenuItem } from '@mui/material'; import Color from 'color'; -import React, { ReactElement } from 'react'; +import React, { ReactElement, useState } from 'react'; import { EntitiesFromSchema } from '../Types'; import styles from './PlotTitleStyles.module.css'; import { XYPosition } from 'reactflow'; @@ -42,53 +42,49 @@ type PlotTitleState = { * A semantic substrates React component for rendering a plot title. * With functionality to change the entity, attributeType and attributeName. */ -export default class PlotTitleComponent extends React.Component<PlotTitleProps, PlotTitleState> { - constructor(props: PlotTitleProps) { - super(props); - - this.state = { - isEditingAttrValue: false, - menuAnchor: null, - menuItems: [], - menuItemsOnClick: () => true, - }; - } +export default function PlotTitleComponent(props: PlotTitleProps) { + const [state, setState] = useState<PlotTitleState>({ + isEditingAttrValue: false, + menuAnchor: null, + menuItems: [], + menuItemsOnClick: () => true, + }); /** * Will be called when the value of the entity changes. * @param {string} value The new entity value. */ - private onEntityChanged(value: string): void { + function onEntityChanged(value: string): void { // Get the first attribute name for this entity. const attrName = - this.props.entitiesFromSchema.attributesPerEntity[value].textAttributeNames.length > 0 - ? this.props.entitiesFromSchema.attributesPerEntity[value].textAttributeNames[0] + props.entitiesFromSchema.attributesPerEntity[value].textAttributeNames.length > 0 + ? props.entitiesFromSchema.attributesPerEntity[value].textAttributeNames[0] : '?'; - this.props.onTitleChanged(value, attrName, '?'); + props.onTitleChanged(value, attrName, '?'); } /** * Will be called when the attribute name changes. * @param {string} value The new attribute name value. */ - private onAttrNameChanged(value: string): void { - this.props.onTitleChanged(this.props.entity, value, '?'); + function onAttrNameChanged(value: string): void { + props.onTitleChanged(props.entity, value, '?'); } /** * Will be called when the attribute value changes. * @param {string} value The new value of the attribute value. */ - private onAttrValueChanged(value: string): void { + function onAttrValueChanged(value: string): void { if (value == '') value = '?'; // only update the state if the attribute value didn't change // If the attribute value did change, this component will be rerendered anyway - if (value != this.props.attributeValue) - this.props.onTitleChanged(this.props.entity, this.props.attributeName, value); + if (value != props.attributeValue) props.onTitleChanged(props.entity, props.attributeName, value); else - this.setState({ + setState({ + ...state, isEditingAttrValue: false, }); } @@ -98,15 +94,15 @@ export default class PlotTitleComponent extends React.Component<PlotTitleProps, * @param {number} xOffset The x position offset. * @returns {ReactElement} The svg elements to render for the attribute value. */ - private renderAttributeValue(xOffset: number): ReactElement { - if (this.state.isEditingAttrValue) + function renderAttributeValue(xOffset: number): ReactElement { + if (state.isEditingAttrValue) return ( <foreignObject x={xOffset} y="-16" width="200" height="150"> <div> <OptimizedAutocomplete - options={this.props.possibleAttrValues} - currentValue={this.props.attributeValue} - onLeave={(v: string) => this.onAttrValueChanged(v)} + options={props.possibleAttrValues} + currentValue={props.attributeValue} + onLeave={(v: string) => onAttrValueChanged(v)} /> </div> </foreignObject> @@ -116,92 +112,105 @@ export default class PlotTitleComponent extends React.Component<PlotTitleProps, <text x={xOffset} className={styles.clickable} - onClick={() => this.setState({ isEditingAttrValue: true })} + onClick={() => + setState({ + ...state, + isEditingAttrValue: true, + }) + } > - {this.props.attributeValue} + {props.attributeValue} </text> ); } - render(): ReactElement { - const { entity, attributeName, entitiesFromSchema, pos, nodeColor } = this.props; - - const withOffset = getWidthOfText(entity + ' ', 'arial', '15px', 'bold'); - const attrNameOffset = getWidthOfText('with ', 'arial', '15px') + withOffset; - const colonOffset = - getWidthOfText(attributeName + ' ', 'arial', '15px', 'bold') + attrNameOffset; - const attrValueOffset = getWidthOfText(': ', 'arial', '15px', 'bold') + colonOffset; - - const nodeColorDarkened = Color(nodeColor).darken(0.3).hex(); - - return ( - <g transform={`translate(${pos.x},${pos.y})`}> - <text - className={styles.clickable} - fill={nodeColorDarkened} - onClick={(event: React.MouseEvent<SVGTextElement>) => { - this.setState({ - menuAnchor: event.currentTarget, - menuItems: entitiesFromSchema.entityNames, - menuItemsOnClick: (v: string) => this.onEntityChanged(v), - }); - }} - > - {entity} - </text> - <text x={withOffset} fontSize={15}> - with - </text> - <text - className={styles.clickable} - x={attrNameOffset} - onClick={(event: React.MouseEvent<SVGTextElement>) => { - this.setState({ - menuAnchor: event.currentTarget, - menuItems: entitiesFromSchema.attributesPerEntity[entity].textAttributeNames, - menuItemsOnClick: (v: string) => this.onAttrNameChanged(v), - }); - }} - > - {attributeName} - </text> - <text x={colonOffset} fontWeight="bold" fontSize={15}> - : - </text> - - {this.renderAttributeValue(attrValueOffset)} - - <Menu - id="simple-menu" - anchorEl={this.state.menuAnchor} - keepMounted - // getContentAnchorEl={null} - anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} - transformOrigin={{ vertical: 'top', horizontal: 'center' }} - open={Boolean(this.state.menuAnchor)} - onClose={() => this.setState({ menuAnchor: null })} - transitionDuration={150} - PaperProps={{ - style: { - maxHeight: 48 * 4.5, // 48 ITEM_HEIGHT - }, - }} - > - {this.state.menuItems.map((option) => ( - <MenuItem - key={option} - selected={option == this.state.menuAnchor?.innerHTML} - onClick={() => { - this.setState({ menuAnchor: null, menuItems: [] }); - - this.state.menuItemsOnClick(option); - }} - > - {option} - </MenuItem> - ))} - </Menu> - </g> - ); - } + const { entity, attributeName, entitiesFromSchema, pos, nodeColor } = props; + + const withOffset = getWidthOfText(entity + ' ', 'arial', '15px', 'bold'); + const attrNameOffset = getWidthOfText('with ', 'arial', '15px') + withOffset; + const colonOffset = getWidthOfText(attributeName + ' ', 'arial', '15px', 'bold') + attrNameOffset; + const attrValueOffset = getWidthOfText(': ', 'arial', '15px', 'bold') + colonOffset; + + const nodeColorDarkened = Color(nodeColor).darken(0.3).hex(); + + return ( + <g transform={`translate(${pos.x},${pos.y})`}> + <text + className={styles.clickable} + fill={nodeColorDarkened} + onClick={(event: React.MouseEvent<SVGTextElement>) => { + setState({ + ...state, + menuAnchor: event.currentTarget, + menuItems: entitiesFromSchema.entityNames, + menuItemsOnClick: (v: string) => onEntityChanged(v), + }); + }} + > + {entity} + </text> + <text x={withOffset} fontSize={15}> + with + </text> + <text + className={styles.clickable} + x={attrNameOffset} + onClick={(event: React.MouseEvent<SVGTextElement>) => { + setState({ + ...state, + menuAnchor: event.currentTarget, + menuItems: entitiesFromSchema.attributesPerEntity[entity].textAttributeNames, + menuItemsOnClick: (v: string) => onAttrNameChanged(v), + }); + }} + > + {attributeName} + </text> + <text x={colonOffset} fontWeight="bold" fontSize={15}> + : + </text> + + {renderAttributeValue(attrValueOffset)} + + <Menu + id="simple-menu" + anchorEl={state.menuAnchor} + keepMounted + // getContentAnchorEl={null} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + transformOrigin={{ vertical: 'top', horizontal: 'center' }} + open={Boolean(state.menuAnchor)} + onClose={() => + setState({ + ...state, + menuAnchor: null, + }) + } + transitionDuration={150} + PaperProps={{ + style: { + maxHeight: 48 * 4.5, // 48 ITEM_HEIGHT + }, + }} + > + {state.menuItems.map((option) => ( + <MenuItem + key={option} + selected={option == state.menuAnchor?.innerHTML} + onClick={() => { + setState({ + ...state, + menuAnchor: null, + menuItems: [], + }); + + state.menuItemsOnClick(option); + }} + > + {option} + </MenuItem> + ))} + </Menu> + </g> + ); } diff --git a/libs/shared/lib/vis/semanticsubstrates/subcomponents/SVGCheckBoxComponent.tsx b/libs/shared/lib/vis/semanticsubstrates/subcomponents/SVGCheckBoxComponent.tsx index d1f8a425a..4b66a6e4c 100644 --- a/libs/shared/lib/vis/semanticsubstrates/subcomponents/SVGCheckBoxComponent.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/subcomponents/SVGCheckBoxComponent.tsx @@ -9,11 +9,8 @@ * We do not test components/renderfunctions/styling files. * See testing plan for more details.*/ import Color from 'color'; -import React, { ReactElement } from 'react'; -import { - PlotType, - RelationType, -} from '../Types'; +import React, { ReactElement, useState } from 'react'; +import { PlotType, RelationType } from '../Types'; /** All the props needed to visualize all the semantic substrates checkboxes. */ type SVGCheckboxesWithSemanticSubstrLabelProps = { @@ -25,71 +22,59 @@ type SVGCheckboxesWithSemanticSubstrLabelProps = { }; /** Renders all the checkboxes for a semantic substrates visualisation. */ -export default class SVGCheckboxesWithSemanticSubstrLabel extends React.Component<SVGCheckboxesWithSemanticSubstrLabelProps> { - render(): ReactElement { - const { plots, relations, visibleRelations, nodeColors, onCheckboxChanged } = this.props; +export default function SVGCheckboxesWithSemanticSubstrLabel(props: SVGCheckboxesWithSemanticSubstrLabelProps) { + const { plots, relations, visibleRelations, nodeColors, onCheckboxChanged } = props; - const checkboxGroups: ReactElement[] = []; + const checkboxGroups: ReactElement[] = []; - // Go through each relation. - for (let fromPlot = 0; fromPlot < relations.length; fromPlot++) { - const checkboxes: JSX.Element[] = []; - // The from- and toPlot title color will be a bit darker than the node colors for their plots. - const fromColor = Color(nodeColors[fromPlot]).darken(0.3).hex(); - for (let toPlot = 0; toPlot < relations[fromPlot].length; toPlot++) { - if (!!relations?.[fromPlot]?.[toPlot] && visibleRelations?.[fromPlot]?.[toPlot] !== undefined && relations[fromPlot][toPlot].length > 0) { - // Add a checkbox for the connections between fromPlot and toPlot. - checkboxes.push( - <g - key={plots[fromPlot].title + fromPlot + '-' + plots[toPlot].title + toPlot} - transform={'translate(0,' + toPlot * 30 + ')'} - > - <text x={20} y={12} fill={fromColor} style={{ fontWeight: 'bold' }}> - {plots[fromPlot].title} - </text> - <path - fill="transparent" - stroke={'black'} - d={`M${110} 7 l40 0 l-0 0 l-15 -5 m15 5 l-15 5`} - /> - <text - x={170} - y={12} - fill={Color(nodeColors[toPlot]).darken(0.3).hex()} - style={{ fontWeight: 'bold' }} - > - {plots[toPlot].title} - </text> - <SVGCheckboxComponent - x={0} - y={0} - key={plots[fromPlot].title + plots[toPlot].title + fromPlot + toPlot} - width={15} - value={visibleRelations[fromPlot][toPlot]} - onChange={(value: boolean) => { - onCheckboxChanged(fromPlot, toPlot, value); - }} - /> - </g>, - ); - } + // Go through each relation. + for (let fromPlot = 0; fromPlot < relations.length; fromPlot++) { + const checkboxes: JSX.Element[] = []; + // The from- and toPlot title color will be a bit darker than the node colors for their plots. + const fromColor = Color(nodeColors[fromPlot]).darken(0.3).hex(); + for (let toPlot = 0; toPlot < relations[fromPlot].length; toPlot++) { + if ( + !!relations?.[fromPlot]?.[toPlot] && + visibleRelations?.[fromPlot]?.[toPlot] !== undefined && + relations[fromPlot][toPlot].length > 0 + ) { + // Add a checkbox for the connections between fromPlot and toPlot. + checkboxes.push( + <g key={plots[fromPlot].title + fromPlot + '-' + plots[toPlot].title + toPlot} transform={'translate(0,' + toPlot * 30 + ')'}> + <text x={20} y={12} fill={fromColor} style={{ fontWeight: 'bold' }}> + {plots[fromPlot].title} + </text> + <path fill="transparent" stroke={'black'} d={`M${110} 7 l40 0 l-0 0 l-15 -5 m15 5 l-15 5`} /> + <text x={170} y={12} fill={Color(nodeColors[toPlot]).darken(0.3).hex()} style={{ fontWeight: 'bold' }}> + {plots[toPlot].title} + </text> + <SVGCheckboxComponent + x={0} + y={0} + key={plots[fromPlot].title + plots[toPlot].title + fromPlot + toPlot} + width={15} + value={visibleRelations[fromPlot][toPlot]} + onChange={(value: boolean) => { + onCheckboxChanged(fromPlot, toPlot, value); + }} + /> + </g> + ); } - - // Offset the height of the checkboxes for each plot. - checkboxGroups.push( - <g - key={plots[fromPlot].title + fromPlot} - transform={ - 'translate(' + (plots[fromPlot].width + 30) + ',' + plots[fromPlot].yOffset + ')' - } - > - {checkboxes} - </g>, - ); } - return <g>{checkboxGroups}</g>; + // Offset the height of the checkboxes for each plot. + checkboxGroups.push( + <g + key={plots[fromPlot].title + fromPlot} + transform={'translate(' + (plots[fromPlot].width + 30) + ',' + plots[fromPlot].yOffset + ')'} + > + {checkboxes} + </g> + ); } + + return <g>{checkboxGroups}</g>; } /** The props for the SVG checkbox component */ @@ -107,49 +92,46 @@ type SVGCheckBoxState = { value: boolean; }; /** Renders a simple checkbox in SVG elements. */ -export class SVGCheckboxComponent extends React.Component<SVGCheckBoxProps, SVGCheckBoxState> { - static defaultProps = { +export function SVGCheckboxComponent( + props: SVGCheckBoxProps = { + x: 0, + y: 0, width: 15, value: false, onChange: () => true, - }; - constructor(props: SVGCheckBoxProps) { - super(props); - this.state = { value: this.props.value }; - } - - render(): ReactElement { - return ( - <g> - <rect - x={this.props.x} - y={this.props.y} - rx="0" - ry="0" - width={this.props.width} - height={this.props.width} - style={{ fill: 'transparent', stroke: 'grey', strokeWidth: 2 }} - onClick={() => { - const newVal = this.state.value ? false : true; - this.props.onChange(newVal); - this.setState({ value: newVal }); - }} - /> - <rect - x={this.props.x + 3} - y={this.props.y + 3} - rx="0" - ry="0" - width={this.props.width - 6} - height={this.props.width - 6} - style={{ fill: this.state.value ? '#00a300' : 'white' }} - onClick={() => { - const newVal = this.state.value ? false : true; - this.props.onChange(newVal); - this.setState({ value: newVal }); - }} - /> - </g> - ); } +) { + const [state, setState] = useState<SVGCheckBoxState>({ value: props.value }); + return ( + <g> + <rect + x={props.x} + y={props.y} + rx="0" + ry="0" + width={props.width} + height={props.width} + style={{ fill: 'transparent', stroke: 'grey', strokeWidth: 2 }} + onClick={() => { + const newVal = state.value ? false : true; + props.onChange(newVal); + setState({ ...state, value: newVal }); + }} + /> + <rect + x={props.x + 3} + y={props.y + 3} + rx="0" + ry="0" + width={props.width - 6} + height={props.width - 6} + style={{ fill: state.value ? '#00a300' : 'white' }} + onClick={() => { + const newVal = state.value ? false : true; + props.onChange(newVal); + setState({ ...state, value: newVal }); + }} + /> + </g> + ); } diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/CalcConnectionLinePositionsUseCase.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/CalcConnectionLinePositionsUseCase.tsx index 857aad24b..1961a6760 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/CalcConnectionLinePositionsUseCase.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/CalcConnectionLinePositionsUseCase.tsx @@ -27,11 +27,7 @@ export default class CalcConnectionLinePositionsUseCase { * @param {number} nodeRadius The node radius, used to calculate the start and end offset. * @returns {ConnecionLinePositions} The positions for drawing the curved line. */ - public static calculatePositions( - startNodePos: XYPosition, - endNodePos: XYPosition, - nodeRadius: number, - ): ConnecionLinePositions { + public static calculatePositions(startNodePos: XYPosition, endNodePos: XYPosition, nodeRadius: number): ConnecionLinePositions { // Calculate the control point for the quadratic curve path, see https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths const distance = CalcDistance(startNodePos, endNodePos); if (distance == 0) return this.returnStartPosition(startNodePos); @@ -48,14 +44,8 @@ export default class CalcConnectionLinePositionsUseCase { const movedEndPos = this.calculateMovedEndPos(endNodePos, endToControlPointVec, nodeRadius); // Create arrowhead start points - const arrowRStart = this.calculateArrowStart( - movedEndPos, - RotateVectorByDeg(endToControlPointVec, 30), - ); - const arrowLStart = this.calculateArrowStart( - movedEndPos, - RotateVectorByDeg(endToControlPointVec, -30), - ); + const arrowRStart = this.calculateArrowStart(movedEndPos, RotateVectorByDeg(endToControlPointVec, 30)); + const arrowLStart = this.calculateArrowStart(movedEndPos, RotateVectorByDeg(endToControlPointVec, -30)); return { start: movedStartPos, @@ -89,11 +79,7 @@ export default class CalcConnectionLinePositionsUseCase { * @param {number} distance The distance between the two nodes. * @returns {XYPosition} The control point. */ - private static calculateControlPoint( - endNodePos: XYPosition, - startNodePos: XYPosition, - distance: number, - ): XYPosition { + private static calculateControlPoint(endNodePos: XYPosition, startNodePos: XYPosition, distance: number): XYPosition { // Normalized vector from start to end const vec: XYPosition = { x: (endNodePos.x - startNodePos.x) / distance, @@ -121,17 +107,11 @@ export default class CalcConnectionLinePositionsUseCase { * @param {number} nodeRadius The node radius, used to calculate the start and end offset. * @returns {XYPosition} The moved start position. */ - private static calculateMovedStartPos( - startNodePos: XYPosition, - controlPoint: XYPosition, - nodeRadius: number, - ): XYPosition { + private static calculateMovedStartPos(startNodePos: XYPosition, controlPoint: XYPosition, nodeRadius: number): XYPosition { const startToControlPointDist = CalcDistance(startNodePos, controlPoint); return { - x: - startNodePos.x + ((controlPoint.x - startNodePos.x) / startToControlPointDist) * nodeRadius, - y: - startNodePos.y + ((controlPoint.y - startNodePos.y) / startToControlPointDist) * nodeRadius, + x: startNodePos.x + ((controlPoint.x - startNodePos.x) / startToControlPointDist) * nodeRadius, + y: startNodePos.y + ((controlPoint.y - startNodePos.y) / startToControlPointDist) * nodeRadius, }; } @@ -142,11 +122,7 @@ export default class CalcConnectionLinePositionsUseCase { * @param {number} nodeRadius The node radius, used to calculate the start and end offset. * @returns {XYPosition} The moved end position. */ - private static calculateMovedEndPos( - endNodePos: XYPosition, - endToControlPointVec: XYPosition, - nodeRadius: number, - ): XYPosition { + private static calculateMovedEndPos(endNodePos: XYPosition, endToControlPointVec: XYPosition, nodeRadius: number): XYPosition { return { x: endNodePos.x - endToControlPointVec.x * nodeRadius, y: endNodePos.y - endToControlPointVec.y * nodeRadius, diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/CalcDefaultPlotSpecsUseCase.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/CalcDefaultPlotSpecsUseCase.tsx index dac2a940f..f4b3db4eb 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/CalcDefaultPlotSpecsUseCase.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/CalcDefaultPlotSpecsUseCase.tsx @@ -4,29 +4,29 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { NodeLinkResultType } from "../../shared/ResultNodeLinkParserUseCase"; -import { AxisLabel, PlotSpecifications } from "../Types"; +import { GraphQueryResult } from '@graphpolaris/shared/lib/data-access'; +import { AxisLabel, PlotSpecifications } from '../Types'; /** UseCase for calculating default plots from node link query result data. */ export default class CalcDefaultPlotSpecsUseCase { /** * Calculates default plot specifications for incoming query result data. * This determines what the default plots will be after executing a new query. - * @param {NodeLinkResultType} nodeLinkResult Query result data in the node link format. + * @param {GraphQueryResult} nodeLinkResult Query result data in the node link format. * @returns {PlotSpecifications[]} PlotSpecifications to generate the plots with the result data. */ - public static calculate(nodeLinkResult: NodeLinkResultType): PlotSpecifications[] { + public static calculate(nodeLinkResult: GraphQueryResult): PlotSpecifications[] { // Search through the first nodes' attributes for the shortest attribute value const plotSpecifications: PlotSpecifications[] = []; if (nodeLinkResult.nodes.length > 0) { const firstNodeAttributes = nodeLinkResult.nodes[0].attributes; - const firstNodeEntity = nodeLinkResult.nodes[0].id.split('/')[0]; + const firstNodeEntity = nodeLinkResult.nodes[0].label; let shortestStringKey = ''; let shortestStringValueLength: number = Number.MAX_VALUE; for (let key in firstNodeAttributes) { if (typeof firstNodeAttributes[key] == 'string') { - const v = firstNodeAttributes[key]; + const v = firstNodeAttributes[key] as string; if (v.length < shortestStringValueLength) { shortestStringKey = key; shortestStringValueLength = v.length; @@ -39,20 +39,17 @@ export default class CalcDefaultPlotSpecsUseCase { for (let i = 0; i < nodeLinkResult.nodes.length; i++) { // Search for the first three nodes with different attribute values with the given attributekey - if (nodeLinkResult.nodes[i].id.split('/')[0] != firstNodeEntity) continue; + if (nodeLinkResult.nodes[i].label != firstNodeEntity) continue; - const v = nodeLinkResult.nodes[i].attributes[shortestStringKey]; + const v = nodeLinkResult.nodes[i].attributes[shortestStringKey] as string; if (values.includes(v)) continue; values.push(v); - let entity = nodeLinkResult.nodes[i].id; - if (nodeLinkResult.nodes[i].id.includes('/')) entity = entity.split('/')[0]; - else if (!!nodeLinkResult.nodes[i]?.attributes?.labels) entity = nodeLinkResult.nodes[i]?.attributes?.labels[0]; plotSpecifications.push({ - entity: nodeLinkResult.nodes[i].id.split('/')[0], + entity: nodeLinkResult.nodes[i].label, labelAttributeType: shortestStringKey, - labelAttributeValue: nodeLinkResult.nodes[i].attributes[shortestStringKey], + labelAttributeValue: v, xAxis: AxisLabel.evenlySpaced, // Use default evenly spaced and # outbound connections on the x and y axis. yAxis: AxisLabel.outboundConnections, xAxisAttributeType: '', diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/CalcDistance.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/CalcDistance.tsx index d07141822..5f6633e17 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/CalcDistance.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/CalcDistance.tsx @@ -4,7 +4,7 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { XYPosition } from "reactflow"; +import { XYPosition } from 'reactflow'; /** * Calculates the difference between two positions. diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/CalcEntityAttrNamesFromResultUseCase.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/CalcEntityAttrNamesFromResultUseCase.tsx index 7120f0e03..05dc2909b 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/CalcEntityAttrNamesFromResultUseCase.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/CalcEntityAttrNamesFromResultUseCase.tsx @@ -4,30 +4,37 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { NodeLinkResultType } from "../../shared/ResultNodeLinkParserUseCase"; -import { EntitiesFromSchema } from "../Types"; - +import { SchemaFromBackend } from '@graphpolaris/shared/lib/schema'; +import { EntitiesFromSchema } from '../Types'; +import { GraphQueryResult } from '@graphpolaris/shared/lib/data-access'; /** Use case for retrieving entity names and attribute names from a schema result. */ export default class CalcEntityAttrNamesFromResultUseCase { /** * Takes a schema result and calculates all the entity names and attribute names per datatype. * Used by semantic substrates for the plot titles and add plot selections. - * @param {NodeLinkResultType} schemaResult A new schema result from the backend. + * @param {SchemaFromBackend} schemaResult A new schema result from the backend. * @param {EntitiesFromSchema} All entity names and attribute names per datatype. So we know what is in the Schema. * @returns {EntitiesFromSchema} All entity names and attribute names per datatype. + * @deprecated //TODO remove */ - public static CalcEntityAttrNamesFromResult(nodeLinkResult: NodeLinkResultType, entitiesFromSchema: EntitiesFromSchema): EntitiesFromSchema { - const listOfNodeTypes: string[] = [] + public static CalcEntityAttrNamesFromResult( + nodeLinkResult: GraphQueryResult, + entitiesFromSchema: EntitiesFromSchema + ): EntitiesFromSchema { + const listOfNodeTypes: string[] = []; nodeLinkResult.nodes.forEach((node) => { - let entityName = node.id.split('/')[0] + let entityName = node.label; if (!listOfNodeTypes.includes(entityName)) { listOfNodeTypes.push(entityName); } - }) + }); - let entitiesFromSchemaPruned: EntitiesFromSchema = { entityNames: [], attributesPerEntity: {} };; + let entitiesFromSchemaPruned: EntitiesFromSchema = { + entityNames: [], + attributesPerEntity: {}, + }; Object.assign(entitiesFromSchemaPruned, entitiesFromSchema); entitiesFromSchemaPruned.entityNames = listOfNodeTypes; diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/CalcEntityAttrNamesFromSchemaUseCase.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/CalcEntityAttrNamesFromSchemaUseCase.tsx index 6e84d932c..2f1adef76 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/CalcEntityAttrNamesFromSchemaUseCase.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/CalcEntityAttrNamesFromSchemaUseCase.tsx @@ -4,7 +4,6 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ import { SchemaGraph } from '@graphpolaris/shared/lib/schema'; -import { Schema } from '../../shared/InputDataTypes'; import { AttributeNames, EntitiesFromSchema } from '../Types'; /** Use case for retrieving entity names and attribute names from a schema result. */ @@ -42,7 +41,7 @@ export default class CalcEntityAttrNamesFromSchemaUseCase { // Create the object with entity names and attribute names. return { - entityNames: schemaResult.nodes.map((node) => (node?.attributes?.name || 'ERROR')), + entityNames: schemaResult.nodes.map((node) => node?.attributes?.name || 'ERROR'), attributesPerEntity, }; } diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/CalcScaledPositionsUseCase.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/CalcScaledPositionsUseCase.tsx index d850b3152..a534346b6 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/CalcScaledPositionsUseCase.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/CalcScaledPositionsUseCase.tsx @@ -4,9 +4,9 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { XYPosition } from "reactflow"; -import { MinMaxType, PlotInputData } from "../Types"; -import { scaleLinear } from "d3"; +import { XYPosition } from 'reactflow'; +import { MinMaxType, PlotInputData } from '../Types'; +import { scaleLinear } from 'd3'; /** * Calculates the scaled positions using d3's scaleLinear functions. @@ -20,18 +20,9 @@ export default class CalcScaledPosUseCase { * @param {MinMaxType} minmaxYAxis The min and max for the y axis. * @returns {Position[]} The scaled positions. */ - public static calculate( - plot: PlotInputData, - minmaxXAxis: MinMaxType, - minmaxYAxis: MinMaxType, - ): XYPosition[] { + public static calculate(plot: PlotInputData, minmaxXAxis: MinMaxType, minmaxYAxis: MinMaxType): XYPosition[] { // Create the scale functions with the minmax and width and height of the plot - const scaleFunctions = CalcScaledPosUseCase.createScaleFunctions( - minmaxXAxis, - minmaxYAxis, - plot.width, - plot.height, - ); + const scaleFunctions = CalcScaledPosUseCase.createScaleFunctions(minmaxXAxis, minmaxYAxis, plot.width, plot.height); // Use the scale functions to scale the nodes positions. const scaledPositions: XYPosition[] = []; @@ -50,20 +41,16 @@ export default class CalcScaledPosUseCase { minmaxXAxis: MinMaxType, minmaxYAxis: MinMaxType, plotWidth: number, - plotHeight: number, + plotHeight: number ): { xAxis: d3.ScaleLinear<number, number, never>; yAxis: d3.ScaleLinear<number, number, never>; } => { // Create the x axis scale funtion with d3 - const xAxisScale = scaleLinear() - .domain([minmaxXAxis.min, minmaxXAxis.max]) - .range([0, plotWidth]); + const xAxisScale = scaleLinear().domain([minmaxXAxis.min, minmaxXAxis.max]).range([0, plotWidth]); // Create the y axis scale funtion with d3 - const yAxisScale = scaleLinear() - .domain([minmaxYAxis.max, minmaxYAxis.min]) - .range([0, plotHeight]); + const yAxisScale = scaleLinear().domain([minmaxYAxis.max, minmaxYAxis.min]).range([0, plotHeight]); return { xAxis: xAxisScale, diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/CalcXYMinMaxUseCase.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/CalcXYMinMaxUseCase.tsx index 11f9dcdd9..0167529cc 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/CalcXYMinMaxUseCase.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/CalcXYMinMaxUseCase.tsx @@ -4,7 +4,7 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { MinMaxType, PlotInputData } from "../Types"; +import { MinMaxType, PlotInputData } from '../Types'; /** UseCase for calculating the min and max for the x and y values of the node positions. */ export default class CalcXYMinMaxUseCase { @@ -13,7 +13,10 @@ export default class CalcXYMinMaxUseCase { * @param {PlotInputData} plot The input data plot with all the nodes. * @returns {{x: MinMaxType; y: MinMaxType}} An object with the min and max for x and y. */ - public static calculate(plot: PlotInputData): { x: MinMaxType; y: MinMaxType } { + public static calculate(plot: PlotInputData): { + x: MinMaxType; + y: MinMaxType; + } { // If there are no nodes in the plot, set the min and max to [-10, 10] if (plot.nodes.length == 0) { return { diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/FilterUseCase.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/FilterUseCase.tsx index 532a3c8fe..34ee4ad3c 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/FilterUseCase.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/FilterUseCase.tsx @@ -3,12 +3,7 @@ * Utrecht University within the Software Project course. * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { - MinMaxType, - NodeType, - PlotType, - RelationType, -} from '../Types'; +import { MinMaxType, NodeType, PlotType, RelationType } from '../Types'; type Filter = { x: MinMaxType; y: MinMaxType }; @@ -22,10 +17,10 @@ export default class FilterUseCase { */ public static checkIfNodeMatchesFilter(node: NodeType, filter: Filter): boolean { return ( - node.originalPosition.x >= filter.x.min && - node.originalPosition.x <= filter.x.max && - node.originalPosition.y >= filter.y.min && - node.originalPosition.y <= filter.y.max + node.originalPosition?.x >= filter?.x.min && + node.originalPosition?.x <= filter?.x.max && + node.originalPosition?.y >= filter?.y.min && + node.originalPosition?.y <= filter?.y.max ); } @@ -43,24 +38,11 @@ export default class FilterUseCase { filteredRelations: RelationType[][][], plots: PlotType[], filtersPerPlot: Filter[], - filterIndexThatChanged: number, + filterIndexThatChanged: number ): void { - this.checkOutboundConnections( - relations, - filteredRelations, - plots, - filtersPerPlot, - filterIndexThatChanged, - ); + this.checkOutboundConnections(relations, filteredRelations, plots, filtersPerPlot, filterIndexThatChanged); for (let fromPlot = 0; fromPlot < relations.length; fromPlot++) { - this.checkInboundConnection( - relations, - filteredRelations, - plots, - filtersPerPlot, - filterIndexThatChanged, - fromPlot, - ); + this.checkInboundConnection(relations, filteredRelations, plots, filtersPerPlot, filterIndexThatChanged, fromPlot); } } @@ -78,7 +60,7 @@ export default class FilterUseCase { filteredRelations: RelationType[][][], plots: PlotType[], filtersPerPlot: Filter[], - filterIndexThatChanged: number, + filterIndexThatChanged: number ) { // Check all the outbound connections for nodes in the plot for which the filter changed. relations[filterIndexThatChanged].forEach((relations, toPlot) => { @@ -113,28 +95,23 @@ export default class FilterUseCase { plots: PlotType[], filtersPerPlot: Filter[], filterIndexThatChanged: number, - fromPlot: number, + fromPlot: number ) { // Check all the inbound connections for nodes in the plot for which the filter changed. const relationsBetweenPlots = relations[fromPlot][filterIndexThatChanged]; - filteredRelations[fromPlot][filterIndexThatChanged] = relationsBetweenPlots.filter( - (relation) => { - const toNode = plots[filterIndexThatChanged].nodes[relation.toIndex]; - const toNodeFilter = filtersPerPlot[filterIndexThatChanged]; - const toNodeMatches = this.checkIfNodeMatchesFilter(toNode, toNodeFilter); + filteredRelations[fromPlot][filterIndexThatChanged] = relationsBetweenPlots.filter((relation) => { + const toNode = plots[filterIndexThatChanged].nodes[relation.toIndex]; + const toNodeFilter = filtersPerPlot[filterIndexThatChanged]; + const toNodeMatches = this.checkIfNodeMatchesFilter(toNode, toNodeFilter); - const fromNode = plots[fromPlot].nodes[relation.fromIndex]; - const fromNodeFilter = filtersPerPlot[fromPlot]; - const fromNodeMatches = this.checkIfNodeMatchesFilter(fromNode, fromNodeFilter); + const fromNode = plots[fromPlot].nodes[relation.fromIndex]; + const fromNodeFilter = filtersPerPlot[fromPlot]; + const fromNodeMatches = this.checkIfNodeMatchesFilter(fromNode, fromNodeFilter); - // Here we also check for connections within the same plot. - // For these connections we only need to check if the from- or the to-node matches the filter. - return ( - (fromNodeMatches && toNodeMatches) || - (fromPlot == filterIndexThatChanged && (fromNodeMatches || toNodeMatches)) - ); - }, - ); + // Here we also check for connections within the same plot. + // For these connections we only need to check if the from- or the to-node matches the filter. + return (fromNodeMatches && toNodeMatches) || (fromPlot == filterIndexThatChanged && (fromNodeMatches || toNodeMatches)); + }); } } diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/RotateVec.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/RotateVec.tsx index 5677ebb1f..a77e65ede 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/RotateVec.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/RotateVec.tsx @@ -10,10 +10,7 @@ * @param {number} degrees The amount of degrees the vector needs to be rotated. * @return {number, number} The rotated vector. */ -export function RotateVectorByDeg( - vec: { x: number; y: number }, - degrees: number, -): { x: number; y: number } { +export function RotateVectorByDeg(vec: { x: number; y: number }, degrees: number): { x: number; y: number } { const radians = -(degrees % 360) * 0.01745329251; // degrees * (PI/180) return { diff --git a/libs/shared/lib/vis/semanticsubstrates/utils/ToPlotDataParserUseCase.tsx b/libs/shared/lib/vis/semanticsubstrates/utils/ToPlotDataParserUseCase.tsx index 7c09b475f..bcaa713af 100644 --- a/libs/shared/lib/vis/semanticsubstrates/utils/ToPlotDataParserUseCase.tsx +++ b/libs/shared/lib/vis/semanticsubstrates/utils/ToPlotDataParserUseCase.tsx @@ -4,13 +4,9 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ import { GraphQueryResult } from '@graphpolaris/shared/lib/data-access'; -import { NodeLinkResultType, Node, ParseToUniqueEdges } from '../../shared/ResultNodeLinkParserUseCase'; -import { - AxisLabel, - PlotInputData, - PlotSpecifications, - RelationType, -} from '../Types'; +import { ParseToUniqueEdges } from '../../shared/ResultNodeLinkParserUseCase'; +import { Edge, Node } from '../../../data-access/store'; +import { AxisLabel, PlotInputData, PlotSpecifications, RelationType } from '../Types'; /** A use case for parsing incoming node-link data to plot data */ export default class ToPlotDataParserUseCase { @@ -26,26 +22,17 @@ export default class ToPlotDataParserUseCase { relationSelectedAttributeNumerical: string, relationSelectedAttributeCatecorigal: string, relationScaleCalculation: (x: number) => number, - relationColourCalculation: (x: string) => string, + relationColourCalculation: (x: string) => string ): { plots: PlotInputData[]; relations: RelationType[][][] } { - const { plots, nodePlotIndexDict } = this.filterNodesWithPlotSpecs( - queryResult.nodes, - plotSpecifications, - ); + const { plots, nodePlotIndexDict } = this.filterNodesWithPlotSpecs(queryResult.nodes, plotSpecifications); // Initialize the relations with empty arrays, an |plots| x |plots| matrix with empty arrays - const relations: RelationType[][][] = new Array(plots.length) - .fill([]) - .map(() => new Array(plots.length).fill([]).map(() => [])); + const relations: RelationType[][][] = new Array(plots.length).fill([]).map(() => new Array(plots.length).fill([]).map(() => [])); // 2D arrays used to collect the number of out- and inbound connections // indexed like so: [plotIndex][nodeIndex in that plot] - const nOutboundConnsPerNode: number[][] = new Array(plots.length) - .fill([]) - .map((_, i) => new Array(plots[i].nodes.length).fill(0)); - const nInboundConnsPerNode: number[][] = new Array(plots.length) - .fill([]) - .map((_, i) => new Array(plots[i].nodes.length).fill(0)); + const nOutboundConnsPerNode: number[][] = new Array(plots.length).fill([]).map((_, i) => new Array(plots[i].nodes.length).fill(0)); + const nInboundConnsPerNode: number[][] = new Array(plots.length).fill([]).map((_, i) => new Array(plots[i].nodes.length).fill(0)); // Only use unique edges const uniqueEdges = ParseToUniqueEdges.parse(queryResult.edges, false); @@ -77,9 +64,7 @@ export default class ToPlotDataParserUseCase { fromIndex: fromPlot.nodeIndex, toIndex: toPlot.nodeIndex, value: relationScaleCalculation(edge.attributes[relationSelectedAttributeNumerical]), - colour: relationColourCalculation( - edge.attributes[relationSelectedAttributeCatecorigal], - ), + colour: relationColourCalculation(edge.attributes[relationSelectedAttributeCatecorigal]), }); nInboundConnsPerNode[toPlot.plotIndex][toPlot.nodeIndex]++; @@ -89,12 +74,7 @@ export default class ToPlotDataParserUseCase { }); // Determine the node position if plotSpecification had an out- or inboundconnection as x or y axis specified - this.determineNodePosForInOutboundConn( - plots, - plotSpecifications, - nInboundConnsPerNode, - nOutboundConnsPerNode, - ); + this.determineNodePosForInOutboundConn(plots, plotSpecifications, nInboundConnsPerNode, nOutboundConnsPerNode); return { plots, relations }; } @@ -110,7 +90,7 @@ export default class ToPlotDataParserUseCase { plots: PlotInputData[], plotSpecs: PlotSpecifications[], nInboundConnsPerNode: number[][], - nOutboundConnsPerNode: number[][], + nOutboundConnsPerNode: number[][] ): void { // Determine the node position if plotSpecification had an out- or inboundconnection as x or y axis specified plots.forEach((plot, plotIndex) => { @@ -121,7 +101,7 @@ export default class ToPlotDataParserUseCase { plotIndex, nodeIndex, nOutboundConnsPerNode, - nInboundConnsPerNode, + nInboundConnsPerNode ); if (x != undefined) node.originalPosition.x = x; // Same for the y pos @@ -130,7 +110,7 @@ export default class ToPlotDataParserUseCase { plotIndex, nodeIndex, nOutboundConnsPerNode, - nInboundConnsPerNode, + nInboundConnsPerNode ); if (y != undefined) node.originalPosition.y = y; }); @@ -145,7 +125,7 @@ export default class ToPlotDataParserUseCase { */ private static filterNodesWithPlotSpecs( nodes: Node[], - plotSpecs: PlotSpecifications[], + plotSpecs: PlotSpecifications[] ): { plots: PlotInputData[]; nodePlotIndexDict: Record<string, { plotIndex: number; nodeIndex: number }[]>; @@ -171,22 +151,19 @@ export default class ToPlotDataParserUseCase { nodes.forEach((node) => { for (let i = 0; i < plotSpecs.length; i++) { // Check if the node has an label attributeType value that matches the filter - if ( - plotSpecs[i].entity == node.id.split('/')[0] && - plotSpecs[i].labelAttributeValue == node.attributes[plotSpecs[i].labelAttributeType] - ) { + if (plotSpecs[i].entity == node.label && plotSpecs[i].labelAttributeValue == node.attributes[plotSpecs[i].labelAttributeType]) { // Check if the axisPositioning spec is of equalDistance or attributeType const x = this.getAxisPosEqualDistanceOrAttrType( plotSpecs[i].xAxis, plots[i].nodes.length + 1, plotSpecs[i].xAxisAttributeType, - node.attributes, + node.attributes ); const y = this.getAxisPosEqualDistanceOrAttrType( plotSpecs[i].yAxis, plots[i].nodes.length + 1, plotSpecs[i].yAxisAttributeType, - node.attributes, + node.attributes ); // Add the node to the correct plot @@ -223,12 +200,11 @@ export default class ToPlotDataParserUseCase { plotSpecAxis: AxisLabel, nodeIndex: number, xAxisAttrType: string, - attributes: Record<string, any>, + attributes: Record<string, any> ): number { switch (plotSpecAxis) { case AxisLabel.byAttribute: - if (attributes[xAxisAttrType] && !isNaN(attributes[xAxisAttrType])) - return +attributes[xAxisAttrType]; + if (attributes[xAxisAttrType] && !isNaN(attributes[xAxisAttrType])) return +attributes[xAxisAttrType]; else return nodeIndex; default: @@ -251,7 +227,7 @@ export default class ToPlotDataParserUseCase { plotIndex: number, nodeIndex: number, nOutboundConnsPerNode: number[][], - nInboundConnsPerNode: number[][], + nInboundConnsPerNode: number[][] ): number | undefined { switch (plotSpecAxis) { case AxisLabel.outboundConnections: diff --git a/libs/shared/lib/vis/shared/AttributeDataType.tsx b/libs/shared/lib/vis/shared/AttributeDataType.tsx index 344c37a5e..2ba60eea0 100644 --- a/libs/shared/lib/vis/shared/AttributeDataType.tsx +++ b/libs/shared/lib/vis/shared/AttributeDataType.tsx @@ -82,7 +82,7 @@ function isAttributeArray(object: any): object is AttributeFromAttributeData[] { typeof attribute.name == 'string' && ['Categorical', 'Numerical', 'Other'].includes(attribute.type) && typeof attribute.nullAmount == 'number' && - isCorrectUniqueCategoricalValues(attribute), + isCorrectUniqueCategoricalValues(attribute) ); } diff --git a/libs/shared/lib/vis/shared/InputDataTypes.tsx b/libs/shared/lib/vis/shared/InputDataTypes.tsx index fdc0a35a5..4d97b6529 100644 --- a/libs/shared/lib/vis/shared/InputDataTypes.tsx +++ b/libs/shared/lib/vis/shared/InputDataTypes.tsx @@ -3,24 +3,13 @@ * Utrecht University within the Software Project course. * © Copyright Utrecht University (Department of Information and Computing Sciences) */ +import { SchemaAttribute } from '../../schema'; import { AttributeCategory } from './Types'; -/** Schema type, consist of nodes and edges */ -// export type Schema = { DEPRECATED USE SCHEMAGRAPH INSTEAD -// edges: Edge[]; -// nodes: Node[]; -// }; - -/** Attribute type, consist of a name */ -export type Attribute = { - name: string; - type: 'string' | 'int' | 'bool' | 'float'; -}; - /** Node type, consist of a name and a list of attributes */ export type Node = { name: string; - attributes: Attribute[]; + attributes: SchemaAttribute[]; }; /** Edge type, consist of a name, start point, end point and a list of attributes */ @@ -29,7 +18,7 @@ export type Edge = { to: string; from: string; collection: string; - attributes: Attribute[]; + attributes: SchemaAttribute[]; }; /** Type of the attribute-data, which could be either of a node (entity) or an edge (relation) */ diff --git a/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx b/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx index eadcc972c..ca67ade05 100644 --- a/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx +++ b/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx @@ -40,14 +40,11 @@ export type AxisType = Node | Edge; /** Gets the group to which the node/edge belongs */ export function getGroupName(axisType: AxisType): string { // FIXME: only works in arangodb - return (axisType.id as string).split('/')[0]; + return axisType.label; } /** Returns true if the given id belongs to the target group. */ -export function isNotInGroup( - nodeOrEdge: AxisType, - targetGroup: string -): boolean { +export function isNotInGroup(nodeOrEdge: AxisType, targetGroup: string): boolean { return getGroupName(nodeOrEdge) != targetGroup; } @@ -55,33 +52,19 @@ export function isNotInGroup( * @param {any} jsonObject The query result object received from the frontend. * @returns True and the jsonObject will be casted, false if the jsonObject did not contain all the data fields. */ -export function isNodeLinkResult( - jsonObject: any -): jsonObject is GraphQueryResult { - if ( - typeof jsonObject === 'object' && - jsonObject !== null && - 'nodes' in jsonObject && - 'edges' in jsonObject - ) { - if (!Array.isArray(jsonObject.nodes) || !Array.isArray(jsonObject.edges)) - return false; - - const validNodes = jsonObject.nodes.every( - (node: any) => 'id' in node && 'attributes' in node - ); - const validEdges = jsonObject.edges.every( - (edge: any) => 'from' in edge && 'to' in edge - ); +export function isNodeLinkResult(jsonObject: any): jsonObject is GraphQueryResult { + if (typeof jsonObject === 'object' && jsonObject !== null && 'nodes' in jsonObject && 'edges' in jsonObject) { + if (!Array.isArray(jsonObject.nodes) || !Array.isArray(jsonObject.edges)) return false; + + const validNodes = jsonObject.nodes.every((node: any) => 'id' in node && 'attributes' in node); + const validEdges = jsonObject.edges.every((edge: any) => 'from' in edge && 'to' in edge); return validNodes && validEdges; } else return false; } /** Returns a record with a type of the nodes as key and a number that represents how many times this type is present in the nodeLinkResult as value. */ -export function getNodeTypes( - nodeLinkResult: GraphQueryResult -): Record<string, number> { +export function getNodeTypes(nodeLinkResult: GraphQueryResult): Record<string, number> { const types: Record<string, number> = {}; nodeLinkResult.nodes.forEach((node) => { @@ -107,10 +90,7 @@ export class ParseToUniqueEdges { * @param {boolean} isLinkPredictionData True if parsing LinkPredictionData, false otherwise. * @returns {UniqueEdge[]} Unique edges with a count property added. */ - public static parse( - queryResultEdges: Edge[], - isLinkPredictionData: boolean - ): UniqueEdge[] { + public static parse(queryResultEdges: Edge[], isLinkPredictionData: boolean): UniqueEdge[] { // Edges to be returned const edges: UniqueEdge[] = []; @@ -121,8 +101,7 @@ export class ParseToUniqueEdges { if (queryResultEdges != null) { if (!isLinkPredictionData) { for (let j = 0; j < queryResultEdges.length; j++) { - const newLink = - queryResultEdges[j].from + ':' + queryResultEdges[j].to; + const newLink = queryResultEdges[j].from + ':' + queryResultEdges[j].to; edgesMap.set(newLink, (edgesMap.get(newLink) || 0) + 1); attriMap.set(newLink, queryResultEdges[j].attributes); } @@ -172,7 +151,7 @@ export default class ResultNodeLinkParserUseCase { for (let i = 0; i < queryResult.nodes.length; i++) { // Assigns a group to every entity type for color coding const nodeId = queryResult.nodes[i].id + '/'; - const entityType = nodeId.split('/')[0]; + const entityType = queryResult.nodes[i].label; // The preferred text to be shown on top of the node let preferredText = nodeId; @@ -187,12 +166,9 @@ export default class ResultNodeLinkParserUseCase { } // Check to see if node has a "naam" attribute and set prefText to it - if (queryResult.nodes[i].attributes.name != undefined) - preferredText = queryResult.nodes[i].attributes.name as string; - if (queryResult.nodes[i].attributes.label != undefined) - preferredText = queryResult.nodes[i].attributes.label as string; - if (queryResult.nodes[i].attributes.naam != undefined) - preferredText = queryResult.nodes[i].attributes.naam as string; + if (queryResult.nodes[i].attributes.name != undefined) preferredText = queryResult.nodes[i].attributes.name as string; + if (queryResult.nodes[i].attributes.label != undefined) preferredText = queryResult.nodes[i].attributes.label as string; + if (queryResult.nodes[i].attributes.naam != undefined) preferredText = queryResult.nodes[i].attributes.naam as string; let data = { id: queryResult.nodes[i].id, @@ -202,15 +178,9 @@ export default class ResultNodeLinkParserUseCase { }; let mlExtra = {}; - if ( - queryResult.nodes[i].mldata && - typeof queryResult.nodes[i].mldata != 'number' - ) { + if (queryResult.nodes[i].mldata && typeof queryResult.nodes[i].mldata != 'number') { mlExtra = { - shortestPathData: queryResult.nodes[i].mldata as Record< - string, - string[] - >, + shortestPathData: queryResult.nodes[i].mldata as Record<string, string[]>, }; shortestPathInResult = true; } else if (typeof queryResult.nodes[i].mldata == 'number') { @@ -231,11 +201,10 @@ export default class ResultNodeLinkParserUseCase { nodes.push(data); } - // Filter unique edges and transform to LinkTypes // List for all links let links: LinkType[] = []; - let allNodeIds = new Set(nodes.map(n => n.id)); + let allNodeIds = new Set(nodes.map((n) => n.id)); // Parse ml edges if (queryResult.mlEdges != undefined) { const uniqueMLEdges = ParseToUniqueEdges.parse(queryResult.mlEdges, true); @@ -266,8 +235,7 @@ export default class ResultNodeLinkParserUseCase { //TODO: is this in use? const maxCount = links.reduce( - (previousValue, currentValue) => - currentValue.value > previousValue ? currentValue.value : previousValue, + (previousValue, currentValue) => (currentValue.value > previousValue ? currentValue.value : previousValue), -1 ); //TODO: is this in use? diff --git a/libs/shared/lib/vis/shared/SchemaResultType.test.tsx b/libs/shared/lib/vis/shared/SchemaResultType.test.tsx index c7ee2085e..cb08d75ad 100644 --- a/libs/shared/lib/vis/shared/SchemaResultType.test.tsx +++ b/libs/shared/lib/vis/shared/SchemaResultType.test.tsx @@ -3,12 +3,12 @@ * Utrecht University within the Software Project course. * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { assert, describe, expect, it } from "vitest"; -import { isSchemaResult } from "./SchemaResultType"; +import { assert, describe, expect, it } from 'vitest'; +import { isSchemaResult } from './SchemaResultType'; /** Testsuite to test the schema result checker */ -describe("isSchemaResult", () => { - it("should return true for a valid schema object", () => { +describe('isSchemaResult', () => { + it('should return true for a valid schema object', () => { const schema: any = { nodes: [], edges: [], @@ -16,14 +16,14 @@ describe("isSchemaResult", () => { expect(isSchemaResult(schema)).toBe(true); schema.nodes.push({ - name: "hoi", - attributes: [{ name: "attr", type: "string" }], + name: 'hoi', + attributes: [{ name: 'attr', type: 'string' }], }); schema.edges.push({ - name: "edge", - from: "1", - to: "2", - collection: "flights", + name: 'edge', + from: '1', + to: '2', + collection: 'flights', attributes: [], }); expect(isSchemaResult(schema)).toBe(true); @@ -32,7 +32,7 @@ describe("isSchemaResult", () => { expect(isSchemaResult(schema)).toBe(true); }); - it("should return false for an invalid schema object", () => { + it('should return false for an invalid schema object', () => { const schema: any = { nodes: [], edges: [], @@ -44,10 +44,10 @@ describe("isSchemaResult", () => { schema.edges = [ { - name: "test", - from: "2", - TO: "2", - collection: "flights", + name: 'test', + from: '2', + TO: '2', + collection: 'flights', attributes: [], }, ]; @@ -55,10 +55,10 @@ describe("isSchemaResult", () => { schema.edges = [ { - name: "test", - from: "2", - to: "2", - collection: "flights", + name: 'test', + from: '2', + to: '2', + collection: 'flights', attributes: 2, }, ]; @@ -66,13 +66,13 @@ describe("isSchemaResult", () => { schema.edges = [ { - name: "test", - from: "2", - to: "2", - collection: "flights", + name: 'test', + from: '2', + to: '2', + collection: 'flights', attributes: [ - { name: "attr", type: "text" }, - { name: "attr", type: "ff at 20" }, + { name: 'attr', type: 'text' }, + { name: 'attr', type: 'ff at 20' }, ], }, ]; diff --git a/libs/shared/lib/vis/shared/SchemaResultType.tsx b/libs/shared/lib/vis/shared/SchemaResultType.tsx index c52dd7b41..804ed3f9c 100644 --- a/libs/shared/lib/vis/shared/SchemaResultType.tsx +++ b/libs/shared/lib/vis/shared/SchemaResultType.tsx @@ -3,27 +3,19 @@ * Utrecht University within the Software Project course. * © Copyright Utrecht University (Department of Information and Computing Sciences) */ -import { Attribute, Schema } from './InputDataTypes'; /** * Checks if an object has all the properties of a schema result. If true, the object will be casted to SchemaResultType * @param {any} object The object to check if it is a SchemaResult object. * @returns If true, the object is a of type SchemaResultType. + * @deprecated //TODO remove */ -export function isSchemaResult(object: any): object is Schema { - if ( - typeof object === 'object' && - 'nodes' in object && - object.nodes != undefined && - 'edges' in object && - object.edges != undefined - ) { +export function isSchemaResult(object: any): object is any { + if (typeof object === 'object' && 'nodes' in object && object.nodes != undefined && 'edges' in object && object.edges != undefined) { if (!Array.isArray(object.nodes) || !Array.isArray(object.edges)) return false; // Check the structure of all nodes - const validNodes = object.nodes.every( - (node: any) => typeof node.name == 'string' && isAttributeArray(node.attributes), - ); + const validNodes = object.nodes.every((node: any) => typeof node.name == 'string' && isAttributeArray(node.attributes)); // Check the structure of all edges const validEdges = object.edges.every( @@ -32,7 +24,7 @@ export function isSchemaResult(object: any): object is Schema { typeof edge.collection == 'string' && typeof edge.from == 'string' && typeof edge.to == 'string' && - isAttributeArray(edge.attributes), + isAttributeArray(edge.attributes) ); return validNodes && validEdges; } @@ -43,15 +35,12 @@ export function isSchemaResult(object: any): object is Schema { * Checks if an object has the structure of a SchemaAttribute. * @param {any} object The object to check. * @returns If true, the object has the structure of SchemaAttribute type. + * @deprecated //TODO remove */ -function isAttributeArray(object: any): object is Attribute[] { +function isAttributeArray(object: any): object is any[] { if (!Array.isArray(object)) { return false; } - return object.every( - (attribute) => - typeof attribute.name == 'string' && - ['string', 'int', 'bool', 'float'].includes(attribute.type), - ); + return object.every((attribute) => typeof attribute.name == 'string' && ['string', 'int', 'bool', 'float'].includes(attribute.type)); } diff --git a/libs/shared/lib/vis/shared/Types.tsx b/libs/shared/lib/vis/shared/Types.tsx index e87ed069c..186166281 100644 --- a/libs/shared/lib/vis/shared/Types.tsx +++ b/libs/shared/lib/vis/shared/Types.tsx @@ -4,7 +4,6 @@ * © Copyright Utrecht University (Department of Information and Computing Sciences) */ import { Edge, Node } from 'reactflow'; -import { Attribute } from './InputDataTypes'; /** * List of schema elements for react flow @@ -87,12 +86,7 @@ export interface AttributeAnalyticsData { onClickPlaceInQueryBuilderButton: (name: string, type: string) => void; searchForAttributes: (id: string, searchbarValue: string) => void; resetAttributeFilters: (id: string) => void; - applyAttributeFilters: ( - id: string, - category: AttributeCategory, - predicate: string, - percentage: number - ) => void; + applyAttributeFilters: (id: string, category: AttributeCategory, predicate: string, percentage: number) => void; } /** All possible options of categories of an attribute */ @@ -119,7 +113,7 @@ export interface AttributeAnalyticsPopupMenuNode extends Node { /** Typing of the attributes which are stored in the popup menu's */ export type AttributeWithData = { - attribute: Attribute; + attribute: any; category: AttributeCategory; nullAmount: number; }; diff --git a/libs/shared/lib/vis/shared/VisConfigPanel/VisConfigPanel.module.scss b/libs/shared/lib/vis/shared/VisConfigPanel/VisConfigPanel.module.scss index 65b8f3161..4904ce9db 100644 --- a/libs/shared/lib/vis/shared/VisConfigPanel/VisConfigPanel.module.scss +++ b/libs/shared/lib/vis/shared/VisConfigPanel/VisConfigPanel.module.scss @@ -52,9 +52,10 @@ $expandButtonSize: 13px; .childrenContainer { width: 100%; height: 100%; - padding: 15px; .children { + padding-left: 15px; + width: 100%; height: 100%; diff --git a/libs/shared/lib/vis/shared/VisConfigPanel/VisConfigPanel.tsx b/libs/shared/lib/vis/shared/VisConfigPanel/VisConfigPanel.tsx index 9db7a6284..4b52f9595 100644 --- a/libs/shared/lib/vis/shared/VisConfigPanel/VisConfigPanel.tsx +++ b/libs/shared/lib/vis/shared/VisConfigPanel/VisConfigPanel.tsx @@ -26,19 +26,12 @@ export default function VisConfigPanelComponent({ isLeft?: boolean; }) { const [show, setShow] = useState(false); - const containerStyle = isLeft - ? { left: show ? 0 : -width + 13, width: width } - : { right: show ? 0 : -width + 13, width: width }; + const containerStyle = isLeft ? { left: show ? 0 : -width + 13, width: width } : { right: show ? 0 : -width + 13, width: width }; const arrowStyle = isLeft ? { right: 0 } : { left: 0 }; return ( <div className={styles.container} style={containerStyle} key="placeholderKey"> - <div - className={styles.expandButton} - key="placeholderKey2" - onClick={() => (show ? setShow(false) : setShow(true))} - style={arrowStyle} - > + <div className={styles.expandButton} key="placeholderKey2" onClick={() => (show ? setShow(false) : setShow(true))} style={arrowStyle}> {/* <ArrowRightSVG className={show == isLeft ? '' : styles.arrowLeft} key="placeholderKey3" /> */} </div> <div className={styles.childrenContainer} key="placeholderKey4"> diff --git a/libs/shared/node.d.ts b/libs/shared/node.d.ts new file mode 100644 index 000000000..3d2ced5bc --- /dev/null +++ b/libs/shared/node.d.ts @@ -0,0 +1,13 @@ +interface ImportMeta { + env: { + VITE_BACKEND_URL: string; + VITE_BACKEND_WSS_URL: string; + VITE_BACKEND_USER: string; + VITE_BACKEND_SCHEMA: string; + VITE_BACKEND_QUERY: string; + VITE_STAGING: string; + VITE_KEYCLOAK_URL: string; + VITE_KEYCLOAK_REALM: string; + VITE_KEYCLOAK_CLIENT: string; + }; +} diff --git a/libs/shared/package.json b/libs/shared/package.json index 7854a1b6c..221adab0f 100644 --- a/libs/shared/package.json +++ b/libs/shared/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "build": "tsc && vite build", + "build-dev": "tsc && vite build --mode development", "lint": "eslint *.ts*", "test": "vitest run", "coverage": "vitest run --coverage" @@ -37,15 +38,18 @@ "d3": "^6.6", "deck.gl": "^8.9.19", "graphology": "^0.25.1", + "graphology-dag": "^0.3.0", "graphology-layout": "^0.6.1", "graphology-layout-forceatlas2": "^0.10.1", "graphology-layout-noverlap": "^0.4.2", + "graphology-simple-path": "^0.2.0", "graphology-types": "^0.24.7", "immer": "^10.0.2", "jspdf": "^2.5.1", "keycloak-js": "^21.1.1", "pixi.js": "^7.1.4", "react-cookie": "^4.1.1", + "react-draggable": "^4.4.5", "react-grid-layout": "^1.3.4", "react-json-view": "^1.21.3", "react-router-dom": "^6.8.1", @@ -71,10 +75,10 @@ "@types/node": "18.13.0", "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", + "@types/react-window": "^1.8.5", "@typescript-eslint/eslint-plugin": "~5.52.0", "@typescript-eslint/parser": "~5.52.0", "@vitejs/plugin-react": "^3.1.0", - "canvas": "^2.11.0", "cytoscape": "^3.23.0", "cytoscape-cise": "^1.0.0", "cytoscape-cose-bilkent": "^4.1.0", diff --git a/libs/shared/tsconfig.json b/libs/shared/tsconfig.json index 855b96076..0f1e0dd82 100644 --- a/libs/shared/tsconfig.json +++ b/libs/shared/tsconfig.json @@ -1,34 +1,35 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "display": "React Library", - "target": "ESNext", - "compilerOptions": { - "composite": false, - "inlineSources": false, - "noUnusedLocals": false, - "noUnusedParameters": false, - "preserveWatchOutput": true, - "jsx": "react-jsx", - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["ES2017", "DOM", "DOM.Iterable", "ESNext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "node", - "resolveJsonModule": true, - "noEmit": true, - "baseUrl": ".", - "paths": { - "@graphpolaris/shared/lib/*": ["./lib/*"] - } - }, - "exclude": ["dist", "build", "node_modules"], - "include": ["src", "lib"], - "files": ["./node.d.ts"], - "references": [{ "path": "./tsconfig.node.json" }] -} +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "React Library", + "target": "ESNext", + "compilerOptions": { + "composite": false, + "inlineSources": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "jsx": "react-jsx", + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["ES2017", "DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "node", + "resolveJsonModule": true, + "noEmit": true, + "baseUrl": ".", + "noImplicitOverride": false, + "paths": { + "@graphpolaris/shared/lib/*": ["./lib/*"] + } + }, + "exclude": ["dist", "build", "node_modules"], + "include": ["src", "lib"], + "files": ["./node.d.ts"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/libs/shared/vite.config.ts b/libs/shared/vite.config.ts index 3749ed152..2f263c107 100644 --- a/libs/shared/vite.config.ts +++ b/libs/shared/vite.config.ts @@ -1,10 +1,11 @@ /// <reference types="vitest" /> import react from '@vitejs/plugin-react'; -import { resolve } from 'path' +import { resolve } from 'path'; import { defineConfig } from 'vite'; import dts from 'vite-plugin-dts'; -import path from 'path' +import path from 'path'; +import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [ @@ -12,7 +13,9 @@ export default defineConfig({ dts({ insertTypesEntry: true, }), + tsconfigPaths(), ], + optimizeDeps: {}, build: { lib: { entry: resolve(__dirname, './index.ts'), @@ -20,6 +23,10 @@ export default defineConfig({ formats: ['es', 'umd'], fileName: (format) => `@graphpolaris-shared.${format}.js`, }, + commonjsOptions: { + include: [], + transformMixedEsModules: true, + }, rollupOptions: { external: ['react', 'react-dom'], output: { @@ -32,20 +39,17 @@ export default defineConfig({ }, resolve: { alias: { - '@graphpolaris/shared/lib': path.resolve(__dirname, './lib') + '@graphpolaris/shared/lib': path.resolve(__dirname, './lib'), }, }, test: { - setupFiles: ['./vitest.setup.ts'], - environment: "happy-dom", - deps: { - // inline: ['vitest-canvas-mock'], - }, - threads: false, + environment: 'happy-dom', + deps: {}, + threads: true, environmentOptions: { jsdom: { resources: 'usable', }, }, - } -}); \ No newline at end of file + }, +}); diff --git a/libs/shared/vitest.setup.ts b/libs/shared/vitest.setup.ts deleted file mode 100644 index b1b48f7de..000000000 --- a/libs/shared/vitest.setup.ts +++ /dev/null @@ -1,2 +0,0 @@ - -// import 'vitest-canvas-mock' \ No newline at end of file diff --git a/libs/storybook/.storybook/preview.ts b/libs/storybook/.storybook/preview.ts index 1c372b694..8c2a141ca 100644 --- a/libs/storybook/.storybook/preview.ts +++ b/libs/storybook/.storybook/preview.ts @@ -1,8 +1,8 @@ -import type { Preview } from "@storybook/react"; +import type { Preview } from '@storybook/react'; const preview: Preview = { parameters: { - actions: { argTypesRegex: "^on[A-Z].*" }, + actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, diff --git a/libs/storybook/package.json b/libs/storybook/package.json index 99b3009c7..a12ddd4ca 100644 --- a/libs/storybook/package.json +++ b/libs/storybook/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "sb": "storybook dev -p 6007 --no-open", + "sb": "storybook dev -p 6008 --no-open", "storybook": "storybook dev -p 6007", "build-storybook": "storybook build" }, diff --git a/libs/storybook/src/App.tsx b/libs/storybook/src/App.tsx index 776eaa027..3bce38974 100644 --- a/libs/storybook/src/App.tsx +++ b/libs/storybook/src/App.tsx @@ -1,10 +1,10 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import { useState } from 'react'; +import reactLogo from './assets/react.svg'; +import viteLogo from '/vite.svg'; +import './App.css'; function App() { - const [count, setCount] = useState(0) + const [count, setCount] = useState(0); return ( <div className="App"> @@ -18,18 +18,14 @@ function App() { </div> <h1>Vite + React</h1> <div className="card"> - <button onClick={() => setCount((count) => count + 1)}> - count is {count} - </button> + <button onClick={() => setCount((count) => count + 1)}>count is {count}</button> <p> Edit <code>src/App.tsx</code> and save to test HMR </p> </div> - <p className="read-the-docs"> - Click on the Vite and React logos to learn more - </p> + <p className="read-the-docs">Click on the Vite and React logos to learn more</p> </div> - ) + ); } -export default App +export default App; diff --git a/libs/storybook/src/main.tsx b/libs/storybook/src/main.tsx index 791f139e2..a2bf01b87 100644 --- a/libs/storybook/src/main.tsx +++ b/libs/storybook/src/main.tsx @@ -1,10 +1,10 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App' -import './index.css' +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <React.StrictMode> <App /> - </React.StrictMode>, -) + </React.StrictMode> +); diff --git a/libs/storybook/src/stories/Button.tsx b/libs/storybook/src/stories/Button.tsx index c33be6ec5..66f8fd32c 100644 --- a/libs/storybook/src/stories/Button.tsx +++ b/libs/storybook/src/stories/Button.tsx @@ -27,13 +27,7 @@ interface ButtonProps { /** * Primary UI component for user interaction */ -export const Button = ({ - primary = false, - size = 'medium', - backgroundColor, - label, - ...props -}: ButtonProps) => { +export const Button = ({ primary = false, size = 'medium', backgroundColor, label, ...props }: ButtonProps) => { const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; return ( <button diff --git a/libs/storybook/src/stories/Header.tsx b/libs/storybook/src/stories/Header.tsx index dc3f3c19c..e13c36bd9 100644 --- a/libs/storybook/src/stories/Header.tsx +++ b/libs/storybook/src/stories/Header.tsx @@ -20,18 +20,9 @@ export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps <div> <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> <g fill="none" fillRule="evenodd"> - <path - d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z" - fill="#FFF" - /> - <path - d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z" - fill="#555AB9" - /> - <path - d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z" - fill="#91BAF8" - /> + <path d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z" fill="#FFF" /> + <path d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z" fill="#555AB9" /> + <path d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z" fill="#91BAF8" /> </g> </svg> <h1>Acme</h1> diff --git a/libs/storybook/src/stories/Page.tsx b/libs/storybook/src/stories/Page.tsx index ec054e813..95b27737a 100644 --- a/libs/storybook/src/stories/Page.tsx +++ b/libs/storybook/src/stories/Page.tsx @@ -29,19 +29,12 @@ export const Page: React.FC = () => { process starting with atomic components and ending with pages. </p> <p> - Render pages with mock data. This makes it easy to build and review page states without - needing to navigate to them in your app. Here are some handy patterns for managing page - data in Storybook: + Render pages with mock data. This makes it easy to build and review page states without needing to navigate to them in your app. + Here are some handy patterns for managing page data in Storybook: </p> <ul> - <li> - Use a higher-level connected component. Storybook helps you compose such data from the - "args" of child component stories - </li> - <li> - Assemble data in the page component from your services. You can mock these services out - using Storybook. - </li> + <li>Use a higher-level connected component. Storybook helps you compose such data from the "args" of child component stories</li> + <li>Assemble data in the page component from your services. You can mock these services out using Storybook.</li> </ul> <p> Get a guided tutorial on component-driven development at{' '} diff --git a/libs/storybook/vite.config.ts b/libs/storybook/vite.config.ts index a06d50222..04cdcde5d 100644 --- a/libs/storybook/vite.config.ts +++ b/libs/storybook/vite.config.ts @@ -1,7 +1,6 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import sassDts from 'vite-plugin-sass-dts' - +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import sassDts from 'vite-plugin-sass-dts'; // https://vitejs.dev/config/ export default defineConfig({ @@ -9,17 +8,17 @@ export default defineConfig({ build: { rollupOptions: { // https://rollupjs.org/guide/en/#big-list-of-options - input: "src/index.ts", + input: 'src/index.ts', output: [ { - format: "cjs", - entryFileNames: "index.js", + format: 'cjs', + entryFileNames: 'index.js', }, { - format: "esm", - entryFileNames: "index.mjs", + format: 'esm', + entryFileNames: 'index.mjs', }, ], }, }, -}) +}); diff --git a/libs/workspace/eslint-config-custom/index.js b/libs/workspace/eslint-config-custom/index.js index 6024e8d63..c7f513170 100644 --- a/libs/workspace/eslint-config-custom/index.js +++ b/libs/workspace/eslint-config-custom/index.js @@ -1,6 +1,6 @@ module.exports = { - extends: ["next", "turbo", "prettier"], + extends: ['next', 'turbo', 'prettier'], rules: { - "@next/next/no-html-link-for-pages": "off", + '@next/next/no-html-link-for-pages': 'off', }, }; diff --git a/nginx/nginx.conf b/nginx/nginx.conf index ef2e21a89..e053b4e8a 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -1,16 +1,16 @@ -server { - listen 80; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - error_page 500 502 503 504 /50x.html; - - location = /50x.html { - root /usr/share/nginx/html; - } - -} +server { + listen 4200; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + +} diff --git a/package.json b/package.json index f4b9b79bb..0fdb684e1 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,12 @@ ], "scripts": { "build": "turbo run build --no-daemon", + "build-dev": "turbo run build-dev --no-daemon", "dev": "turbo run dev --no-daemon", "sb": "turbo run sb --no-daemon", "lint": "turbo run lint --no-daemon", "test": "turbo run test --no-daemon", - "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "format": "prettier --write \"**/*.{ts,tsx,md,js}\"", "prepare": "husky install" }, "devDependencies": { @@ -22,7 +23,8 @@ "eslint-config-custom": "workspace:*", "husky": "^8.0.0", "prettier": "latest", - "turbo": "latest" + "turbo": "latest", + "vitest": "^0.29.2" }, "engines": { "node": ">=14.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ffca8108..73e36f4b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,10 +25,13 @@ importers: version: 8.0.3 prettier: specifier: latest - version: 2.8.8 + version: 3.0.0 turbo: specifier: latest - version: 1.10.3 + version: 1.10.8 + vitest: + specifier: ^0.29.2 + version: 0.29.4(happy-dom@8.9.0)(jsdom@21.1.1)(sass@1.59.3) apps/docs: dependencies: @@ -229,6 +232,9 @@ importers: graphology: specifier: ^0.25.1 version: 0.25.1(graphology-types@0.24.7) + graphology-dag: + specifier: ^0.3.0 + version: 0.3.0(graphology-types@0.24.7) graphology-layout: specifier: ^0.6.1 version: 0.6.1(graphology-types@0.24.7) @@ -238,6 +244,9 @@ importers: graphology-layout-noverlap: specifier: ^0.4.2 version: 0.4.2(graphology-types@0.24.7) + graphology-simple-path: + specifier: ^0.2.0 + version: 0.2.0(graphology-types@0.24.7) graphology-types: specifier: ^0.24.7 version: 0.24.7 @@ -256,6 +265,9 @@ importers: react-cookie: specifier: ^4.1.1 version: 4.1.1(react@18.2.0) + react-draggable: + specifier: ^4.4.5 + version: 4.4.5(react-dom@18.2.0)(react@18.2.0) react-grid-layout: specifier: ^1.3.4 version: 1.3.4(react-dom@18.2.0)(react@18.2.0) @@ -326,6 +338,9 @@ importers: '@types/react-dom': specifier: ^18.0.10 version: 18.0.11 + '@types/react-window': + specifier: ^1.8.5 + version: 1.8.5 '@typescript-eslint/eslint-plugin': specifier: ~5.52.0 version: 5.52.0(@typescript-eslint/parser@5.52.0)(eslint@7.32.0)(typescript@4.9.5) @@ -335,9 +350,6 @@ importers: '@vitejs/plugin-react': specifier: ^3.1.0 version: 3.1.0(vite@4.2.1) - canvas: - specifier: ^2.11.0 - version: 2.11.0 cytoscape-cise: specifier: ^1.0.0 version: 1.0.0(cytoscape@3.23.0) @@ -367,7 +379,7 @@ importers: version: 8.7.0(eslint@7.32.0) eslint-config-turbo: specifier: latest - version: 1.10.3(eslint@7.32.0) + version: 1.10.9(eslint@7.32.0) eslint-plugin-import: specifier: 2.27.5 version: 2.27.5(@typescript-eslint/parser@5.52.0)(eslint-import-resolver-typescript@2.7.1)(eslint@7.32.0) @@ -388,7 +400,7 @@ importers: version: 8.9.0 jsdom: specifier: ^21.1.1 - version: 21.1.1(canvas@2.11.0) + version: 21.1.1 postcss: specifier: ^8.4.21 version: 8.4.21 @@ -557,7 +569,7 @@ importers: version: 8.7.0(eslint@7.32.0) eslint-config-turbo: specifier: latest - version: 1.10.3(eslint@7.32.0) + version: 1.10.9(eslint@7.32.0) eslint-plugin-react: specifier: 7.31.8 version: 7.31.8(eslint@7.32.0) @@ -3207,24 +3219,6 @@ packages: resolution: {integrity: sha512-7hFhtkb0KTLEls+TRw/rWayq5EeHtTaErgm/NskVoXmtgAQu/9D299aeyj6mzAR/6XUnYRp2lU+4IcrYRFjVsQ==} dev: false - /@mapbox/node-pre-gyp@1.0.10: - resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==} - hasBin: true - dependencies: - detect-libc: 2.0.1 - https-proxy-agent: 5.0.1 - make-dir: 3.1.0 - node-fetch: 2.6.9 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.3.8 - tar: 6.1.13 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@mapbox/point-geometry@0.1.0: resolution: {integrity: sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==} dev: false @@ -6661,6 +6655,12 @@ packages: '@types/react': 18.0.28 dev: false + /@types/react-window@1.8.5: + resolution: {integrity: sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==} + dependencies: + '@types/react': 18.0.28 + dev: true + /@types/react@18.0.28: resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} dependencies: @@ -7052,10 +7052,6 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true - /abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: true - /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -7693,19 +7689,6 @@ packages: /caniuse-lite@1.0.30001466: resolution: {integrity: sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==} - /canvas@2.11.0: - resolution: {integrity: sha512-bdTjFexjKJEwtIo0oRx8eD4G2yWoUOXP9lj279jmQ2zMnTQhT8C3512OKz3s+ZOaQlLbE7TuVvRDYDB3Llyy5g==} - engines: {node: '>=6'} - requiresBuild: true - dependencies: - '@mapbox/node-pre-gyp': 1.0.10 - nan: 2.17.0 - simple-get: 3.1.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /canvg@3.0.10: resolution: {integrity: sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==} engines: {node: '>=10.0.0'} @@ -8717,13 +8700,6 @@ packages: - react-dom dev: false - /decompress-response@4.2.1: - resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} - engines: {node: '>=8'} - dependencies: - mimic-response: 2.1.0 - dev: true - /deep-eql@4.1.3: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} @@ -8839,11 +8815,6 @@ packages: engines: {node: '>=8'} dev: true - /detect-libc@2.0.1: - resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} - engines: {node: '>=8'} - dev: true - /detect-package-manager@2.0.1: resolution: {integrity: sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==} engines: {node: '>=12'} @@ -8940,7 +8911,6 @@ packages: /dotenv@16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} - dev: true /draco3d@1.5.5: resolution: {integrity: sha512-JVuNV0EJzD3LBYhGyIXJLeBID/EVtmFO1ZNhAYflTgiMiAJlbhXQmRRda/azjc8MRVMHh0gqGhiqHUo5dIXM8Q==} @@ -9271,13 +9241,13 @@ packages: dependencies: eslint: 7.32.0 - /eslint-config-turbo@1.10.3(eslint@7.32.0): - resolution: {integrity: sha512-ggzPfTJfMsMS383oZ4zfTP1zQvyMyiigOQJRUnLt1nqII6SKkTzdKZdwmXRDHU24KFwUfEFtT6c8vnm2VhL0uQ==} + /eslint-config-turbo@1.10.9(eslint@7.32.0): + resolution: {integrity: sha512-YA5QWxWte/NiRJL0/Cv7aATfIvS5sUAuyD6ZuyTZEzwyU7E6FUXGo44amjf9INkyj96HrJ2nYWoFkCRx3vs6Ag==} peerDependencies: eslint: '>6.6.0' dependencies: eslint: 7.32.0 - eslint-plugin-turbo: 1.10.3(eslint@7.32.0) + eslint-plugin-turbo: 1.10.9(eslint@7.32.0) /eslint-import-resolver-node@0.3.7: resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} @@ -9420,11 +9390,12 @@ packages: semver: 6.3.0 string.prototype.matchall: 4.0.8 - /eslint-plugin-turbo@1.10.3(eslint@7.32.0): - resolution: {integrity: sha512-g3Mnnk7el1FqxHfqbE/MayLvCsYjA/vKmAnUj66kV4AlM7p/EZqdt42NMcMSKtDVEm0w+utQkkzWG2Xsa0Pd/g==} + /eslint-plugin-turbo@1.10.9(eslint@7.32.0): + resolution: {integrity: sha512-o8Nga4WFMvzF0lo3d3UyjGli2JOUn/4SRtRdvcf4EA9/TPotU/NUHqO16Cp0SHZJG/tGYIy5LY1O/EO7Mxbd1A==} peerDependencies: eslint: '>6.6.0' dependencies: + dotenv: 16.0.3 eslint: 7.32.0 /eslint-scope@5.1.1: @@ -10192,6 +10163,16 @@ packages: lodash: 4.17.21 dev: true + /graphology-dag@0.3.0(graphology-types@0.24.7): + resolution: {integrity: sha512-dg4JPb+/LDEbDinZIj7ezWzlEXDRokshdpTL8oAuftE9Uy0uTKGOKSmYULY8p3j/vw0HB31Wog9T/kpqprUQpg==} + peerDependencies: + graphology-types: '>=0.19.0' + dependencies: + graphology-types: 0.24.7 + graphology-utils: 2.5.2(graphology-types@0.24.7) + mnemonist: 0.39.5 + dev: false + /graphology-generators@0.11.2(graphology-types@0.24.7): resolution: {integrity: sha512-hx+F0OZRkVdoQ0B1tWrpxoakmHZNex0c6RAoR0PrqJ+6fz/gz6CQ88Qlw78C6yD9nlZVRgepIoDYhRTFV+bEHg==} peerDependencies: @@ -10263,9 +10244,27 @@ packages: mnemonist: 0.39.5 dev: true + /graphology-simple-path@0.2.0(graphology-types@0.24.7): + resolution: {integrity: sha512-4cGMWbVuJM0zlKDUx6dS6JGGLddizDPe8PsTokXVz2eTeHYg07qa5TgwIco15ta2RMM05+xy8N1mFnpS85y0kw==} + peerDependencies: + graphology-types: '>=0.20.0' + dependencies: + graphology-types: 0.24.7 + graphology-utils: 1.8.0(graphology-types@0.24.7) + mnemonist: 0.39.5 + dev: false + /graphology-types@0.24.7: resolution: {integrity: sha512-tdcqOOpwArNjEr0gNQKCXwaNCWnQJrog14nJNQPeemcLnXQUUGrsCWpWkVKt46zLjcS6/KGoayeJfHHyPDlvwA==} + /graphology-utils@1.8.0(graphology-types@0.24.7): + resolution: {integrity: sha512-Pa7SW30OMm8fVtyH49b3GJ/uxlMHGfXly50wIhlcc7ZoX9ahZa7sPBz+obo4WZClrRV6wh3tIu0GJoI42eao1A==} + peerDependencies: + graphology-types: '>=0.19.0' + dependencies: + graphology-types: 0.24.7 + dev: false + /graphology-utils@2.5.2(graphology-types@0.24.7): resolution: {integrity: sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==} peerDependencies: @@ -11053,7 +11052,7 @@ packages: - supports-color dev: true - /jsdom@21.1.1(canvas@2.11.0): + /jsdom@21.1.1: resolution: {integrity: sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==} engines: {node: '>=14'} peerDependencies: @@ -11065,7 +11064,6 @@ packages: abab: 2.0.6 acorn: 8.8.2 acorn-globals: 7.0.1 - canvas: 2.11.0 cssstyle: 3.0.0 data-urls: 4.0.0 decimal.js: 10.4.3 @@ -11617,11 +11615,6 @@ packages: engines: {node: '>=6'} dev: true - /mimic-response@2.1.0: - resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} - engines: {node: '>=8'} - dev: true - /min-document@2.19.0: resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} dependencies: @@ -11753,11 +11746,6 @@ packages: thenify-all: 1.6.0 dev: true - /nan@2.17.0: - resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} - requiresBuild: true - dev: true - /nanoid@3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -11879,14 +11867,6 @@ packages: /node-releases@2.0.10: resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} - /nopt@5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} - hasBin: true - dependencies: - abbrev: 1.1.1 - dev: true - /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -12547,6 +12527,12 @@ packages: hasBin: true dev: true + /prettier@3.0.0: + resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} + engines: {node: '>=14'} + hasBin: true + dev: true + /pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -13614,18 +13600,6 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true - /simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - dev: true - - /simple-get@3.1.1: - resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} - dependencies: - decompress-response: 4.2.1 - once: 1.4.0 - simple-concat: 1.0.1 - dev: true - /simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: @@ -14385,65 +14359,65 @@ packages: tslib: 1.14.1 typescript: 4.9.5 - /turbo-darwin-64@1.10.3: - resolution: {integrity: sha512-IIB9IomJGyD3EdpSscm7Ip1xVWtYb7D0x7oH3vad3gjFcjHJzDz9xZ/iw/qItFEW+wGFcLSRPd+1BNnuLM8AsA==} + /turbo-darwin-64@1.10.8: + resolution: {integrity: sha512-FOK3qrLZE2Yq7/2DkAnAzghisGQroZJs85Rui3IXM/2e7rTtBADmU9w36d4k0Yw7RHEiOo8U4eAYUl52OWRwJQ==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64@1.10.3: - resolution: {integrity: sha512-SBNmOZU9YEB0eyNIxeeQ+Wi0Ufd+nprEVp41rgUSRXEIpXjsDjyBnKnF+sQQj3+FLb4yyi/yZQckB+55qXWEsw==} + /turbo-darwin-arm64@1.10.8: + resolution: {integrity: sha512-8mbgH8oBycusa8RnbHlvrpHxfZsgNrk6CXMu/KJECpajYT3nSOMK2Rrs+422HqLDTVUw4GAqmTr26nUx8yJoyA==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-linux-64@1.10.3: - resolution: {integrity: sha512-kvAisGKE7xHJdyMxZLvg53zvHxjqPK1UVj4757PQqtx9dnjYHSc8epmivE6niPgDHon5YqImzArCjVZJYpIGHQ==} + /turbo-linux-64@1.10.8: + resolution: {integrity: sha512-eJ1ND3LuILw28gd+9f3Ews7Eika9WOxp+/PxJI+EPHseTrbLMLYqSPAunmZdOx840Pq0Sk5j4Nik7NCzuCWXkg==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm64@1.10.3: - resolution: {integrity: sha512-Qgaqln0IYRgyL0SowJOi+PNxejv1I2xhzXOI+D+z4YHbgSx87ox1IsALYBlK8VRVYY8VCXl+PN12r1ioV09j7A==} + /turbo-linux-arm64@1.10.8: + resolution: {integrity: sha512-3+pVaOzGP/5GFvQakxuHDMsj43Y6bmaq5/84tvgGL0FgtKpsQvBfdaDs12HX5cb/zUnd2/jdQPNiGJwVeC/McA==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-windows-64@1.10.3: - resolution: {integrity: sha512-rbH9wManURNN8mBnN/ZdkpUuTvyVVEMiUwFUX4GVE5qmV15iHtZfDLUSGGCP2UFBazHcpNHG1OJzgc55GFFrUw==} + /turbo-windows-64@1.10.8: + resolution: {integrity: sha512-LdryI+ZQsVrW4hWZw5G5vJz0syjWxyc0tnieZRefy+d9Ti1du/qCYLP0KQRgL9Yuh1klbH/tzmx70upGARgWKQ==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64@1.10.3: - resolution: {integrity: sha512-ThlkqxhcGZX39CaTjsHqJnqVe+WImjX13pmjnpChz6q5HHbeRxaJSFzgrHIOt0sUUVx90W/WrNRyoIt/aafniw==} + /turbo-windows-arm64@1.10.8: + resolution: {integrity: sha512-whHnhM84KIa2Ly/fcw2Ujw2Rr/9wh8ynAdZ9bdvZoZKAbOr3tXKft0tmy50jQ6IsNr6Cj0XD4cuSTKhvqoGtYA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo@1.10.3: - resolution: {integrity: sha512-U4gKCWcKgLcCjQd4Pl8KJdfEKumpyWbzRu75A6FCj6Ctea1PIm58W6Ltw1QXKqHrl2pF9e1raAskf/h6dlrPCA==} + /turbo@1.10.8: + resolution: {integrity: sha512-lmPKkeRMC/3gjTVxICt93A8zAzjGjbZINdekjzivn4g/rOjpHVNuOuVANU5L4H4R1bzQr8FFvZNQeQaElOjz/Q==} hasBin: true requiresBuild: true optionalDependencies: - turbo-darwin-64: 1.10.3 - turbo-darwin-arm64: 1.10.3 - turbo-linux-64: 1.10.3 - turbo-linux-arm64: 1.10.3 - turbo-windows-64: 1.10.3 - turbo-windows-arm64: 1.10.3 + turbo-darwin-64: 1.10.8 + turbo-darwin-arm64: 1.10.8 + turbo-linux-64: 1.10.8 + turbo-linux-arm64: 1.10.8 + turbo-windows-64: 1.10.8 + turbo-windows-arm64: 1.10.8 dev: true /type-check@0.3.2: @@ -15041,7 +15015,7 @@ packages: chai: 4.3.7 debug: 4.3.4(supports-color@5.5.0) happy-dom: 8.9.0 - jsdom: 21.1.1(canvas@2.11.0) + jsdom: 21.1.1 local-pkg: 0.4.3 pathe: 1.1.0 picocolors: 1.0.0 diff --git a/turbo.json b/turbo.json index 4ce7ad057..ad609769f 100644 --- a/turbo.json +++ b/turbo.json @@ -1,20 +1,24 @@ -{ - "$schema": "https://turbo.build/schema.json", - "globalDependencies": ["**/.env.*local"], - "pipeline": { - "build": { - "dependsOn": ["^build"], - "outputs": ["dist/**", ".next/**"] - }, - "lint": { - "outputs": [] - }, - "test": {}, - "dev": { - "cache": false - }, - "sb": { - "outputs": ["storybook-static/**"] - } - } -} +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": ["**/.env*"], + "pipeline": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", ".next/**"] + }, + "build-dev": { + "dependsOn": ["^build-dev"], + "outputs": ["dist/**", ".next/**"] + }, + "lint": { + "outputs": [] + }, + "test": {}, + "dev": { + "cache": false + }, + "sb": { + "outputs": ["storybook-static/**"] + } + } +} -- GitLab