diff --git a/packages/admin-panel/package.json b/packages/admin-panel/package.json index 60671a8a17..fecef62936 100644 --- a/packages/admin-panel/package.json +++ b/packages/admin-panel/package.json @@ -22,12 +22,15 @@ "lint:fix": "yarn lint --fix", "start": "yarn package:start:react-scripts", "start-dev": "yarn start", - "start-fullstack": "npm-run-all -c -l -p start-central-server start-entity-server start-report-server start-admin-panel-server start-dev", + "start-fullstack": "npm-run-all -c -l -p start-servers start-frontend", "start-central-server": "yarn workspace @tupaia/central-server start-dev", "start-entity-server": "yarn workspace @tupaia/entity-server start-dev", "start-report-server": "yarn workspace @tupaia/report-server start-dev", "start-admin-panel-server": "yarn workspace @tupaia/admin-panel-server start-dev", - "test": "echo No tests" + "test": "echo No tests", + "start-ui-components": "yarn workspace @tupaia/ui-components build-dev:watch", + "start-frontend": "npm-run-all -c -l -p start-ui-components start-dev", + "start-servers": "npm-run-all -c -l -p start-central-server start-entity-server start-report-server start-admin-panel-server" }, "browserslist": [ "defaults" diff --git a/packages/admin-panel/src/VizBuilderApp/api/queries/index.js b/packages/admin-panel/src/VizBuilderApp/api/queries/index.js index 50c875fe87..2829cfbf32 100644 --- a/packages/admin-panel/src/VizBuilderApp/api/queries/index.js +++ b/packages/admin-panel/src/VizBuilderApp/api/queries/index.js @@ -9,5 +9,6 @@ export * from './useUser'; export * from './useDashboardVisualisation'; export * from './useMapOverlays'; export * from './useCountries'; +export * from './useEntities'; export * from './useSearchPermissionGroups'; export * from './useSearchTransformSchemas'; diff --git a/packages/admin-panel/src/VizBuilderApp/api/queries/useEntities.js b/packages/admin-panel/src/VizBuilderApp/api/queries/useEntities.js new file mode 100644 index 0000000000..d9a118304e --- /dev/null +++ b/packages/admin-panel/src/VizBuilderApp/api/queries/useEntities.js @@ -0,0 +1,25 @@ +/** + * Tupaia + * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd + */ +import { useQuery } from 'react-query'; +import { stringifyQuery } from '@tupaia/utils'; +import { get } from '../api'; +import { DEFAULT_REACT_QUERY_OPTIONS } from '../constants'; + +export const useEntities = search => + useQuery( + ['entities', search], + async () => { + const endpoint = stringifyQuery(undefined, `entities`, { + columns: JSON.stringify(['name', 'code']), + filter: JSON.stringify({ + name: { comparator: 'ilike', comparisonValue: `%${search}%`, castAs: 'text' }, + }), + }); + return get(endpoint); + }, + { + ...DEFAULT_REACT_QUERY_OPTIONS, + }, + ); diff --git a/packages/admin-panel/src/dataTables/components/PreviewFilters/PreviewFilters.js b/packages/admin-panel/src/dataTables/components/PreviewFilters/PreviewFilters.js index dc2244c409..099773a3c1 100644 --- a/packages/admin-panel/src/dataTables/components/PreviewFilters/PreviewFilters.js +++ b/packages/admin-panel/src/dataTables/components/PreviewFilters/PreviewFilters.js @@ -31,7 +31,6 @@ export const PreviewFilters = ({ params, onChange, runtimeParams }) => { { onChange(p.name, newValue); diff --git a/packages/admin-panel/src/dataTables/components/PreviewFilters/filters/OrganisationUnitCodesField.js b/packages/admin-panel/src/dataTables/components/PreviewFilters/filters/OrganisationUnitCodesField.js index 43e86e2c2e..f94d974e1e 100644 --- a/packages/admin-panel/src/dataTables/components/PreviewFilters/filters/OrganisationUnitCodesField.js +++ b/packages/admin-panel/src/dataTables/components/PreviewFilters/filters/OrganisationUnitCodesField.js @@ -3,26 +3,22 @@ * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd */ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { ParameterType } from '../../editing'; -import { useLocations } from '../../../../VizBuilderApp/api'; +import { useEntities } from '../../../../VizBuilderApp/api'; import { Autocomplete } from '../../../../autocomplete'; +import { useDebounce } from '../../../../utilities'; import { getArrayFieldValue } from './utils'; -export const OrganisationUnitCodesField = ({ name, onChange, runtimeParams }) => { - const { hierarchy = 'explore' } = runtimeParams; +export const OrganisationUnitCodesField = ({ name, onChange }) => { const [searchTerm, setSearchTerm] = useState(''); + const debouncedSearchTerm = useDebounce(searchTerm, 100); const [selectedOptions, setSelectedOptions] = useState([]); // [{code:"DL", name:"Demo Land"}] - const { data: locations = [], isLoading } = useLocations(hierarchy, searchTerm); - const limitedLocations = locations.slice(0, 20); // limit the options to 20 to stop the ui jamming - - useEffect(() => { - setSelectedOptions([]); - onChange([]); - }, [hierarchy]); // hierarchy determines entity relation visibility + const { data: entities = [], isLoading } = useEntities(debouncedSearchTerm); + const limitedLocations = entities.slice(0, 20); // limit the options to 20 to stop the ui jamming return ( OrganisationUnitCodesField.propTypes = { ...ParameterType, onChange: PropTypes.func.isRequired, - runtimeParams: PropTypes.object, -}; - -OrganisationUnitCodesField.defaultProps = { - runtimeParams: {}, }; diff --git a/packages/admin-panel/src/editor/actions.js b/packages/admin-panel/src/editor/actions.js index 30c53d0a44..8a93ad57a7 100644 --- a/packages/admin-panel/src/editor/actions.js +++ b/packages/admin-panel/src/editor/actions.js @@ -45,6 +45,12 @@ export const openBulkEditModal = ( type: EDITOR_DATA_FETCH_SUCCESS, recordData: response.body, }); + dispatch({ + type: EDITOR_OPEN, + fields, + recordData: response.body, + endpoint: bulkUpdateEndpoint, + }); } catch (error) { dispatch({ type: EDITOR_ERROR, diff --git a/packages/admin-panel/src/pages/resources/DataTablesPage.js b/packages/admin-panel/src/pages/resources/DataTablesPage.js index 0c7344f658..662a87dde6 100644 --- a/packages/admin-panel/src/pages/resources/DataTablesPage.js +++ b/packages/admin-panel/src/pages/resources/DataTablesPage.js @@ -62,6 +62,14 @@ const COLUMNS = [ }, }, }, + { + Header: 'Delete', + source: 'id', + type: 'delete', + actionConfig: { + endpoint: DATA_TABLES_ENDPOINT, + }, + }, ]; const CREATE_CONFIG = { diff --git a/packages/central-server/src/apiV2/accessRequests/assertAccessRequestPermissions.js b/packages/central-server/src/apiV2/accessRequests/assertAccessRequestPermissions.js index c60e4ebb9f..13948c9a10 100644 --- a/packages/central-server/src/apiV2/accessRequests/assertAccessRequestPermissions.js +++ b/packages/central-server/src/apiV2/accessRequests/assertAccessRequestPermissions.js @@ -5,7 +5,7 @@ import { hasBESAdminAccess, BES_ADMIN_PERMISSION_GROUP } from '../../permissions'; import { - getAdminPanelAllowedEntityIds, + getAdminPanelAllowedCountryIds, getAdminPanelAllowedCountryCodes, mergeFilter, } from '../utilities'; @@ -76,7 +76,7 @@ export const createAccessRequestDBFilter = async (accessPolicy, models, criteria // If we don't have BES Admin access, add a filter to the SQL query const dbConditions = { ...criteria }; dbConditions['access_request.entity_id'] = mergeFilter( - await getAdminPanelAllowedEntityIds(accessPolicy, models), + await getAdminPanelAllowedCountryIds(accessPolicy, models), dbConditions['access_request.entity_id'], ); diff --git a/packages/central-server/src/apiV2/dataGroups/assertDataGroupPermissions.js b/packages/central-server/src/apiV2/dataGroups/assertDataGroupPermissions.js index 90b0805cc8..aef5ffe25c 100644 --- a/packages/central-server/src/apiV2/dataGroups/assertDataGroupPermissions.js +++ b/packages/central-server/src/apiV2/dataGroups/assertDataGroupPermissions.js @@ -5,14 +5,10 @@ import { QUERY_CONJUNCTIONS } from '@tupaia/database'; import { hasBESAdminAccess } from '../../permissions'; +import { getPermissionListWithWildcard } from '../utilities'; const { RAW } = QUERY_CONJUNCTIONS; -const getPermissionListWithWildcard = async accessPolicy => { - const userPermissionGroups = accessPolicy.getPermissionGroups(); - return ['*', ...userPermissionGroups]; -}; - export const assertDataGroupGETPermissions = async (accessPolicy, models, dataGroupId) => { if (await assertDataGroupPermissions(accessPolicy, models, dataGroupId, 'some')) { return true; diff --git a/packages/central-server/src/apiV2/dataTables/CreateDataTables.js b/packages/central-server/src/apiV2/dataTables/CreateDataTables.js deleted file mode 100644 index fde721c0f9..0000000000 --- a/packages/central-server/src/apiV2/dataTables/CreateDataTables.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Tupaia - * Copyright (c) 2017 - 2021 Beyond Essential Systems Pty Ltd - */ - -import { CreateHandler } from '../CreateHandler'; -import { - assertAnyPermissions, - assertAdminPanelAccess, - assertBESAdminAccess, -} from '../../permissions'; - -export class CreateDataTables extends CreateHandler { - async assertUserHasAccess() { - await this.assertPermissions( - assertAnyPermissions( - [assertBESAdminAccess, assertAdminPanelAccess], - 'You need either BES Admin or Tupaia Admin Panel access to create a Data Table', - ), - ); - } - - async createRecord() { - return this.insertRecord(); - } -} diff --git a/packages/central-server/src/apiV2/dataTables/GETDataTables.js b/packages/central-server/src/apiV2/dataTables/GETDataTables.js index 06e1676061..3cb9c5668c 100644 --- a/packages/central-server/src/apiV2/dataTables/GETDataTables.js +++ b/packages/central-server/src/apiV2/dataTables/GETDataTables.js @@ -14,10 +14,10 @@ export class GETDataTables extends GETHandler { permissionsFilteredInternally = true; async findSingleRecord(dataTableId, options) { - const dataGroupPermissionChecker = accessPolicy => + const dataTablePermissionChecker = accessPolicy => assertDataTableGETPermissions(accessPolicy, this.models, dataTableId); await this.assertPermissions( - assertAnyPermissions([assertBESAdminAccess, dataGroupPermissionChecker]), + assertAnyPermissions([assertBESAdminAccess, dataTablePermissionChecker]), ); return super.findSingleRecord(dataTableId, options); diff --git a/packages/central-server/src/apiV2/dataTables/assertDataTablePermissions.js b/packages/central-server/src/apiV2/dataTables/assertDataTablePermissions.js index f8fa3ad0f9..f63686c5f8 100644 --- a/packages/central-server/src/apiV2/dataTables/assertDataTablePermissions.js +++ b/packages/central-server/src/apiV2/dataTables/assertDataTablePermissions.js @@ -4,11 +4,7 @@ */ import { hasBESAdminAccess } from '../../permissions'; - -const getPermissionListWithWildcard = async accessPolicy => { - const userPermissionGroups = accessPolicy.getPermissionGroups(); - return ['*', ...userPermissionGroups]; -}; +import { getPermissionListWithWildcard } from '../utilities'; export const assertDataTableGETPermissions = async (accessPolicy, models, dataTableId) => { // User requires access to any permission group @@ -18,16 +14,6 @@ export const assertDataTableGETPermissions = async (accessPolicy, models, dataTa throw new Error('You do not have permission to view this data-table'); }; -export const assertDataTableEditPermissions = async (accessPolicy, models, dataTableId) => { - // User requires access to all permission groups - if (await assertDataTablePermissions(accessPolicy, models, dataTableId, 'every')) { - return true; - } - throw new Error( - 'You require access to all of a data-tables permission groups to perform this action', - ); -}; - const assertDataTablePermissions = async (accessPolicy, models, dataTableId, test) => { const dataTable = await models.dataTable.findById(dataTableId); if (!dataTable) { diff --git a/packages/central-server/src/apiV2/dataTables/index.js b/packages/central-server/src/apiV2/dataTables/index.js index 3e190abfbf..3fd03e0575 100644 --- a/packages/central-server/src/apiV2/dataTables/index.js +++ b/packages/central-server/src/apiV2/dataTables/index.js @@ -4,4 +4,3 @@ */ export { GETDataTables } from './GETDataTables'; -export { CreateDataTables } from './CreateDataTables'; diff --git a/packages/central-server/src/apiV2/import/importUsers.js b/packages/central-server/src/apiV2/import/importUsers.js index 8d0690806a..9001b77e94 100644 --- a/packages/central-server/src/apiV2/import/importUsers.js +++ b/packages/central-server/src/apiV2/import/importUsers.js @@ -12,8 +12,10 @@ import { ObjectValidator, hasContent, constructIsOneOf, + constructIsEmptyOr, } from '@tupaia/utils'; import { hashAndSaltPassword } from '@tupaia/auth'; +import { VerifiedEmail } from '@tupaia/types'; import { assertBESAdminAccess } from '../../permissions'; export async function importUsers(req, res) { @@ -90,10 +92,11 @@ const FIELD_VALIDATORS = { first_name: [hasContent], last_name: [], email: [hasContent], - gender: [hasContent, constructIsOneOf(['f', 'm'])], + gender: [constructIsEmptyOr(constructIsOneOf(['f', 'm', 'unknown']))], employer: [hasContent], position: [hasContent], mobile_number: [], password: [hasContent], permission_group: [hasContent], + verified_email: [constructIsEmptyOr(constructIsOneOf(Object.values(VerifiedEmail)))], }; diff --git a/packages/central-server/src/apiV2/index.js b/packages/central-server/src/apiV2/index.js index a7f7639363..cc00715a4a 100644 --- a/packages/central-server/src/apiV2/index.js +++ b/packages/central-server/src/apiV2/index.js @@ -30,7 +30,7 @@ import { GETClinics } from './GETClinics'; import { GETDisasters } from './GETDisasters'; import { GETDataElements, EditDataElements, DeleteDataElements } from './dataElements'; import { GETDataGroups, EditDataGroups, DeleteDataGroups } from './dataGroups'; -import { CreateDataTables, GETDataTables } from './dataTables'; +import { GETDataTables } from './dataTables'; import { GETFeedItems } from './GETFeedItems'; import { GETGeographicalAreas } from './GETGeographicalAreas'; import { GETSurveyGroups } from './GETSurveyGroups'; @@ -264,7 +264,7 @@ apiV2.post('/surveyResponses', catchAsyncErrors(surveyResponse)); apiV2.post('/countries', useRouteHandler(BESAdminCreateHandler)); apiV2.post('/dataElements', useRouteHandler(BESAdminCreateHandler)); apiV2.post('/dataGroups', useRouteHandler(BESAdminCreateHandler)); -apiV2.post('/dataTables', useRouteHandler(CreateDataTables)); +apiV2.post('/dataTables', useRouteHandler(BESAdminCreateHandler)); apiV2.post('/dashboards', useRouteHandler(CreateDashboard)); apiV2.post('/mapOverlayGroups', useRouteHandler(CreateMapOverlayGroups)); apiV2.post('/disasters', useRouteHandler(BESAdminCreateHandler)); @@ -330,6 +330,7 @@ apiV2.delete('/answers/:recordId', useRouteHandler(DeleteAnswers)); apiV2.delete('/surveyResponses/:parentRecordId/answers/:recordId', useRouteHandler(DeleteAnswers)); apiV2.delete('/dataElements/:recordId', useRouteHandler(DeleteDataElements)); apiV2.delete('/dataGroups/:recordId', useRouteHandler(DeleteDataGroups)); +apiV2.delete('/dataTables/:recordId', useRouteHandler(BESAdminDeleteHandler)); apiV2.delete('/disasters/:recordId', useRouteHandler(BESAdminDeleteHandler)); apiV2.delete('/entities/:recordId', useRouteHandler(DeleteEntity)); apiV2.delete('/feedItems/:recordId', useRouteHandler(BESAdminDeleteHandler)); diff --git a/packages/central-server/src/apiV2/mapOverlays/DeleteMapOverlays.js b/packages/central-server/src/apiV2/mapOverlays/DeleteMapOverlays.js index b6b599aaa5..0d86bb571f 100644 --- a/packages/central-server/src/apiV2/mapOverlays/DeleteMapOverlays.js +++ b/packages/central-server/src/apiV2/mapOverlays/DeleteMapOverlays.js @@ -10,4 +10,11 @@ export class DeleteMapOverlays extends DeleteHandler { async assertUserHasAccess() { await this.assertPermissions(assertBESAdminAccess); } + + async deleteRecord() { + const mapOverlay = await this.resourceModel.findById(this.recordId); + const reportModel = mapOverlay.legacy ? this.models.legacyReport : this.models.report; + await reportModel.delete({ code: mapOverlay.report_code }); + return super.deleteRecord(); + } } diff --git a/packages/central-server/src/apiV2/userEntityPermissions/assertUserEntityPermissionPermissions.js b/packages/central-server/src/apiV2/userEntityPermissions/assertUserEntityPermissionPermissions.js index 03141eb891..ea6716298f 100644 --- a/packages/central-server/src/apiV2/userEntityPermissions/assertUserEntityPermissionPermissions.js +++ b/packages/central-server/src/apiV2/userEntityPermissions/assertUserEntityPermissionPermissions.js @@ -8,7 +8,7 @@ import { BES_ADMIN_PERMISSION_GROUP, TUPAIA_ADMIN_PANEL_PERMISSION_GROUP, } from '../../permissions'; -import { getAdminPanelAllowedEntityIds, getAdminPanelAllowedCountryCodes } from '../utilities'; +import { getAdminPanelAllowedCountryIds, getAdminPanelAllowedCountryCodes } from '../utilities'; export const assertUserEntityPermissionPermissions = async ( accessPolicy, @@ -78,7 +78,7 @@ export const createUserEntityPermissionDBFilter = async (accessPolicy, models, c } // If we don't have BES Admin access, add a filter to the SQL query const dbConditions = { - 'user_entity_permission.entity_id': await getAdminPanelAllowedEntityIds(accessPolicy, models), + 'user_entity_permission.entity_id': await getAdminPanelAllowedCountryIds(accessPolicy, models), ...criteria, }; diff --git a/packages/central-server/src/apiV2/utilities/getAdminPanelAllowedEntityIds.js b/packages/central-server/src/apiV2/utilities/getAdminPanelAllowedCountries.js similarity index 84% rename from packages/central-server/src/apiV2/utilities/getAdminPanelAllowedEntityIds.js rename to packages/central-server/src/apiV2/utilities/getAdminPanelAllowedCountries.js index 49e1362870..91bd15de99 100644 --- a/packages/central-server/src/apiV2/utilities/getAdminPanelAllowedEntityIds.js +++ b/packages/central-server/src/apiV2/utilities/getAdminPanelAllowedCountries.js @@ -38,7 +38,7 @@ export const getAdminPanelAllowedCountryCodes = accessPolicy => { }; /* - * Get a list of entity ids this user has tupaia admin panel access to, or throw an error if they have none + * Get a list of country ids this user has tupaia admin panel access to, or throw an error if they have none * * @param {AccessPolicy} accessPolicy * @param {ModelRegistry} models @@ -46,10 +46,10 @@ export const getAdminPanelAllowedCountryCodes = accessPolicy => { * @returns string[] The entity ids */ -export const getAdminPanelAllowedEntityIds = async (accessPolicy, models) => { +export const getAdminPanelAllowedCountryIds = async (accessPolicy, models) => { const accessibleCountryCodes = getAdminPanelAllowedCountryCodes(accessPolicy); const entities = await models.entity.find({ - country_code: accessibleCountryCodes, + code: accessibleCountryCodes, }); return entities.map(e => e.id); diff --git a/packages/central-server/src/apiV2/utilities/index.js b/packages/central-server/src/apiV2/utilities/index.js index 651220c3fd..22cf2258e6 100644 --- a/packages/central-server/src/apiV2/utilities/index.js +++ b/packages/central-server/src/apiV2/utilities/index.js @@ -12,9 +12,9 @@ export { } from './fetchCountriesByPermissionGroup'; export { fetchRequestingMeditrakDevice } from './fetchRequestingMeditrakDevice'; export { - getAdminPanelAllowedEntityIds, + getAdminPanelAllowedCountryIds, getAdminPanelAllowedCountryCodes, -} from './getAdminPanelAllowedEntityIds'; +} from './getAdminPanelAllowedCountries'; export { getArrayQueryParameter } from './getArrayQueryParameter'; export { getColumnsForMeditrakApp } from './getColumnsForMeditrakApp'; export { hasAccessToEntityForVisualisation } from './hasAccessToEntityForVisualisation'; diff --git a/packages/central-server/src/tests/apiV2/dashboardVisualisations/dashboardVisualisations.fixtures.js b/packages/central-server/src/tests/apiV2/dashboardVisualisations/dashboardVisualisations.fixtures.js index 042656a690..02a15de9f8 100644 --- a/packages/central-server/src/tests/apiV2/dashboardVisualisations/dashboardVisualisations.fixtures.js +++ b/packages/central-server/src/tests/apiV2/dashboardVisualisations/dashboardVisualisations.fixtures.js @@ -14,6 +14,7 @@ const DASHBOARD_ITEMS = [ config: { name: 'Modern Dashboard Item', type: 'view', viewType: 'singleValue' }, report_code: 'Modern_Report', legacy: false, + permissionGroupIds: [], }, { id: generateTestId(), @@ -21,6 +22,7 @@ const DASHBOARD_ITEMS = [ config: { name: 'Legacy Dashboard Item', type: 'chart', chartType: 'bar' }, report_code: 'Legacy_Report', legacy: true, + permissionGroupIds: [], }, { id: generateTestId(), diff --git a/packages/database/package.json b/packages/database/package.json index 76f6d1071e..f3b53c5948 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -25,7 +25,7 @@ "refresh-database": "node ./scripts/refreshDatabase.js", "test": "yarn package:test:withdb --runInBand", "test:coverage": "yarn test --coverage", - "update-test-data": "bash -c 'source .env && pg_dump -s -U $DB_USER -O $DB_NAME > src/__tests__/testData/testDataDump.sql && pg_dump -t migrations -c -U $DB_USER -O $DB_NAME >> src/__tests__/testData/testDataDump.sql'", + "update-test-data": "bash -c 'source .env && pg_dump -s -h $DB_URL -U $DB_USER -O $DB_NAME > src/__tests__/testData/testDataDump.sql && pg_dump -t migrations -c -h $DB_URL -U $DB_USER -O $DB_NAME >> src/__tests__/testData/testDataDump.sql'", "setup-test-database": "DB_NAME=tupaia_test scripts/setupTestDatabase.sh", "check-test-database-exists": "DB_NAME=tupaia_test scripts/checkTestDatabaseExists.sh" }, diff --git a/packages/database/src/__tests__/testData/testDataDump.sql b/packages/database/src/__tests__/testData/testDataDump.sql index 451d3e66a8..8f1b25d2e8 100644 --- a/packages/database/src/__tests__/testData/testDataDump.sql +++ b/packages/database/src/__tests__/testData/testDataDump.sql @@ -2,7 +2,7 @@ -- PostgreSQL database dump -- --- Dumped from database version 13.5 (Ubuntu 13.5-1.pgdg18.04+1) +-- Dumped from database version 13.7 -- Dumped by pg_dump version 13.5 (Ubuntu 13.5-1.pgdg18.04+1) SET statement_timeout = 0; @@ -52,6 +52,22 @@ CREATE TYPE public.data_source_type AS ENUM ( ); +-- +-- Name: data_table_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.data_table_type AS ENUM ( + 'analytics', + 'events', + 'entity_relations', + 'entities', + 'sql', + 'data_group_metadata', + 'data_element_metadata', + 'entity_attributes' +); + + -- -- Name: disaster_event_type; Type: TYPE; Schema: public; Owner: - -- @@ -100,7 +116,21 @@ CREATE TYPE public.entity_type AS ENUM ( 'sub_facility', 'postcode', 'household', - 'larval_habitat' + 'larval_habitat', + 'local_government', + 'medical_area', + 'nursing_zone', + 'fetp_graduate', + 'incident', + 'incident_reported', + 'fiji_aspen_facility', + 'wish_sub_district', + 'trap', + 'asset', + 'institute', + 'msupply_store', + 'complaint', + 'water_sample' ); @@ -137,7 +167,19 @@ CREATE TYPE public.service_type AS ENUM ( 'indicator', 'weather', 'kobo', - 'data-lake' + 'data-lake', + 'superset' +); + + +-- +-- Name: sync_group_sync_status; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.sync_group_sync_status AS ENUM ( + 'IDLE', + 'SYNCING', + 'ERROR' ); @@ -169,104 +211,108 @@ CREATE TYPE public.verified_email AS ENUM ( CREATE FUNCTION public.build_analytics_table(force boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $_X$ - declare - tStartTime TIMESTAMP; - source_table TEXT; - source_tables_array TEXT[] := array['answer', 'survey_response', 'entity', 'survey', 'question', 'data_source']; - pSqlStatement TEXT := ' - SELECT - entity.code as entity_code, - entity.name as entity_name, - question.code as data_element_code, - survey.code as data_group_code, - survey_response.id as event_id, - CASE - WHEN question.type = ''Binary'' OR question.type = ''Checkbox'' - THEN CASE - WHEN answer.text = ''Yes'' THEN ''1'' - ELSE ''0'' - END - ELSE answer.text - END as value, - question.type as type, - to_char(survey_response.data_time, ''YYYYMMDD'') as "day_period", - concat( - extract (isoyear from survey_response.data_time), - ''W'', - to_char(extract (week from survey_response.data_time), ''FM09'')) - as "week_period", - to_char(survey_response.data_time, ''YYYYMM'') as "month_period", - to_char(survey_response.data_time, ''YYYY'') as "year_period", - survey_response.data_time as "date" - FROM - survey_response - INNER JOIN - answer ON answer.survey_response_id = survey_response.id - INNER JOIN - entity ON entity.id = survey_response.entity_id - INNER JOIN - survey ON survey.id = survey_response.survey_id - INNER JOIN - question ON question.id = answer.question_id - INNER JOIN - data_source ON data_source.id = question.data_source_id - WHERE data_source.service_type = ''tupaia'' AND survey_response.outdated IS FALSE AND survey_response.approval_status IN (''not_required'', ''approved'')'; - - begin - RAISE NOTICE 'Creating Materialized View Logs...'; - - FOREACH source_table IN ARRAY source_tables_array LOOP - IF (SELECT NOT EXISTS ( - SELECT FROM pg_tables - WHERE schemaname = 'public' - AND tablename = 'log$_' || source_table - )) - THEN - EXECUTE 'ALTER TABLE ' || source_table || ' DISABLE TRIGGER ' || source_table || '_trigger'; - tStartTime := clock_timestamp(); - PERFORM mv$createMaterializedViewlog(source_table, 'public'); - RAISE NOTICE 'Created Materialized View Log for % table, took %', source_table, clock_timestamp() - tStartTime; - EXECUTE 'ALTER TABLE ' || source_table || ' ENABLE TRIGGER ' || source_table || '_trigger'; - ELSE - RAISE NOTICE 'Materialized View Log for % table already exists, skipping', source_table; - END IF; - END LOOP; - - - RAISE NOTICE 'Creating analytics materialized view...'; - IF (SELECT NOT EXISTS ( - SELECT FROM pg_tables - WHERE schemaname = 'public' - AND tablename = 'analytics' - )) - THEN - tStartTime := clock_timestamp(); - PERFORM mv$createMaterializedView( - pViewName => 'analytics', - pSelectStatement => pSqlStatement, - pOwner => 'public', - pFastRefresh => TRUE - ); - RAISE NOTICE 'Created analytics table, took %', clock_timestamp() - tStartTime; - ELSE - IF (force) - THEN - RAISE NOTICE 'Force rebuilding analytics table'; - tStartTime := clock_timestamp(); - PERFORM mv$createMaterializedView( - pViewName => 'analytics_tmp', - pSelectStatement => pSqlStatement, - pOwner => 'public', - pFastRefresh => TRUE - ); - RAISE NOTICE 'Created analytics table, took %', clock_timestamp() - tStartTime; - PERFORM mv$removeMaterializedView('analytics', 'public'); - PERFORM mv$renameMaterializedView('analytics_tmp', 'analytics', 'public'); - ELSE - RAISE NOTICE 'Analytics Materialized View already exists, skipping'; - END IF; - END IF; - end $_X$; +declare + tStartTime TIMESTAMP; + source_table TEXT; + source_tables_array TEXT[] := array['answer', 'survey_response', 'entity', 'survey', 'question', 'data_element']; + pSqlStatement TEXT := ' + SELECT + entity.code as entity_code, + entity.name as entity_name, + question.code as data_element_code, + survey.code as data_group_code, + survey_response.id as event_id, + CASE + WHEN question.type = ''Binary'' OR question.type = ''Checkbox'' + THEN CASE + WHEN answer.text = ''Yes'' THEN ''1'' + ELSE ''0'' + END + WHEN question.type = ''Entity'' + THEN answer_entity.name + ELSE answer.text + END as value, + question.type as type, + to_char(survey_response.data_time, ''YYYYMMDD'') as "day_period", + concat( + extract (isoyear from survey_response.data_time), + ''W'', + to_char(extract (week from survey_response.data_time), ''FM09'')) + as "week_period", + to_char(survey_response.data_time, ''YYYYMM'') as "month_period", + to_char(survey_response.data_time, ''YYYY'') as "year_period", + survey_response.data_time as "date" + FROM + survey_response + INNER JOIN + answer ON answer.survey_response_id = survey_response.id + INNER JOIN + entity ON entity.id = survey_response.entity_id + LEFT JOIN + entity answer_entity ON answer_entity.id = answer.text + INNER JOIN + survey ON survey.id = survey_response.survey_id + INNER JOIN + question ON question.id = answer.question_id + INNER JOIN + data_element ON data_element.id = question.data_element_id + WHERE data_element.service_type = ''tupaia'' AND survey_response.outdated IS FALSE AND survey_response.approval_status IN (''not_required'', ''approved'')'; + +begin + RAISE NOTICE 'Creating Materialized View Logs...'; + + FOREACH source_table IN ARRAY source_tables_array LOOP + IF (SELECT NOT EXISTS ( + SELECT FROM pg_tables + WHERE schemaname = 'public' + AND tablename = 'log$_' || source_table + )) + THEN + EXECUTE 'ALTER TABLE ' || source_table || ' DISABLE TRIGGER ' || source_table || '_trigger'; + tStartTime := clock_timestamp(); + PERFORM mv$createMaterializedViewLog(source_table, 'public'); + RAISE NOTICE 'Created Materialized View Log for % table, took %', source_table, clock_timestamp() - tStartTime; + EXECUTE 'ALTER TABLE ' || source_table || ' ENABLE TRIGGER ' || source_table || '_trigger'; + ELSE + RAISE NOTICE 'Materialized View Log for % table already exists, skipping', source_table; + END IF; + END LOOP; + + + RAISE NOTICE 'Creating analytics materialized view...'; + IF (SELECT NOT EXISTS ( + SELECT FROM pg_tables + WHERE schemaname = 'public' + AND tablename = 'analytics' + )) + THEN + tStartTime := clock_timestamp(); + PERFORM mv$createMaterializedView( + pViewName => 'analytics', + pSelectStatement => pSqlStatement, + pOwner => 'public', + pFastRefresh => TRUE + ); + RAISE NOTICE 'Created analytics table, took %', clock_timestamp() - tStartTime; + ELSE + IF (force) + THEN + RAISE NOTICE 'Force rebuilding analytics table'; + tStartTime := clock_timestamp(); + PERFORM mv$createMaterializedView( + pViewName => 'analytics_tmp', + pSelectStatement => pSqlStatement, + pOwner => 'public', + pFastRefresh => TRUE + ); + RAISE NOTICE 'Created analytics table, took %', clock_timestamp() - tStartTime; + PERFORM mv$removeMaterializedView('analytics', 'public'); + PERFORM mv$renameMaterializedView('analytics_tmp', 'analytics', 'public'); + ELSE + RAISE NOTICE 'Analytics Materialized View already exists, skipping'; + END IF; + END IF; +end $_X$; -- @@ -391,39 +437,39 @@ $_X$; CREATE FUNCTION public.drop_analytics_log_tables() RETURNS void LANGUAGE plpgsql AS $_X$ - declare - tStartTime TIMESTAMP; - source_table TEXT; - source_tables_array TEXT[] := array['answer', 'survey_response', 'entity', 'survey', 'question', 'data_source']; - - begin - IF (SELECT EXISTS ( - SELECT FROM pg_tables - WHERE schemaname = 'public' - AND tablename = 'analytics' - )) - THEN - RAISE NOTICE 'Must drop analytics table before dropping log tables. Run drop_analytics_table()'; - ELSE - RAISE NOTICE 'Dropping Materialized View Logs...'; - FOREACH source_table IN ARRAY source_tables_array LOOP - IF (SELECT EXISTS ( - SELECT FROM pg_tables - WHERE schemaname = 'public' - AND tablename = 'log$_' || source_table - )) - THEN - EXECUTE 'ALTER TABLE ' || source_table || ' DISABLE TRIGGER ' || source_table || '_trigger'; - tStartTime := clock_timestamp(); - PERFORM mv$removeMaterializedViewLog(source_table, 'public'); - RAISE NOTICE 'Dropped Materialized View Log for % table, took %', source_table, clock_timestamp() - tStartTime; - EXECUTE 'ALTER TABLE ' || source_table || ' ENABLE TRIGGER ' || source_table || '_trigger'; - ELSE - RAISE NOTICE 'Materialized View Log for % table does not exist, skipping', source_table; - END IF; - END LOOP; - END IF; - end $_X$; + declare + tStartTime TIMESTAMP; + source_table TEXT; + source_tables_array TEXT[] := array['answer', 'survey_response', 'entity', 'survey', 'question', 'data_element']; + + begin + IF (SELECT EXISTS ( + SELECT FROM pg_tables + WHERE schemaname = 'public' + AND tablename = 'analytics' + )) + THEN + RAISE NOTICE 'Must drop analytics table before dropping log tables. Run drop_analytics_table()'; + ELSE + RAISE NOTICE 'Dropping Materialized View Logs...'; + FOREACH source_table IN ARRAY source_tables_array LOOP + IF (SELECT EXISTS ( + SELECT FROM pg_tables + WHERE schemaname = 'public' + AND tablename = 'log$_' || source_table + )) + THEN + EXECUTE 'ALTER TABLE ' || source_table || ' DISABLE TRIGGER ' || source_table || '_trigger'; + tStartTime := clock_timestamp(); + PERFORM mv$removeMaterializedViewLog(source_table, 'public'); + RAISE NOTICE 'Dropped Materialized View Log for % table, took %', source_table, clock_timestamp() - tStartTime; + EXECUTE 'ALTER TABLE ' || source_table || ' ENABLE TRIGGER ' || source_table || '_trigger'; + ELSE + RAISE NOTICE 'Materialized View Log for % table does not exist, skipping', source_table; + END IF; + END LOOP; + END IF; + end $_X$; -- @@ -689,6 +735,16 @@ CREATE FUNCTION public.update_change_time() RETURNS trigger $$; +-- +-- Name: array_concat_agg(anyarray); Type: AGGREGATE; Schema: public; Owner: - +-- + +CREATE AGGREGATE public.array_concat_agg(anyarray) ( + SFUNC = array_cat, + STYPE = anyarray +); + + -- -- Name: most_recent(text, timestamp without time zone); Type: AGGREGATE; Schema: public; Owner: - -- @@ -788,7 +844,9 @@ CREATE TABLE public.api_request_log ( request_time timestamp without time zone DEFAULT now(), query jsonb, metadata jsonb DEFAULT '{}'::jsonb, - refresh_token text + refresh_token text, + api text NOT NULL, + method text ); @@ -867,7 +925,8 @@ CREATE TABLE public.dashboard_item ( code text NOT NULL, config jsonb DEFAULT '{}'::jsonb NOT NULL, report_code text, - legacy boolean DEFAULT false NOT NULL + legacy boolean DEFAULT false NOT NULL, + permission_group_ids text[] ); @@ -889,6 +948,20 @@ CREATE TABLE public.dashboard_relation ( ); +-- +-- Name: data_element; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.data_element ( + id text NOT NULL, + code text NOT NULL, + service_type public.service_type NOT NULL, + config jsonb DEFAULT '{}'::jsonb NOT NULL, + permission_groups text[] DEFAULT '{}'::text[] NOT NULL, + CONSTRAINT valid_data_service_config CHECK (((service_type <> 'dhis'::public.service_type) OR ((config ->> 'dhisInstanceCode'::text) IS NOT NULL))) +); + + -- -- Name: data_element_data_group; Type: TABLE; Schema: public; Owner: - -- @@ -900,6 +973,33 @@ CREATE TABLE public.data_element_data_group ( ); +-- +-- Name: data_element_data_service; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.data_element_data_service ( + id text NOT NULL, + data_element_code text NOT NULL, + country_code text NOT NULL, + service_type public.service_type NOT NULL, + service_config jsonb DEFAULT '{}'::jsonb NOT NULL, + CONSTRAINT valid_data_service_config CHECK (((service_type <> 'dhis'::public.service_type) OR ((service_config ->> 'dhisInstanceCode'::text) IS NOT NULL))) +); + + +-- +-- Name: data_group; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.data_group ( + id text NOT NULL, + code text NOT NULL, + service_type public.service_type NOT NULL, + config jsonb DEFAULT '{}'::jsonb NOT NULL, + CONSTRAINT valid_data_service_config CHECK (((service_type <> 'dhis'::public.service_type) OR ((config ->> 'dhisInstanceCode'::text) IS NOT NULL))) +); + + -- -- Name: data_service_entity; Type: TABLE; Schema: public; Owner: - -- @@ -919,20 +1019,36 @@ CREATE TABLE public.data_service_sync_group ( id text NOT NULL, code text NOT NULL, service_type public.service_type NOT NULL, - config jsonb NOT NULL + config jsonb NOT NULL, + data_group_code text NOT NULL, + sync_cursor text DEFAULT '1970-01-01T00:00:00.000Z'::text, + sync_status public.sync_group_sync_status DEFAULT 'IDLE'::public.sync_group_sync_status ); -- --- Name: data_source; Type: TABLE; Schema: public; Owner: - +-- Name: data_table; Type: TABLE; Schema: public; Owner: - -- -CREATE TABLE public.data_source ( +CREATE TABLE public.data_table ( id text NOT NULL, code text NOT NULL, - type public.data_source_type NOT NULL, - service_type public.service_type NOT NULL, - config jsonb DEFAULT '{}'::jsonb NOT NULL + description text, + config jsonb DEFAULT '{}'::jsonb NOT NULL, + permission_groups text[] NOT NULL, + type public.data_table_type NOT NULL +); + + +-- +-- Name: dhis_instance; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.dhis_instance ( + id text NOT NULL, + code text NOT NULL, + readonly boolean NOT NULL, + config jsonb NOT NULL ); @@ -1054,6 +1170,19 @@ CREATE TABLE public.error_log ( ); +-- +-- Name: external_database_connection; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.external_database_connection ( + id text NOT NULL, + code text NOT NULL, + name text NOT NULL, + description text, + permission_groups text[] DEFAULT '{}'::text[] NOT NULL +); + + -- -- Name: feed_item; Type: TABLE; Schema: public; Owner: - -- @@ -1373,8 +1502,8 @@ CREATE TABLE public.question ( detail text, option_set_id character varying, hook text, - data_source_id text, - CONSTRAINT data_source_id_not_null_on_conditions CHECK (((type = ANY (ARRAY['DateOfData'::text, 'Instruction'::text, 'PrimaryEntity'::text, 'SubmissionDate'::text])) OR (data_source_id IS NOT NULL))) + data_element_id text, + CONSTRAINT data_source_id_not_null_on_conditions CHECK (((type = ANY (ARRAY['DateOfData'::text, 'Instruction'::text, 'PrimaryEntity'::text, 'SubmissionDate'::text])) OR (data_element_id IS NOT NULL))) ); @@ -1415,6 +1544,17 @@ CREATE TABLE public.setting ( ); +-- +-- Name: superset_instance; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.superset_instance ( + id text NOT NULL, + code text NOT NULL, + config jsonb NOT NULL +); + + -- -- Name: survey; Type: TABLE; Schema: public; Owner: - -- @@ -1422,15 +1562,15 @@ CREATE TABLE public.setting ( CREATE TABLE public.survey ( id text NOT NULL, name text NOT NULL, - code text NOT NULL, + code character varying(30) NOT NULL, permission_group_id text, country_ids text[] DEFAULT '{}'::text[], can_repeat boolean DEFAULT false, survey_group_id text, integration_metadata jsonb DEFAULT '{}'::jsonb, - data_source_id text NOT NULL, period_granularity public.period_granularity, - requires_approval boolean DEFAULT false + requires_approval boolean DEFAULT false, + data_group_id text ); @@ -1506,25 +1646,12 @@ CREATE TABLE public.survey_screen_component ( -- --- Name: sync_service; Type: TABLE; Schema: public; Owner: - +-- Name: sync_group_log; Type: TABLE; Schema: public; Owner: - -- -CREATE TABLE public.sync_service ( +CREATE TABLE public.sync_group_log ( id text NOT NULL, - code text NOT NULL, - service_type public.service_type NOT NULL, - sync_cursor text NOT NULL, - config jsonb NOT NULL -); - - --- --- Name: sync_service_log; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.sync_service_log ( - id text NOT NULL, - service_code text NOT NULL, + sync_group_code text NOT NULL, service_type public.service_type NOT NULL, log_message text NOT NULL, "timestamp" timestamp without time zone DEFAULT timezone('UTC'::text, now()) @@ -1579,6 +1706,17 @@ CREATE TABLE public.user_entity_permission ( ); +-- +-- Name: user_favourite_dashboard_item; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.user_favourite_dashboard_item ( + id text NOT NULL, + user_id text NOT NULL, + dashboard_item_id text NOT NULL +); + + -- -- Name: migrations id; Type: DEFAULT; Schema: public; Owner: - -- @@ -1586,6 +1724,68 @@ CREATE TABLE public.user_entity_permission ( ALTER TABLE ONLY public.migrations ALTER COLUMN id SET DEFAULT nextval('public.migrations_id_seq'::regclass); +-- +-- Name: meditrak_sync_queue meditrak_sync_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.meditrak_sync_queue + ADD CONSTRAINT meditrak_sync_queue_pkey PRIMARY KEY (id); + + +-- +-- Name: permissions_based_meditrak_sync_queue; Type: MATERIALIZED VIEW; Schema: public; Owner: - +-- + +CREATE MATERIALIZED VIEW public.permissions_based_meditrak_sync_queue AS + SELECT msq.id, + msq.type, + msq.record_type, + msq.record_id, + msq.change_time, + max(e.type) AS entity_type, + COALESCE(NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(co.id)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(e_co.id)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(c.country_id)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(ga.country_id)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(public.array_concat_agg(s.country_ids)) AS unnest), '{}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(public.array_concat_agg(sg_s.country_ids)) AS unnest), '{}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(public.array_concat_agg(ss_s.country_ids)) AS unnest), '{}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(public.array_concat_agg(ssc_ss_s.country_ids)) AS unnest), '{}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(public.array_concat_agg(q_ssc_ss_s.country_ids)) AS unnest), '{}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(public.array_concat_agg(os_q_ssc_ss_s.country_ids)) AS unnest), '{}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(public.array_concat_agg(o_os_q_ssc_ss_s.country_ids)) AS unnest), '{}'::text[])) AS country_ids, + COALESCE(NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(pg.name)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(s_pg.name)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(sg_s_pg.name)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(ss_s_pg.name)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(ssc_ss_s_pg.name)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(q_ssc_ss_s_pg.name)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(os_q_ssc_ss_s_pg.name)) AS unnest), '{NULL}'::text[]), NULLIF(ARRAY( SELECT DISTINCT unnest(array_agg(o_os_q_ssc_ss_s_pg.name)) AS unnest), '{NULL}'::text[])) AS permission_groups + FROM ((((((((((((((((((((((((((((((((((((public.meditrak_sync_queue msq + LEFT JOIN public.country co ON ((msq.record_id = co.id))) + LEFT JOIN public.entity e ON ((msq.record_id = (e.id)::text))) + LEFT JOIN public.country e_co ON ((e_co.code = (e.country_code)::text))) + LEFT JOIN public.clinic c ON ((msq.record_id = c.id))) + LEFT JOIN public.geographical_area ga ON ((msq.record_id = ga.id))) + LEFT JOIN public.permission_group pg ON ((msq.record_id = pg.id))) + LEFT JOIN public.survey s ON ((msq.record_id = s.id))) + LEFT JOIN public.permission_group s_pg ON ((s.permission_group_id = s_pg.id))) + LEFT JOIN public.survey_group sg ON ((msq.record_id = sg.id))) + LEFT JOIN public.survey sg_s ON ((sg_s.survey_group_id = sg.id))) + LEFT JOIN public.permission_group sg_s_pg ON ((sg_s.permission_group_id = sg_s_pg.id))) + LEFT JOIN public.survey_screen ss ON ((msq.record_id = ss.id))) + LEFT JOIN public.survey ss_s ON ((ss.survey_id = ss_s.id))) + LEFT JOIN public.permission_group ss_s_pg ON ((ss_s.permission_group_id = ss_s_pg.id))) + LEFT JOIN public.survey_screen_component ssc ON ((msq.record_id = ssc.id))) + LEFT JOIN public.survey_screen ssc_ss ON ((ssc.screen_id = ssc_ss.id))) + LEFT JOIN public.survey ssc_ss_s ON ((ssc_ss.survey_id = ssc_ss_s.id))) + LEFT JOIN public.permission_group ssc_ss_s_pg ON ((ssc_ss_s.permission_group_id = ssc_ss_s_pg.id))) + LEFT JOIN public.question q ON ((msq.record_id = q.id))) + LEFT JOIN public.survey_screen_component q_ssc ON ((q_ssc.question_id = q.id))) + LEFT JOIN public.survey_screen q_ssc_ss ON ((q_ssc.screen_id = q_ssc_ss.id))) + LEFT JOIN public.survey q_ssc_ss_s ON ((q_ssc_ss.survey_id = q_ssc_ss_s.id))) + LEFT JOIN public.permission_group q_ssc_ss_s_pg ON ((q_ssc_ss_s.permission_group_id = q_ssc_ss_s_pg.id))) + LEFT JOIN public.option_set os ON ((msq.record_id = os.id))) + LEFT JOIN public.question os_q ON (((os_q.option_set_id)::text = os.id))) + LEFT JOIN public.survey_screen_component os_q_ssc ON ((os_q_ssc.question_id = os_q.id))) + LEFT JOIN public.survey_screen os_q_ssc_ss ON ((os_q_ssc.screen_id = os_q_ssc_ss.id))) + LEFT JOIN public.survey os_q_ssc_ss_s ON ((os_q_ssc_ss.survey_id = os_q_ssc_ss_s.id))) + LEFT JOIN public.permission_group os_q_ssc_ss_s_pg ON ((os_q_ssc_ss_s.permission_group_id = os_q_ssc_ss_s_pg.id))) + LEFT JOIN public.option o ON ((msq.record_id = o.id))) + LEFT JOIN public.option_set o_os ON ((o.option_set_id = o_os.id))) + LEFT JOIN public.question o_os_q ON (((o_os_q.option_set_id)::text = o_os.id))) + LEFT JOIN public.survey_screen_component o_os_q_ssc ON ((o_os_q_ssc.question_id = o_os_q.id))) + LEFT JOIN public.survey_screen o_os_q_ssc_ss ON ((o_os_q_ssc.screen_id = o_os_q_ssc_ss.id))) + LEFT JOIN public.survey o_os_q_ssc_ss_s ON ((o_os_q_ssc_ss.survey_id = o_os_q_ssc_ss_s.id))) + LEFT JOIN public.permission_group o_os_q_ssc_ss_s_pg ON ((o_os_q_ssc_ss_s.permission_group_id = o_os_q_ssc_ss_s_pg.id))) + GROUP BY msq.id + WITH NO DATA; + + -- -- Name: access_request access_request_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -1754,6 +1954,22 @@ ALTER TABLE ONLY public.data_element_data_group ADD CONSTRAINT data_element_data_group_unique UNIQUE (data_element_id, data_group_id); +-- +-- Name: data_element_data_service data_element_data_service_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.data_element_data_service + ADD CONSTRAINT data_element_data_service_pkey PRIMARY KEY (id); + + +-- +-- Name: data_group data_group_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.data_group + ADD CONSTRAINT data_group_pkey PRIMARY KEY (id); + + -- -- Name: data_service_entity data_service_entity_entity_code_key; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -1787,19 +2003,43 @@ ALTER TABLE ONLY public.data_service_sync_group -- --- Name: data_source data_source_code_type_key; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: data_element data_source_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- -ALTER TABLE ONLY public.data_source - ADD CONSTRAINT data_source_code_type_key UNIQUE (code, type); +ALTER TABLE ONLY public.data_element + ADD CONSTRAINT data_source_pkey PRIMARY KEY (id); -- --- Name: data_source data_source_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: data_table data_table_code_key; Type: CONSTRAINT; Schema: public; Owner: - -- -ALTER TABLE ONLY public.data_source - ADD CONSTRAINT data_source_pkey PRIMARY KEY (id); +ALTER TABLE ONLY public.data_table + ADD CONSTRAINT data_table_code_key UNIQUE (code); + + +-- +-- Name: data_table data_table_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.data_table + ADD CONSTRAINT data_table_pkey PRIMARY KEY (id); + + +-- +-- Name: dhis_instance dhis_instance_code_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dhis_instance + ADD CONSTRAINT dhis_instance_code_key UNIQUE (code); + + +-- +-- Name: dhis_instance dhis_instance_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dhis_instance + ADD CONSTRAINT dhis_instance_pkey PRIMARY KEY (id); -- @@ -1906,6 +2146,22 @@ ALTER TABLE ONLY public.error_log ADD CONSTRAINT error_log_pkey PRIMARY KEY (id); +-- +-- Name: external_database_connection external_database_connection_code_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.external_database_connection + ADD CONSTRAINT external_database_connection_code_key UNIQUE (code); + + +-- +-- Name: external_database_connection external_database_connection_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.external_database_connection + ADD CONSTRAINT external_database_connection_pkey PRIMARY KEY (id); + + -- -- Name: feed_item feed_item_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2026,14 +2282,6 @@ ALTER TABLE ONLY public.meditrak_sync_queue ADD CONSTRAINT meditrak_sync_queue_change_time_key UNIQUE (change_time); --- --- Name: meditrak_sync_queue meditrak_sync_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.meditrak_sync_queue - ADD CONSTRAINT meditrak_sync_queue_pkey PRIMARY KEY (id); - - -- -- Name: meditrak_sync_queue meditrak_sync_queue_record_id_unique; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2218,6 +2466,22 @@ ALTER TABLE ONLY public.setting ADD CONSTRAINT setting_pkey PRIMARY KEY (id); +-- +-- Name: superset_instance superset_instance_code_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.superset_instance + ADD CONSTRAINT superset_instance_code_key UNIQUE (code); + + +-- +-- Name: superset_instance superset_instance_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.superset_instance + ADD CONSTRAINT superset_instance_pkey PRIMARY KEY (id); + + -- -- Name: survey survey_code_unique; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2291,29 +2555,13 @@ ALTER TABLE ONLY public.survey_screen -- --- Name: sync_service sync_service_code_key; Type: CONSTRAINT; Schema: public; Owner: - +-- Name: sync_group_log sync_service_log_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- -ALTER TABLE ONLY public.sync_service - ADD CONSTRAINT sync_service_code_key UNIQUE (code); - - --- --- Name: sync_service_log sync_service_log_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.sync_service_log +ALTER TABLE ONLY public.sync_group_log ADD CONSTRAINT sync_service_log_pkey PRIMARY KEY (id); --- --- Name: sync_service sync_service_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.sync_service - ADD CONSTRAINT sync_service_pkey PRIMARY KEY (id); - - -- -- Name: userSession userSession_id_key; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2354,6 +2602,22 @@ ALTER TABLE ONLY public.user_entity_permission ADD CONSTRAINT user_entity_permission_pkey PRIMARY KEY (id); +-- +-- Name: user_favourite_dashboard_item user_favourite_dashboard_item_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_favourite_dashboard_item + ADD CONSTRAINT user_favourite_dashboard_item_pkey PRIMARY KEY (id); + + +-- +-- Name: user_favourite_dashboard_item user_favourite_dashboard_item_user_id_dashboard_item_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_favourite_dashboard_item + ADD CONSTRAINT user_favourite_dashboard_item_user_id_dashboard_item_id_key UNIQUE (user_id, dashboard_item_id); + + -- -- Name: ancestor_descendant_relation_ancestor_id_idx; Type: INDEX; Schema: public; Owner: - -- @@ -2501,6 +2765,13 @@ CREATE INDEX permission_group_name_idx ON public.permission_group USING btree (n CREATE INDEX permission_group_parent_id_idx ON public.permission_group USING btree (parent_id); +-- +-- Name: permissions_based_meditrak_sync_queue_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX permissions_based_meditrak_sync_queue_id_idx ON public.permissions_based_meditrak_sync_queue USING btree (id); + + -- -- Name: question_code_idx; Type: INDEX; Schema: public; Owner: - -- @@ -2564,6 +2835,13 @@ CREATE INDEX survey_name_idx ON public.survey USING btree (name); CREATE INDEX survey_permission_group_id_idx ON public.survey USING btree (permission_group_id); +-- +-- Name: survey_response_data_time_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX survey_response_data_time_idx ON public.survey_response USING btree (data_time DESC); + + -- -- Name: survey_response_end_time_idx; Type: INDEX; Schema: public; Owner: - -- @@ -2571,6 +2849,20 @@ CREATE INDEX survey_permission_group_id_idx ON public.survey USING btree (permis CREATE INDEX survey_response_end_time_idx ON public.survey_response USING btree (end_time); +-- +-- Name: survey_response_entity_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX survey_response_entity_id_idx ON public.survey_response USING btree (entity_id); + + +-- +-- Name: survey_response_outdated_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX survey_response_outdated_id_idx ON public.survey_response USING btree (outdated); + + -- -- Name: survey_response_start_time_idx; Type: INDEX; Schema: public; Owner: - -- @@ -2683,6 +2975,13 @@ CREATE INDEX user_entity_permission_permission_group_id_idx ON public.user_entit CREATE INDEX user_entity_permission_user_id_idx ON public.user_entity_permission USING btree (user_id); +-- +-- Name: user_favourite_dashboard_item_user_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX user_favourite_dashboard_item_user_id_idx ON public.user_favourite_dashboard_item USING btree (user_id); + + -- -- Name: access_request access_request_trigger; Type: TRIGGER; Schema: public; Owner: - -- @@ -2760,6 +3059,27 @@ CREATE TRIGGER dashboard_trigger AFTER INSERT OR DELETE OR UPDATE ON public.dash CREATE TRIGGER data_element_data_group_trigger AFTER INSERT OR DELETE OR UPDATE ON public.data_element_data_group FOR EACH ROW EXECUTE FUNCTION public.notification(); +-- +-- Name: data_element_data_service data_element_data_service_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER data_element_data_service_trigger AFTER INSERT OR DELETE OR UPDATE ON public.data_element_data_service FOR EACH ROW EXECUTE FUNCTION public.notification(); + + +-- +-- Name: data_element data_element_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER data_element_trigger AFTER INSERT OR DELETE OR UPDATE ON public.data_element FOR EACH ROW EXECUTE FUNCTION public.notification(); + + +-- +-- Name: data_group data_group_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER data_group_trigger AFTER INSERT OR DELETE OR UPDATE ON public.data_group FOR EACH ROW EXECUTE FUNCTION public.notification(); + + -- -- Name: data_service_entity data_service_entity_trigger; Type: TRIGGER; Schema: public; Owner: - -- @@ -2768,17 +3088,17 @@ CREATE TRIGGER data_service_entity_trigger AFTER INSERT OR DELETE OR UPDATE ON p -- --- Name: data_service_sync_group data_service_sync_group_trigger; Type: TRIGGER; Schema: public; Owner: - +-- Name: data_table data_table_trigger; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER data_service_sync_group_trigger AFTER INSERT OR DELETE OR UPDATE ON public.data_service_sync_group FOR EACH ROW EXECUTE FUNCTION public.notification(); +CREATE TRIGGER data_table_trigger AFTER INSERT OR DELETE OR UPDATE ON public.data_table FOR EACH ROW EXECUTE FUNCTION public.notification(); -- --- Name: data_source data_source_trigger; Type: TRIGGER; Schema: public; Owner: - +-- Name: dhis_instance dhis_instance_trigger; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER data_source_trigger AFTER INSERT OR DELETE OR UPDATE ON public.data_source FOR EACH ROW EXECUTE FUNCTION public.notification(); +CREATE TRIGGER dhis_instance_trigger AFTER INSERT OR DELETE OR UPDATE ON public.dhis_instance FOR EACH ROW EXECUTE FUNCTION public.notification(); -- @@ -2823,6 +3143,13 @@ CREATE TRIGGER entity_relation_trigger AFTER INSERT OR DELETE OR UPDATE ON publi CREATE TRIGGER entity_trigger AFTER INSERT OR DELETE OR UPDATE ON public.entity FOR EACH ROW EXECUTE FUNCTION public.notification(); +-- +-- Name: external_database_connection external_database_connection_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER external_database_connection_trigger AFTER INSERT OR DELETE OR UPDATE ON public.external_database_connection FOR EACH ROW EXECUTE FUNCTION public.notification(); + + -- -- Name: geographical_area geographical_area_trigger; Type: TRIGGER; Schema: public; Owner: - -- @@ -2950,17 +3277,17 @@ CREATE TRIGGER refresh_token_trigger AFTER INSERT OR DELETE OR UPDATE ON public. -- --- Name: report report_trigger; Type: TRIGGER; Schema: public; Owner: - +-- Name: setting setting_trigger; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER report_trigger AFTER INSERT OR DELETE OR UPDATE ON public.report FOR EACH ROW EXECUTE FUNCTION public.notification(); +CREATE TRIGGER setting_trigger AFTER INSERT OR DELETE OR UPDATE ON public.setting FOR EACH ROW EXECUTE FUNCTION public.notification(); -- --- Name: setting setting_trigger; Type: TRIGGER; Schema: public; Owner: - +-- Name: superset_instance superset_instance_trigger; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER setting_trigger AFTER INSERT OR DELETE OR UPDATE ON public.setting FOR EACH ROW EXECUTE FUNCTION public.notification(); +CREATE TRIGGER superset_instance_trigger AFTER INSERT OR DELETE OR UPDATE ON public.superset_instance FOR EACH ROW EXECUTE FUNCTION public.notification(); -- @@ -3006,17 +3333,17 @@ CREATE TRIGGER survey_trigger AFTER INSERT OR DELETE OR UPDATE ON public.survey -- --- Name: sync_service_log sync_service_log_trigger; Type: TRIGGER; Schema: public; Owner: - +-- Name: sync_group_log sync_group_log_trigger; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER sync_service_log_trigger AFTER INSERT OR DELETE OR UPDATE ON public.sync_service_log FOR EACH ROW EXECUTE FUNCTION public.notification(); +CREATE TRIGGER sync_group_log_trigger AFTER INSERT OR DELETE OR UPDATE ON public.sync_group_log FOR EACH ROW EXECUTE FUNCTION public.notification(); -- --- Name: sync_service sync_service_trigger; Type: TRIGGER; Schema: public; Owner: - +-- Name: sync_group_log sync_service_log_trigger; Type: TRIGGER; Schema: public; Owner: - -- -CREATE TRIGGER sync_service_trigger AFTER INSERT OR DELETE OR UPDATE ON public.sync_service FOR EACH ROW EXECUTE FUNCTION public.notification(); +CREATE TRIGGER sync_service_log_trigger AFTER INSERT OR DELETE OR UPDATE ON public.sync_group_log FOR EACH ROW EXECUTE FUNCTION public.notification(); -- @@ -3033,6 +3360,13 @@ CREATE TRIGGER user_account_trigger AFTER INSERT OR DELETE OR UPDATE ON public.u CREATE TRIGGER user_entity_permission_trigger AFTER INSERT OR DELETE OR UPDATE ON public.user_entity_permission FOR EACH ROW EXECUTE FUNCTION public.notification(); +-- +-- Name: user_favourite_dashboard_item user_favourite_dashboard_item_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER user_favourite_dashboard_item_trigger AFTER INSERT OR DELETE OR UPDATE ON public.user_favourite_dashboard_item FOR EACH ROW EXECUTE FUNCTION public.notification(); + + -- -- Name: access_request access_request_entity_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3182,15 +3516,15 @@ ALTER TABLE ONLY public.dashboard -- ALTER TABLE ONLY public.data_element_data_group - ADD CONSTRAINT data_element_data_group_data_element_id_fk FOREIGN KEY (data_element_id) REFERENCES public.data_source(id) ON UPDATE CASCADE ON DELETE CASCADE; + ADD CONSTRAINT data_element_data_group_data_element_id_fk FOREIGN KEY (data_element_id) REFERENCES public.data_element(id) ON UPDATE CASCADE ON DELETE CASCADE; -- --- Name: data_element_data_group data_element_data_group_data_group_id_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: data_element_data_group data_element_data_group_data_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.data_element_data_group - ADD CONSTRAINT data_element_data_group_data_group_id_fk FOREIGN KEY (data_group_id) REFERENCES public.data_source(id) ON UPDATE CASCADE ON DELETE CASCADE; + ADD CONSTRAINT data_element_data_group_data_group_id_fkey FOREIGN KEY (data_group_id) REFERENCES public.data_group(id) ON UPDATE CASCADE ON DELETE CASCADE; -- @@ -3342,7 +3676,7 @@ ALTER TABLE ONLY public.project -- ALTER TABLE ONLY public.question - ADD CONSTRAINT question_data_source_id_fkey FOREIGN KEY (data_source_id) REFERENCES public.data_source(id); + ADD CONSTRAINT question_data_source_id_fkey FOREIGN KEY (data_element_id) REFERENCES public.data_element(id); -- @@ -3378,11 +3712,11 @@ ALTER TABLE ONLY public.report -- --- Name: survey survey_data_source_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- Name: survey survey_data_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- ALTER TABLE ONLY public.survey - ADD CONSTRAINT survey_data_source_id_fkey FOREIGN KEY (data_source_id) REFERENCES public.data_source(id); + ADD CONSTRAINT survey_data_group_id_fkey FOREIGN KEY (data_group_id) REFERENCES public.data_group(id) ON UPDATE CASCADE ON DELETE SET NULL; -- @@ -3489,13 +3823,29 @@ ALTER TABLE ONLY public.user_entity_permission ADD CONSTRAINT user_entity_permission_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.user_account(id) ON UPDATE CASCADE ON DELETE CASCADE; +-- +-- Name: user_favourite_dashboard_item user_favourite_dashboard_item_dashboard_item_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_favourite_dashboard_item + ADD CONSTRAINT user_favourite_dashboard_item_dashboard_item_id_fkey FOREIGN KEY (dashboard_item_id) REFERENCES public.dashboard_item(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: user_favourite_dashboard_item user_favourite_dashboard_item_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_favourite_dashboard_item + ADD CONSTRAINT user_favourite_dashboard_item_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.user_account(id) ON UPDATE CASCADE ON DELETE CASCADE; + + -- -- Name: SCHEMA public; Type: ACL; Schema: -; Owner: - -- REVOKE ALL ON SCHEMA public FROM PUBLIC; +GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO tupaia; -GRANT ALL ON SCHEMA public TO mvrefresh; -- @@ -3514,7 +3864,7 @@ CREATE EVENT TRIGGER schema_change_trigger ON ddl_command_end -- PostgreSQL database dump -- --- Dumped from database version 13.5 (Ubuntu 13.5-1.pgdg18.04+1) +-- Dumped from database version 13.7 -- Dumped by pg_dump version 13.5 (Ubuntu 13.5-1.pgdg18.04+1) SET statement_timeout = 0; @@ -4994,7 +5344,17 @@ COPY public.migrations (id, name, run_on) FROM stdin; 1417 /20211123223023-DeleteAccidentalTestCountry-modifies-data 2021-11-29 21:50:09.257 1418 /20211124053026-AddMapOverlayGroupForNcd-modifies-data 2021-11-29 21:50:09.325 1475 /20220119005332-UpdateTongaCovidPermissions-modifies-data 2022-01-24 22:49:16.616 +1476 /20211121221500-AddApprovalColumns-modifies-schema 2022-02-01 22:42:00.939 +1477 /20211125004704-UpdateAnalyticsTableForApprovalStatus-modifies-schema 2022-02-01 23:21:28.055 +1478 /20220122223251-PenFaaSamoaAddVillageSchoolEntityRelations 2022-02-07 23:31:49.31 +1479 /20220201052718-DeleteDuplicateSamoaSchoolEntities-modifies-data 2022-02-07 23:31:50.897 +1480 /20220203064851-AddDataLakeServiceType-modifies-schema 2022-02-14 21:17:19.809 +1481 /20220208004345-RemoveApostropheFromAustralianEntityCode-modifies-data 2022-02-14 21:17:20.992 1426 /20211125021151-ChangePermissionForPenFaa-modifies-data 2021-12-02 07:56:58.451 +1482 /20220210214956-UpdateCovid19SamoaVisualisationPermissions-modifies-data 2022-02-21 21:35:16.153 +1483 /20220210235957-UnfpaMarshallIslandsAddFacilities-modifies-data 2022-02-21 21:35:16.361 +1484 /20220218050602-AddCovidKiribatiProject-modifies-data 2022-02-21 21:35:16.438 +1485 /20220221232146-AddDataElementFromDataLakeToTupaia-modifies-data 2022-02-22 02:40:11.274 1446 /20211126004049-AddPrimaryPlatformToUsersTable-modifies-schema 2021-12-06 21:03:19.715 1447 /20211130040028-UpdateFacilityNamesSolomonIslands-modifies-data 2021-12-06 21:03:20.95 1448 /20211201030947-SamoaDeleteDuplicateSchool-modifies-data 2021-12-06 21:03:22.821 @@ -5008,10 +5368,93 @@ COPY public.migrations (id, name, run_on) FROM stdin; 1472 /20220107050226-AddFlutrackingPostcodeMapOverlayGroup-modifies-data 2022-01-20 01:09:45.154 1473 /20220107061813-SetMapOverlayGroupCodeAsUniqueKey-modifies-schema 2022-01-20 01:09:45.35 1474 /20220117011411-CovidSamoaDeleteOutdatedResponses-modifies-data 2022-01-20 04:20:56.496 -1476 /20211121221500-AddApprovalColumns-modifies-schema 2022-02-03 17:24:21.927 -1477 /20211125004704-UpdateAnalyticsTableForApprovalStatus-modifies-schema 2022-02-03 17:57:18.406 -1479 /20220203064851-AddDataLakeServiceType-modifies-schema 2022-02-04 11:44:29.918 -1480 /20220122223251-PenFaaSamoaAddVillageSchoolEntityRelations 2022-03-31 12:32:41.54 +1486 /20220222043511-UnfpaMarshallIslandsAddMissingFacility-modifies-data 2022-02-24 02:37:26.283 +1487 /20220223024636-AddVillageToCovidKiribatiProjectHeirarchy-modifies-data 2022-02-24 02:37:26.36 +1488 /20220223052221-DeleteInvalidKiribatiEntities-modifies-data 2022-02-25 00:06:04.303 +1489 /20220218015022-AddDataLakeDataElementsIntoTupaiaDataSource-modifies-data 2022-02-28 23:26:28.042 +1490 /20220303051055-AddPalauToPacmossiProject-modifies-data 2022-03-08 05:36:46.918 +1491 /20220309231120-AddCovidDataLakeDataElementsIntoTupaiaDataSource-modifies-data 2022-03-11 02:38:02.596 +1492 /20220211015354-AddConstraintSurveyCodeLength-modifies-schema 2022-04-05 00:53:00.916 +1493 /20220405032953-UpdateAnalyticsTableToHandleEntityTypeAnswer-modifies-schema 2022-04-20 01:39:22.307 +1494 /20220513032539-ReinstateMissingEntityAnswersC19TestRegistration-modifies-data 2022-05-15 23:25:48.911 +1495 /20220407063942-AddPngEntitiesAndUpdateRelationsForStrive-modifies-data 2022-05-17 06:29:13.096 +1496 /20220515045038-AddEntityTypeLocalGov-modifies-schema 2022-05-17 06:29:13.196 +1497 /20220516000532-AddProjectImpactHealth-modifies-data 2022-05-17 06:29:15.319 +1498 /20220518002531-AddFacilityEntitiesForImpactHealth-modifies-data 2022-05-18 08:16:07.198 +1499 /20220520063938-ExcludeBetioFromCovidKiribatiProject-modifies-data 2022-05-31 00:26:48.994 +1500 /20220523020255-AddEntityTypesMedicalAreaAndNursingZone-modifies-schema 2022-05-31 00:26:49.485 +1501 /20220523032848-AddProjectMassDrugAdministration-modifies-data 2022-05-31 00:26:50.943 +1502 /20220524011100-AddProjectCovidSolomonIslands-modifies-data 2022-05-31 00:26:51.768 +1503 /20220531030730-RerunMigrationToReinstateMissingEntityAnswersC19TestRegistration-modifies-data 2022-05-31 06:35:49.385 +1504 /20220520141500-SplitDataSourceTable-modifies-schema 2022-06-21 09:25:20.818 +1505 /20220520141600-AddPermissionGroupsToDataSources-modifies-schema 2022-06-21 09:25:32.591 +1506 /20220520141700-UpdateDataElementPermissionsFromVisuals-modifies-data 2022-06-21 09:50:16.716 +1507 /20220623233124-AddHouseholdsToEntityHierarchyCanonicalTypes-modifies-data 2022-06-24 04:20:10.871 +1508 /20220614225441-AddLoggingForAllServicesApiRequestLog-modifies-schema 2022-06-28 02:12:13.906 +1509 /20220615053401-AddSamoaToPacMossiProject-modifies-data 2022-06-28 02:12:14.954 +1510 /20220615070630-UpdateEntityRelationsForSamatauVillageSamoa-modifies-data 2022-06-28 02:12:15.098 +1511 /20220616025825-DeleteCovidMinisterViewDashboard-modifies-data 2022-06-28 02:12:15.401 +1512 /20220706235939-Maui4CleanupFijiFacilities-modifies-data 2022-07-12 02:20:22.644 +1513 /20220710234424-UpgradeFrontendExcludedTypesConfig-modifies-data 2022-07-13 04:52:02.235 +1514 /20220714065133-AddEntityImageHookToHouseholdQuestion-modifies-data 2022-07-15 05:04:53.185 +1515 /20220707245124-SetNiueParentToWorld-modifies-data 2022-07-26 00:50:03.693 +1516 /20220512232924-CreateDhisInstanceTable-modifies-schema 2022-08-09 02:38:49.574 +1517 /20220513014400-AddDhisInstances-modifies-data 2022-08-09 02:39:56.425 +1518 /20220718043838-UnfpaAddMHFacilityRelations-modifies-data 2022-08-09 02:40:01.644 +1519 /20220802000352-ChangeCovidSamoaProjectName-modifies-data 2022-08-09 02:40:03.279 +1520 /20220804020223-DeletePalauEnviroHealthEntities-modifies-data 2022-08-09 02:42:10.357 +1521 /20220622064208-AddArrayConcatAggregationFunction-modifies-schema 2022-08-24 02:09:49.684 +1522 /20220810013153-UpdateAllNonRegionalDhisInstanceConfigDataElementsAndGroups-modifies-data 2022-08-24 02:10:55.565 +1523 /20220630013151-LinkSyncServiceToSurvey-modifies-schema 2022-09-06 04:53:40.71 +1524 /20220811014552-CreateSupersetInstanceTable-modifies-schema 2022-09-06 04:53:43.076 +1525 /20220812055419-MoveSyncServiceToDataServiceSyncGroup-modifies-schema 2022-09-06 04:53:47.024 +1526 /20220819002505-AddEntityAttributeHookHouseholdHeadToQuestion-modifies-data 2022-09-06 04:53:51.026 +1527 /20220819055530-DeletePgFacilities-modifies-data 2022-09-06 04:56:17.807 +1528 /20220822035354-AddSyncStatusToSyncGroup-modifies-schema 2022-09-06 04:56:17.831 +1529 /20220901060328-AddProjectTuvaluEhealth-modifies-data 2022-09-06 04:56:21.444 +1530 /20220822025509-AddDataElementDataServiceTable-modifies-schema 2022-09-20 03:39:40.664 +1531 /20220823234713-AddUserFavouriteDashboardTable-modifies-schema 2022-09-20 03:39:41.726 +1532 /20220829032330-AddConstraintDataServiceConfig-modifies-schema 2022-09-20 03:39:44.744 +1533 /20220905010529-AddEntityTypeFETPGraduate-modifies-schema 2022-09-20 03:39:59.512 +1534 /20220905020318-UpdateIndividualEntityTypeToFetpGraduateForFETPProject-modifies-data 2022-09-20 03:40:29.937 +1535 /20220908010028-AddDataTableModel-modifies-schema 2022-09-20 03:40:31.698 +1536 /20220908043740-AddConstraintDataElementDataServiceTable-modifies-schema 2022-09-20 03:40:33.615 +1537 /20220908093833-DeletePermissionGroupDEehVectorSurveillance-modifies-data 2022-09-20 03:40:34.715 +1538 /20220909021244-AddAnalyticsDataTable-modifies-data 2022-09-20 03:40:35.997 +1539 /20220915060402-AddEventsDataTable-modifies-schema 2022-09-20 03:40:36.441 +1540 /20220920051046-RestoreEntityNameInAnalytics-modifies-schema 2022-10-04 01:34:35.375 +1541 /20220920062403-AddEntityIdIndexOnSurveyResponseTable-modifies-schema 2022-10-04 01:35:41.726 +1542 /20220916024924-AddUnfpaFacilities-modifies-data 2022-10-17 23:48:26.256 +1543 /20220925225155-AddSupersetMappings-modifies-data 2022-10-17 23:48:26.371 +1544 /20221001204123-AddExternalDatabaseConnectionTable-modifies-schema 2022-11-08 01:12:59.712 +1545 /20221006004520-AddEntityRelationsDataTable-modifies-data 2022-11-08 01:13:00.247 +1546 /20221006010514-AddEntitiesDataTable-modifies-data 2022-11-08 01:13:01.077 +1547 /20221102231839-AddNigeriaToTupaia-modifies-data 2022-11-08 01:13:01.854 +1548 /20221104002716-UnfpaPopulationTileSet-modifies-data 2022-11-22 01:31:32.806 +1549 /20221110021119-AddFacilitiesPenfaa-modifies-data 2022-11-22 01:31:33.685 +1550 /20221111050217-AddSupersetInstances-modifies-data 2022-11-22 01:31:33.722 +1551 /20221102021424-ChangeWishSubDistrictToUseNewType-modifies-data 2023-01-10 05:11:31.165 +1552 /20230109035514-PalauEditAndCreatePermissions-modifies-data 2023-01-24 00:40:29.817 +1553 /20230110023215-SolIslandsDeleteCovidProject-modifies-data 2023-01-24 00:40:30.105 +1554 /20230118223340-ChangeSupersetCountryCodeToFj-modifies-data 2023-01-24 00:40:30.163 +1555 /20230116004255-AddPermissionGroupColumnToDashboardItems-modifies-data 2023-02-14 00:20:56.161 +1556 /20230119053753-ConvertInternalDataTablesToTheirOwnTypes-modifies-schema 2023-02-14 00:20:58.597 +1557 /20221002042508-AddSqlDataTableType-modifies-schema 2023-02-16 13:58:55.864336 +1558 /20230214033100-ChangeConstraintOnTypeAndPermissionGroupsInDataTable-modifies-schema 2023-04-13 14:08:46.011 +1559 /20230216140701-MoveFetchIntoTransformInReports-modifies-data 2023-04-13 14:09:18.031 +1560 /20230216141720-ConvertReportsToPullFromDataTables-modifies-schema 2023-04-13 14:09:49.522 +1561 /20230217031100-AddNewDataTableTypes-modifies-schema 2023-04-13 14:10:00.428 +1562 /20230219232553-AddMetadataInDataTable-modifies-data 2023-04-13 14:10:00.645 +1563 /20230222233428-RewriteVizsUsedDataElementCodeToName-modifies-schema 2023-04-13 14:10:02.728 +1564 /20230314005038-RewriteWishFijiBaselineVizesWithOrgUnitContext-modifies-data 2023-04-13 14:10:03.256 +1565 /20230314042110-MigrateInsertNumberOfFacilitiesColumn-modifies-data 2023-04-13 14:10:04.073 +1566 /20230316041331-RewriteWishFijiMatrixReports-modifies-data 2023-04-13 14:10:04.457 +1567 /20230316061147-RewriteWishFijiMatrixReportsWithMultiDataElements-modifies-data 2023-04-13 14:10:04.717 +1568 /20230320011713-ConvertOrgUnitCodeToNameMatrixReports-modifies-data 2023-04-13 14:10:06.267 +1569 /20230321045049-AddEntityAttributesDataTableType-modifies-schema 2023-04-13 14:10:06.56 +1570 /20230328002338-FixDateOffsetVizes-modifies-data 2023-04-13 14:10:07.118 +1571 /20230402232608-AddEntityAttributesDataTable-modifies-data 2023-04-13 14:10:07.319 +1572 /20230413041856-FixupDropAnalyticsLogTablesFunction-modifies-schema 2023-04-13 14:20:27.99 \. @@ -5019,7 +5462,7 @@ COPY public.migrations (id, name, run_on) FROM stdin; -- Name: migrations_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - -- -SELECT pg_catalog.setval('public.migrations_id_seq', 1480, true); +SELECT pg_catalog.setval('public.migrations_id_seq', 1572, true); -- @@ -5030,6 +5473,8 @@ ALTER TABLE ONLY public.migrations ADD CONSTRAINT migrations_pkey PRIMARY KEY (id); + + -- -- PostgreSQL database dump complete -- diff --git a/packages/database/src/migrations/20230413041856-FixupDropAnalyticsLogTablesFunction-modifies-schema.js b/packages/database/src/migrations/20230413041856-FixupDropAnalyticsLogTablesFunction-modifies-schema.js new file mode 100644 index 0000000000..c8f04917cb --- /dev/null +++ b/packages/database/src/migrations/20230413041856-FixupDropAnalyticsLogTablesFunction-modifies-schema.js @@ -0,0 +1,30 @@ +'use strict'; + +import { createDropAnalyticsLogTablesFunction } from './migrationData/buildAnalyticsTableFunction/createDropAnalyticsLogTablesFunction.v1'; + +var dbm; +var type; +var seed; + +/** + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { + dbm = options.dbmigrate; + type = dbm.dataType; + seed = seedLink; +}; + +exports.up = async function (db) { + await db.runSql(createDropAnalyticsLogTablesFunction); + return null; +}; + +exports.down = function (db) { + return null; +}; + +exports._meta = { + version: 1, +}; diff --git a/packages/database/src/migrations/migrationData/buildAnalyticsTableFunction/createDropAnalyticsLogTablesFunction.v1.js b/packages/database/src/migrations/migrationData/buildAnalyticsTableFunction/createDropAnalyticsLogTablesFunction.v1.js new file mode 100644 index 0000000000..0d44f06946 --- /dev/null +++ b/packages/database/src/migrations/migrationData/buildAnalyticsTableFunction/createDropAnalyticsLogTablesFunction.v1.js @@ -0,0 +1,48 @@ +/** + * Tupaia + * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd + */ + +/** + * SQL for dropping the analytics log tables. If changes need to be made how to the analytics table is built + * then please create a NEW file with a higher version. + * + * This is to ensure that we keep a track of changes to the analytics table in a single place, while also + * allowing for historic migrations to be able to execute correctly + */ +export const createDropAnalyticsLogTablesFunction = ` +CREATE OR REPLACE FUNCTION drop_analytics_log_tables() RETURNS void AS $$ +declare + tStartTime TIMESTAMP; + source_table TEXT; + source_tables_array TEXT[] := array['answer', 'survey_response', 'entity', 'survey', 'question', 'data_element']; + +begin + IF (SELECT EXISTS ( + SELECT FROM pg_tables + WHERE schemaname = 'public' + AND tablename = 'analytics' + )) + THEN + RAISE NOTICE 'Must drop analytics table before dropping log tables. Run drop_analytics_table()'; + ELSE + RAISE NOTICE 'Dropping Materialized View Logs...'; + FOREACH source_table IN ARRAY source_tables_array LOOP + IF (SELECT EXISTS ( + SELECT FROM pg_tables + WHERE schemaname = 'public' + AND tablename = 'log$_' || source_table + )) + THEN + EXECUTE 'ALTER TABLE ' || source_table || ' DISABLE TRIGGER ' || source_table || '_trigger'; + tStartTime := clock_timestamp(); + PERFORM mv$removeMaterializedViewLog(source_table, 'public'); + RAISE NOTICE 'Dropped Materialized View Log for % table, took %', source_table, clock_timestamp() - tStartTime; + EXECUTE 'ALTER TABLE ' || source_table || ' ENABLE TRIGGER ' || source_table || '_trigger'; + ELSE + RAISE NOTICE 'Materialized View Log for % table does not exist, skipping', source_table; + END IF; + END LOOP; + END IF; +end $$ LANGUAGE plpgsql; +`; diff --git a/packages/dhis-api/examples.http b/packages/dhis-api/examples.http new file mode 100644 index 0000000000..56d1412e13 --- /dev/null +++ b/packages/dhis-api/examples.http @@ -0,0 +1,34 @@ +@host = https://dev-tonga-aggregation.tupaia.org +@contentType = application/json + +# In order to setup authorization, please set a base64encoded value of the dev Tupaia DHIS login in your restClient environment variables, as dhisClientEncoded. +# Also set the username and password for the dev Tupaia DHIS login in your restClient environment variables, as dhisUsername and dhisPassword. +# see: https://marketplace.visualstudio.com/items?itemName=humao.rest-client#environment-variables +@authorization = Basic {{dhisClientEncoded}} + +# This is an example time period for the below requests +@timePeriod=pe:201801;201802;201803;201804;201805;201806;201807;201808;201809;201810;201811;201812 + +### Login +# @name login +POST {{host}}/uaa/oauth/token HTTP/1.1 +content-type: application/x-www-form-urlencoded +authorization: {{authorization}} + +username={{dhisClient}} +&password={{dhisPassword}} +&grant_type=password + + +### Fetch DHIS Population Data for time period. This will use the access_token from the login request as authorization, so you will need to run login first. +# @name fetchDhisPopData + +GET {{host}}/api/analytics/rawData.json?paging=false&inputIdScheme=code&outputIdScheme=code&includeMetadataDetails=true&dimension=dx:POP02_POP37;POP_2017&dimension=ou:TO&dimension=co&dimension={{timePeriod}} +authorization: Bearer {{login.response.body.access_token}} + +### Fetch DHIS Family Planning Data for time period. This will use the access_token from the login request as authorization, so you will need to run login first. +# @name fetchDhisFamPlanData + +GET {{host}}/api/analytics/rawData.json?paging=false&inputIdScheme=code&outputIdScheme=code&includeMetadataDetails=true&dimension=dx:Family_Planning_Acceptors_Depo_Provera;Family_Planning_Acceptors_IUD;Family_Planning_Acceptors_Jadelle;Family_Planning_Acceptors_Natural_Method;Family_Planning_Acceptors_Other;Family_Planning_Acceptors_Pill_Combined;Family_Planning_Acceptors_Pill_Single;Family_Planning_Acceptors_Sterilization_Female;Family_Planning_Acceptors_Sterilization_Male;Family_Planning_Acceptors_Condom_Female;Family_Planning_Acceptors_Condom_Male&dimension=ou:TO&dimension=co&dimension={{timePeriod}} +authorization: Bearer {{login.response.body.access_token}} + diff --git a/packages/lesmis/package.json b/packages/lesmis/package.json index 4ee415a322..f1c6f9ad10 100755 --- a/packages/lesmis/package.json +++ b/packages/lesmis/package.json @@ -16,13 +16,16 @@ "lint": "yarn package:lint:js", "lint:fix": "yarn lint --fix", "start-dev": "PORT=3003 yarn package:start:react-scripts", - "start-fullstack": "npm-run-all -c -l -p start-central-server start-entity-server start-report-server start-web-config-server start-lesmis-server start-dev", + "start-fullstack": "npm-run-all -c -l -p start-servers start-frontend", "start-central-server": "yarn workspace @tupaia/central-server start-dev", "start-entity-server": "yarn workspace @tupaia/entity-server start-dev", "start-report-server": "yarn workspace @tupaia/report-server start-dev", "start-web-config-server": "yarn workspace @tupaia/web-config-server start-dev", "start-lesmis-server": "yarn workspace @tupaia/lesmis-server start-dev", - "test": "yarn package:test" + "test": "yarn package:test", + "start-ui-components": "yarn workspace @tupaia/ui-components build-dev:watch", + "start-frontend": "npm-run-all -c -l -p start-ui-components start-dev", + "start-servers": "npm-run-all -c -l -p start-central-server start-entity-server start-report-server start-web-config-server start-lesmis-server" }, "browserslist": [ "defaults" diff --git a/packages/psss/package.json b/packages/psss/package.json index 9613866efe..a65e31d70b 100644 --- a/packages/psss/package.json +++ b/packages/psss/package.json @@ -19,7 +19,7 @@ "lint": "yarn package:lint:js", "lint:fix": "yarn lint --fix", "start-dev": "yarn package:start:react-scripts", - "start-fullstack": "npm-run-all -c -l -p start-psss-server start-entity-server start-central-server start-report-server start-dev", + "start-fullstack": "npm-run-all -c -l -p start-servers start-frontend", "start-psss-server": "yarn workspace @tupaia/psss-server start-dev -s", "start-entity-server": "yarn workspace @tupaia/entity-server start-dev -s", "start-central-server": "yarn workspace @tupaia/central-server start-dev -s", @@ -27,7 +27,10 @@ "test": "yarn test:jest", "test:cypress:open": "start-server-and-test start-fullstack 3000 cypress:open", "test:cypress:run": "start-server-and-test start-fullstack 3000 cypress:run", - "test:jest": "yarn package:test --env=jsdom" + "test:jest": "yarn package:test --env=jsdom", + "start-ui-components": "yarn workspace @tupaia/ui-components build-dev:watch", + "start-frontend": "npm-run-all -c -l -p start-ui-components start-dev", + "start-servers": "npm-run-all -c -l -p start-psss-server start-entity-server start-central-server start-report-server" }, "browserslist": [ "defaults" diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 19d3c93c60..13f1923074 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -19,7 +19,8 @@ "storybook": "start-storybook -s public -p 6006", "test": "yarn package:test --env=jsdom", "test:coverage": "yarn test --coverage", - "test:watch": "yarn test --watch" + "test:watch": "yarn test --watch", + "build-dev:watch": "yarn run package:build:js -w" }, "dependencies": { "@date-io/date-fns": "1.x", diff --git a/packages/ui-components/src/components/Chart/BarChart.js b/packages/ui-components/src/components/Chart/BarChart.js index 6cb4a11300..30ccc8a876 100644 --- a/packages/ui-components/src/components/Chart/BarChart.js +++ b/packages/ui-components/src/components/Chart/BarChart.js @@ -8,6 +8,7 @@ import PropTypes from 'prop-types'; import { Bar, LabelList } from 'recharts'; import { formatDataValueByType } from '@tupaia/utils'; import { BLUE, CHART_TYPES } from './constants'; +import { getIsTimeSeries } from './utils'; export const BarChart = ({ color = BLUE, @@ -39,6 +40,13 @@ export const BarChart = ({ return 1; } } + /* + [There is a known bug with Recharts](https://github.com/recharts/recharts/issues/2711) where if a bar graph is a time scale, it overflows the bars over the edge of the axes. + As a workaround, until it is fixed, setting the bar size to be a specific value stops this from happening. + */ + if (getIsTimeSeries(data) && !isEnlarged) { + return Math.max(1, Math.floor(200 / data.length)); + } return undefined; }; diff --git a/packages/ui-components/src/components/Chart/Table/getChartTableData.js b/packages/ui-components/src/components/Chart/Table/getChartTableData.js index 765925ee7d..64389099ce 100644 --- a/packages/ui-components/src/components/Chart/Table/getChartTableData.js +++ b/packages/ui-components/src/components/Chart/Table/getChartTableData.js @@ -87,6 +87,11 @@ const processColumns = (viewContent, sortByTimestamp) => { return firstColumn ? [firstColumn, ...configColumns] : configColumns; }; +const sortDates = (dateA, dateB) => { + const dateAMoreRecent = dateA > dateB; + return dateAMoreRecent ? 1 : -1; +}; + const processData = viewContent => { if (!viewContent?.data) { return []; @@ -97,16 +102,20 @@ const processData = viewContent => { if (chartType === CHART_TYPES.PIE) { return data.sort((a, b) => b.value - a.value); } + // For time series, sort by timestamp so that the table is in chronological order always + if (getIsTimeSeries(data)) { + return data.sort((a, b) => sortDates(a.timestamp, b.timestamp)); + } + return data; }; export const getChartTableData = viewContent => { // Because react-table wants its sort function to be memoized, it needs to live here, outside of // the other useMemo hooks - const sortByTimestamp = useMemo(() => (rowA, rowB) => { - const rowAMoreRecent = rowA.original.timestamp > rowB.original.timestamp; - return rowAMoreRecent ? 1 : -1; - }); + const sortByTimestamp = useMemo(() => (rowA, rowB) => + sortDates(rowA.original.timestamp, rowB.original.timestamp), + ); const columns = useMemo(() => processColumns(viewContent, sortByTimestamp), [ JSON.stringify(viewContent), ]); diff --git a/packages/ui-components/src/components/Chart/XAxis.js b/packages/ui-components/src/components/Chart/XAxis.js index 9b7abc1098..9e990b08ff 100644 --- a/packages/ui-components/src/components/Chart/XAxis.js +++ b/packages/ui-components/src/components/Chart/XAxis.js @@ -26,8 +26,8 @@ const X_AXIS_PADDING = { }, preview: { dataLengthThreshold: 9, - base: 4, - offset: 10, + base: 8, + offset: 16, minimum: 10, }, }; diff --git a/packages/ui-components/src/components/DataTable/useDataTableExport.js b/packages/ui-components/src/components/DataTable/useDataTableExport.js index 4383fd66f2..71a6101c47 100644 --- a/packages/ui-components/src/components/DataTable/useDataTableExport.js +++ b/packages/ui-components/src/components/DataTable/useDataTableExport.js @@ -25,7 +25,15 @@ export const useDataTableExport = (columns, data, title, startDate, endDate) => ); const body = tableData.length > 0 - ? tableData.map(row => tableColumns.map(col => row.values[col.id])) + ? tableData.map(row => + tableColumns.map(col => { + const value = row.values[col.id]; + const valueIsPercentageString = value.includes('%'); + const num = valueIsPercentageString ? value : parseFloat(value); + // if it's a number, return in number format + return isNaN(num) ? value : num; + }), + ) : [['There is no available data for the selected time period.']]; // Make xlsx worksheet diff --git a/packages/web-frontend/package.json b/packages/web-frontend/package.json index d7825e9eef..1cc31a5e08 100644 --- a/packages/web-frontend/package.json +++ b/packages/web-frontend/package.json @@ -18,14 +18,17 @@ "start": "PORT=8088 yarn package:start:react-scripts", "start-dev": "yarn start", "start-mobile": "REACT_APP_APP_TYPE=mobile PORT=8089 PORT=8088 package:start:react-scripts", - "start-fullstack": "npm-run-all -c -l -p start-central-server start-entity-server start-report-server start-web-config-server start-dev", + "start-fullstack": "npm-run-all -c -l -p start-servers start-frontend", "start-central-server": "yarn workspace @tupaia/central-server start-dev", "start-entity-server": "yarn workspace @tupaia/entity-server start-dev", "start-report-server": "yarn workspace @tupaia/report-server start-dev", "start-web-config-server": "yarn workspace @tupaia/web-config-server start-dev", + "start-servers": "npm-run-all -c -l -p start-central-server start-entity-server start-report-server start-web-config-server", "start-data-table-server": "yarn workspace @tupaia/data-table-server start-dev", "storybook": "start-storybook -s public -p 6007", - "test": "node scripts/test.js --env=jsdom" + "test": "node scripts/test.js --env=jsdom", + "start-ui-components": "yarn workspace @tupaia/ui-components build-dev:watch", + "start-frontend": "npm-run-all -c -l -p start-ui-components start-dev" }, "resolutions": { "jss": "10.0.0" diff --git a/packages/web-frontend/src/containers/EnlargedDialog/EnlargedDialogContent.js b/packages/web-frontend/src/containers/EnlargedDialog/EnlargedDialogContent.js index 502dffb329..7c36a8f509 100644 --- a/packages/web-frontend/src/containers/EnlargedDialog/EnlargedDialogContent.js +++ b/packages/web-frontend/src/containers/EnlargedDialog/EnlargedDialogContent.js @@ -128,9 +128,10 @@ export class EnlargedDialogContent extends PureComponent { } renderBody() { - const { viewContent, errorMessage } = this.props; + const { viewContent, errorMessage, isLoading } = this.props; const noData = viewContent.data && viewContent.data.length === 0; + if (isLoading) return ; if (errorMessage) { return {errorMessage}; } diff --git a/packages/web-frontend/src/containers/Form/common/LoadingIndicator.js b/packages/web-frontend/src/containers/Form/common/LoadingIndicator.js index 5072893052..918a86a1c4 100644 --- a/packages/web-frontend/src/containers/Form/common/LoadingIndicator.js +++ b/packages/web-frontend/src/containers/Form/common/LoadingIndicator.js @@ -8,6 +8,7 @@ import React from 'react'; import styled from 'styled-components'; import CircularProgress from '@material-ui/core/CircularProgress'; +import PropTypes from 'prop-types'; const LoadingOverlay = styled.div` position: absolute; @@ -15,15 +16,22 @@ const LoadingOverlay = styled.div` bottom: 0; left: 0; right: 0; - background: rgba(255, 255, 255, 0.7); + background: ${props => (props.showBackground ? 'rgba(255, 255, 255, 0.5)' : 'transparent')}; z-index: 2; display: flex; align-items: center; justify-content: center; `; -export const LoadingIndicator = () => ( - +export const LoadingIndicator = ({ showBackground }) => ( + ); + +LoadingIndicator.propTypes = { + showBackground: PropTypes.bool, +}; +LoadingIndicator.defaultProps = { + showBackground: true, +}; diff --git a/packages/web-frontend/src/sagas/watchUser/watchAttemptTokenLogin.js b/packages/web-frontend/src/sagas/watchUser/watchAttemptTokenLogin.js index 5e32276a97..034ad85691 100644 --- a/packages/web-frontend/src/sagas/watchUser/watchAttemptTokenLogin.js +++ b/packages/web-frontend/src/sagas/watchUser/watchAttemptTokenLogin.js @@ -7,6 +7,7 @@ import { } from '../../actions'; import { LOGIN_TYPES } from '../../constants'; import { request } from '../../utils'; +import { fetchProjectData } from '../../projects/sagas'; function* attemptTokenLogin(action) { const { passwordResetToken } = action; @@ -39,6 +40,7 @@ function* attemptTokenLogin(action) { yield put(findLoggedIn(LOGIN_TYPES.TOKEN, true)); // default to email verified for one time login to prevent a nag screen yield put(fetchResetTokenLoginSuccess()); + yield call(fetchProjectData); } catch (error) { yield put(error.errorFunction(error)); } diff --git a/packages/web-frontend/src/sagas/watchUser/watchSetVerifyEmailToken.js b/packages/web-frontend/src/sagas/watchUser/watchSetVerifyEmailToken.js index b06353515d..a3e35d7c09 100644 --- a/packages/web-frontend/src/sagas/watchUser/watchSetVerifyEmailToken.js +++ b/packages/web-frontend/src/sagas/watchUser/watchSetVerifyEmailToken.js @@ -4,6 +4,7 @@ import { openEmailVerifiedPage, setOverlayComponent, SET_VERIFY_EMAIL_TOKEN, + goHome, } from '../../actions'; import { request } from '../../utils'; @@ -13,10 +14,10 @@ function* attemptVerifyToken(action) { try { yield call(request, requestResourceUrl); yield put(openEmailVerifiedPage()); - yield put(setOverlayComponent('landing')); } catch (error) { yield put(fetchEmailVerifyError()); - yield put(setOverlayComponent('landing')); + } finally { + yield put(goHome()); } }