diff --git a/.circleci/config.yml b/.circleci/config.yml
index af1f09b8a3..a57248f6da 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,9 +1,11 @@
version: 2.1
orbs:
- aws-cli: circleci/aws-cli@3.1
+ aws-cli: circleci/aws-cli@4.1.1
+ aws-ecs: circleci/aws-ecs@4.0.0
opsgenie: opsgenie/opsgenie@1.0.8
+
jobs:
frontend-code-test:
resource_class: large
@@ -68,7 +70,7 @@ jobs:
TM_ORG_CODE: "CICode"
TM_ORG_NAME: "CircleCI Test Organisation"
- - image: cimg/postgres:14.2-postgis
+ - image: cimg/postgres:14.9-postgis
environment:
POSTGRES_USER: taskingmanager
POSTGRES_DB: test_tm
@@ -79,12 +81,6 @@ jobs:
- run: sudo apt-get update
- run: sudo apt-get -y install libgeos-dev # Required for shapely
- run: sudo apt-get -y install proj-bin libproj-dev
- - run:
- name: Configure Postgresql Test database
- command: |
- psql \
- -d $SQLALCHEMY_DATABASE_URI \
- -c "CREATE EXTENSION postgis;"
- run: pip install --upgrade pip pdm
- run: pdm config --global python.use_venv False
- run: pdm export --dev --without-hashes > requirements.txt
@@ -113,13 +109,13 @@ jobs:
description: "Cloudformation stack name"
type: string
docker:
- - image: cimg/postgres:15.1-postgis
+ - image: cimg/postgres:15.4-postgis
steps:
- aws-cli/setup:
- role-arn: "arn:aws:iam::$ORG_AWS_ACCOUNT_ID:role/CircleCI-OIDC-Connect"
- profile-name: "OIDC-Profile"
- role-session-name: "database-snapshot"
- session-duration: "2700"
+ role_arn: "arn:aws:iam::$ORG_AWS_ACCOUNT_ID:role/CircleCI-OIDC-Connect"
+ profile_name: "OIDC-Profile"
+ role_session_name: "database-snapshot"
+ session_duration: "2700"
- run:
name: Find the instance ID of the database in the stack to backup
command: |
@@ -193,10 +189,10 @@ jobs:
steps:
- checkout
- aws-cli/setup:
- role-arn: "arn:aws:iam::$ORG_AWS_ACCOUNT_ID:role/CircleCI-OIDC-Connect"
- profile-name: "OIDC-Profile"
- role-session-name: "backend-deploy"
- session-duration: "2700"
+ role_arn: "arn:aws:iam::$ORG_AWS_ACCOUNT_ID:role/CircleCI-OIDC-Connect"
+ profile_name: "OIDC-Profile"
+ role_session_name: "backend-deploy"
+ session_duration: "2700"
- run: sudo apt-get update
- run: sudo apt-get -y install libgeos-dev jq
- run: sudo yarn global add @mapbox/cfn-config @mapbox/cloudfriend
@@ -222,6 +218,21 @@ jobs:
export JSON_CONFIG="$(< $CIRCLE_WORKING_DIRECTORY/cfn-config-<< parameters.stack_name >>.json)"
cfn-config update << parameters.stack_name >> $CIRCLE_WORKING_DIRECTORY/scripts/aws/cloudformation/tasking-manager.template.js -f -c hot-cfn-config -t hot-cfn-config -r $AWS_REGION -p "$JSON_CONFIG"
+ backend_deploy_containers:
+ working_directory: /home/circleci/tasking-manager
+ docker:
+ - image: cimg/python:3.10.7
+ steps:
+ - checkout
+ - aws-cli/setup:
+ role_arn: "arn:aws:iam::$ORG_AWS_ACCOUNT_ID:role/CircleCI-OIDC-Connect"
+ profile_name: "OIDC-Profile"
+ role_session_name: "backend-deploy-containers"
+ session_duration: "2700"
+ - run: sudo apt-get update
+ - run: sudo apt-get -y install curl
+ - run: echo "Run AWS Fargate"
+
frontend_deploy:
working_directory: /home/circleci/tasking-manager
resource_class: large
@@ -234,10 +245,10 @@ jobs:
steps:
- checkout
- aws-cli/setup:
- role-arn: "arn:aws:iam::$ORG_AWS_ACCOUNT_ID:role/CircleCI-OIDC-Connect"
- profile-name: "OIDC-Profile"
- role-session-name: "frontend-deploy"
- session-duration: "1800"
+ role_arn: "arn:aws:iam::$ORG_AWS_ACCOUNT_ID:role/CircleCI-OIDC-Connect"
+ profile_name: "OIDC-Profile"
+ role_session_name: "frontend-deploy"
+ session_duration: "1800"
- run:
name: Deploy Frontend to S3
command: |
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 731d49ea7b..9a90dea7b1 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,12 +1,12 @@
-"Component: Frontend":
+"codebase: frontend":
- frontend/**/*
-"Component: Backend":
+"codebase: backend":
- backend/**/*
- tests/**/*
- migrations/**/*
- ./manage.py
- ./pyproject.toml
-"Component: Infrastructure":
+"infrastructure":
- .circleci/*
- .github/**/*
- scripts/aws/**/*
@@ -20,5 +20,5 @@
- frontend/yarn.lock
"python":
- ./pyproject.toml
-"Type: Translations":
+"type: translations":
- frontend/src/locales/*
diff --git a/README.md b/README.md
index f5317df6ad..90750c3c2d 100644
--- a/README.md
+++ b/README.md
@@ -18,9 +18,7 @@ This is Free and Open Source Software. You are welcome to use the code and set u
* Read the monthly update blogs on [OSM Discourse](https://community.openstreetmap.org/c/general/38/all).
## Product Roadmap
-We have included below a high level roadmap/plan [subject to change] that can be used as an overview.
-![image](https://user-images.githubusercontent.com/98902727/218763601-f08e3879-51f3-40a7-ae6e-bdf96f8a5979.png)
-
+We have included below a [high level roadmap/plan](https://github.com/orgs/hotosm/projects/28/) [subject to change] that can be used as an overview.
## Developers
diff --git a/backend/api/teams/resources.py b/backend/api/teams/resources.py
index 3f5c13b848..06df030d27 100644
--- a/backend/api/teams/resources.py
+++ b/backend/api/teams/resources.py
@@ -181,8 +181,7 @@ def delete(self, team_id):
"SubCode": "UserNotTeamManager",
}, 401
- TeamService.delete_team(team_id)
- return {"Success": "Team deleted"}, 200
+ return TeamService.delete_team(team_id)
class TeamsAllAPI(Resource):
diff --git a/backend/config.py b/backend/config.py
index a76256e6d4..555205014f 100644
--- a/backend/config.py
+++ b/backend/config.py
@@ -205,7 +205,7 @@ class EnvironmentConfig:
"""
import json
- _params = json.loads(os.getenv("OAUTH2_APP_CREDENTIALS"), None)
+ _params = json.loads(os.getenv("OAUTH2_APP_CREDENTIALS", None))
OAUTH_CLIENT_ID = _params.get("CLIENT_ID", None)
OAUTH_CLIENT_SECRET = _params.get("CLIENT_SECRET", None)
OAUTH_REDIRECT_URI = _params.get("REDIRECT_URI", None)
diff --git a/backend/services/team_service.py b/backend/services/team_service.py
index 292360e3b8..6eeaf7b5b3 100644
--- a/backend/services/team_service.py
+++ b/backend/services/team_service.py
@@ -566,8 +566,12 @@ def delete_team(team_id: int):
if team.can_be_deleted():
team.delete()
+ return {"Success": "Team deleted"}, 200
else:
- raise TeamServiceError("Team has projects, cannot be deleted")
+ return {
+ "Error": "Team has projects, cannot be deleted",
+ "SubCode": "This team has projects associated. Before deleting team, unlink any associated projects.",
+ }, 400
@staticmethod
def check_team_membership(project_id: int, allowed_roles: list, user_id: int):
diff --git a/example.env b/example.env
index b02511f8f1..51c2e37818 100644
--- a/example.env
+++ b/example.env
@@ -84,10 +84,10 @@ OSM_REGISTER_URL=https://www.openstreetmap.org/user/new
# It's not required to set this tag. Case it isn't set, an image will be used as background.
# TM_HOMEPAGE_VIDEO_URL=
-# Endpoint for the missing maps stats
+# API base URL and token(used to retrieve user stats only) for ohsomeNow Stats
#
-TM_USER_STATS_API_URL=https://osm-stats-production-api.azurewebsites.net/users/
-TM_HOMEPAGE_STATS_API_URL=https://osmstats-api.hotosm.org/wildcard?key=hotosm-project-*
+OHSOME_STATS_BASE_URL=https://stats.now.ohsome.org/api
+OHSOME_STATS_TOKEN=testSuperSecretTestToken
# Secret (required)
#
diff --git a/frontend/.env.expand b/frontend/.env.expand
index df7cb9d0ba..29585a9c42 100644
--- a/frontend/.env.expand
+++ b/frontend/.env.expand
@@ -29,8 +29,8 @@ REACT_APP_MAPBOX_TOKEN=$TM_MAPBOX_TOKEN
REACT_APP_ENABLE_SERVICEWORKER=$TM_ENABLE_SERVICEWORKER
REACT_APP_MAX_FILESIZE=$TM_IMPORT_MAX_FILESIZE
REACT_APP_MAX_AOI_AREA=$TM_MAX_AOI_AREA
-REACT_APP_USER_STATS_API_URL=$TM_USER_STATS_API_URL
-REACT_APP_HOMEPAGE_STATS_API_URL=$TM_HOMEPAGE_STATS_API_URL
+REACT_APP_OHSOME_STATS_BASE_URL=$OHSOME_STATS_BASE_URL
+REACT_APP_OHSOME_STATS_TOKEN=$OHSOME_STATS_TOKEN
REACT_APP_OSM_CLIENT_ID=$TM_CLIENT_ID
REACT_APP_OSM_CLIENT_SECRET=$TM_CLIENT_SECRET
REACT_APP_OSM_REDIRECT_URI=$TM_REDIRECT_URI
diff --git a/frontend/src/api/projects.js b/frontend/src/api/projects.js
index 66163644b8..f9d46564d3 100644
--- a/frontend/src/api/projects.js
+++ b/frontend/src/api/projects.js
@@ -37,8 +37,10 @@ export const useProjectsQuery = (fullProjectsQuery, action) => {
};
export const useProjectQuery = (projectId) => {
+ const token = useSelector((state) => state.auth.token);
+ const locale = useSelector((state) => state.preferences['locale']);
const fetchProject = ({ signal }) => {
- return api().get(`projects/${projectId}/`, {
+ return api(token, locale).get(`projects/${projectId}/`, {
signal,
});
};
diff --git a/frontend/src/api/stats.js b/frontend/src/api/stats.js
index c54d0b7332..5bacdd28dc 100644
--- a/frontend/src/api/stats.js
+++ b/frontend/src/api/stats.js
@@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import api from './apiClient';
-import { HOMEPAGE_STATS_API_URL } from '../config';
+import { OHSOME_STATS_BASE_URL } from '../config';
export const useSystemStatisticsQuery = () => {
const fetchSystemStats = ({ signal }) => {
@@ -33,7 +33,7 @@ export const useProjectStatisticsQuery = (projectId) => {
export const useOsmStatsQuery = () => {
const fetchOsmStats = ({ signal }) => {
- return api().get(HOMEPAGE_STATS_API_URL, {
+ return api().get(`${OHSOME_STATS_BASE_URL}/stats/hotosm-project-%2A`, {
signal,
});
};
@@ -42,20 +42,15 @@ export const useOsmStatsQuery = () => {
queryKey: ['osm-stats'],
queryFn: fetchOsmStats,
useErrorBoundary: true,
+ select: (data) => data.data.result
});
};
export const useOsmHashtagStatsQuery = (defaultComment) => {
const fetchOsmStats = ({ signal }) => {
- return api().get(
- `https://osm-stats-production-api.azurewebsites.net/stats/${defaultComment[0].replace(
- '#',
- '',
- )}`,
- {
- signal,
- },
- );
+ return api().get(`${OHSOME_STATS_BASE_URL}/stats/${defaultComment[0].replace('#', '')}`, {
+ signal,
+ });
};
return useQuery({
@@ -63,6 +58,6 @@ export const useOsmHashtagStatsQuery = (defaultComment) => {
queryFn: fetchOsmStats,
useErrorBoundary: true,
enabled: Boolean(defaultComment?.[0]),
- select: (data) => data.data,
+ select: (data) => data.data.result,
});
};
diff --git a/frontend/src/assets/styles/_extra.scss b/frontend/src/assets/styles/_extra.scss
index 49359a7bc4..b5887bdfb5 100644
--- a/frontend/src/assets/styles/_extra.scss
+++ b/frontend/src/assets/styles/_extra.scss
@@ -616,3 +616,8 @@ a[href="https://www.mapbox.com/map-feedback/"]
.link:focus {
outline: revert;
}
+
+// Override tachyons font-family for code tag
+.code {
+ font-family: inherit;
+}
diff --git a/frontend/src/components/homepage/stats.js b/frontend/src/components/homepage/stats.js
index da36a3e1aa..1901fa35f4 100644
--- a/frontend/src/components/homepage/stats.js
+++ b/frontend/src/components/homepage/stats.js
@@ -42,15 +42,15 @@ export const StatsSection = () => {
user.username)}
+ contributors={
+ Array.isArray(contributors) ? contributors.map((user) => user.username) : undefined
+ }
/>
diff --git a/frontend/src/components/projectStats/edits.js b/frontend/src/components/projectStats/edits.js
index 04a8a96164..7f76e9733f 100644
--- a/frontend/src/components/projectStats/edits.js
+++ b/frontend/src/components/projectStats/edits.js
@@ -1,14 +1,13 @@
import React from 'react';
-import ReactTooltip from 'react-tooltip';
-import { FormattedMessage, useIntl } from 'react-intl';
+import { FormattedMessage } from 'react-intl';
import projectMessages from './messages';
import userDetailMessages from '../userDetail/messages';
-import { MappingIcon, HomeIcon, RoadIcon, EditIcon, InfoIcon } from '../svgIcons';
+import { MappingIcon, HomeIcon, RoadIcon, EditIcon } from '../svgIcons';
import { StatsCard } from '../statsCard';
+import StatsTimestamp from '../statsTimestamp';
export const EditsStats = ({ data }) => {
- const intl = useIntl();
const { changesets, buildings, roads, edits } = data;
const iconClass = 'h-50 w-50';
@@ -16,14 +15,12 @@ export const EditsStats = ({ data }) => {
return (
-
-
-
-
-
+
+
+
+
+
+
{
hasPrev: false,
page: 1,
pages: 3,
+ total: 10,
}}
/>,
);
@@ -32,6 +33,7 @@ describe('ProjectCardPaginator Component', () => {
hasPrev: false,
page: 1,
pages: 3,
+ total: 10,
}}
/>,
);
diff --git a/frontend/src/components/rapidEditor.js b/frontend/src/components/rapidEditor.js
index fc5f833c81..05fbc219f9 100644
--- a/frontend/src/components/rapidEditor.js
+++ b/frontend/src/components/rapidEditor.js
@@ -1,6 +1,8 @@
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
+import PropTypes from 'prop-types';
+
import { OSM_CLIENT_ID, OSM_CLIENT_SECRET, OSM_REDIRECT_URI, OSM_SERVER_URL } from '../config';
import { types } from '../store/actions/editor';
@@ -278,5 +280,15 @@ function RapidEditor({
return ;
}
+RapidEditor.propTypes = {
+ setDisable: PropTypes.func,
+ comment: PropTypes.string,
+ presets: PropTypes.array,
+ imagery: PropTypes.string,
+ gpxUrl: PropTypes.string.isRequired,
+ powerUser: PropTypes.bool.isRequired,
+ showSidebar: PropTypes.bool.isRequired,
+};
+
export { RapidEditor, generateStartingHash, equalsUrlParameters, updateUrl };
export default RapidEditor;
diff --git a/frontend/src/components/statsTimestamp/index.js b/frontend/src/components/statsTimestamp/index.js
new file mode 100644
index 0000000000..447ac28351
--- /dev/null
+++ b/frontend/src/components/statsTimestamp/index.js
@@ -0,0 +1,45 @@
+import { useState, useEffect } from 'react';
+import { useIntl } from 'react-intl';
+import ReactTooltip from 'react-tooltip';
+
+import { fetchExternalJSONAPI } from '../../network/genericJSONRequest';
+import { OHSOME_STATS_BASE_URL } from '../../config';
+import { InfoIcon } from '../svgIcons';
+import messages from './messages';
+
+function StatsTimestamp({ messageType }) {
+ const intl = useIntl();
+ const [lastUpdated, setLastUpdated] = useState(null);
+
+ useEffect(() => {
+ fetchExternalJSONAPI(`${OHSOME_STATS_BASE_URL}/metadata`)
+ .then((res) => {
+ setLastUpdated(res.result.max_timestamp);
+ })
+ .catch((error) => console.error(error));
+ }, []);
+
+ const dateOptions = {
+ year: 'numeric',
+ month: 'short',
+ day: '2-digit',
+ hour: 'numeric',
+ minute: 'numeric',
+ };
+
+ return (
+
+
+
+
+ );
+}
+
+export default StatsTimestamp;
diff --git a/frontend/src/components/statsTimestamp/messages.js b/frontend/src/components/statsTimestamp/messages.js
new file mode 100644
index 0000000000..5151378ecb
--- /dev/null
+++ b/frontend/src/components/statsTimestamp/messages.js
@@ -0,0 +1,14 @@
+import { defineMessages } from 'react-intl';
+
+export default defineMessages({
+ generic: {
+ id: 'stats.ohsome.timestamp.generic',
+ defaultMessage:
+ 'These statistics come from ohsomeNow Stats and were last updated at {formattedDate} ({timeZone}). Missing fields will be made available soon!',
+ },
+ project: {
+ id: 'stats.ohsome.timestamp.project',
+ defaultMessage:
+ 'These stats were retrieved using the default changeset comment of the project and were last updated at {formattedDate} ({timeZone}).',
+ },
+});
diff --git a/frontend/src/components/taskSelection/actionSidebars.js b/frontend/src/components/taskSelection/actionSidebars.js
index cdfd3e2b86..4dcceaed3f 100644
--- a/frontend/src/components/taskSelection/actionSidebars.js
+++ b/frontend/src/components/taskSelection/actionSidebars.js
@@ -185,9 +185,10 @@ export function CompletionTabForMapping({
)}
{showReadCommentsAlert && (
historyTabSwitch()}
+ onKeyDown={() => {}}
>
@@ -586,19 +587,12 @@ const TaskValidationSelector = ({
// the contributors is filled only on the case of single task validation,
// so we need to fetch the task history in the case of multiple task validation
useEffect(() => {
- if (showCommentInput && isValidatingMultipleTasks && !contributors.length) {
+ if (showCommentInput && isValidatingMultipleTasks) {
fetchLocalJSONAPI(`projects/${projectId}/tasks/${id}/`).then((response) =>
setContributorsList(getTaskContributors(response.taskHistory, userDetails.username)),
);
}
- }, [
- isValidatingMultipleTasks,
- showCommentInput,
- contributors,
- id,
- projectId,
- userDetails.username,
- ]);
+ }, [isValidatingMultipleTasks, showCommentInput, id, projectId, userDetails.username]);
return (
@@ -650,7 +644,7 @@ const TaskValidationSelector = ({
setVisibility(false)}
+ onKeyDown={() => {}}
>
@@ -834,6 +829,7 @@ function TaskSpecificInstructions({ instructions, open = true }: Object) {
className="ttu blue-grey mt1 mb0 pointer"
role="button"
onClick={() => setIsOpen(!isOpen)}
+ onKeyDown={() => {}}
>
{isOpen ? (
diff --git a/frontend/src/components/taskSelection/tests/actionSidebars.test.js b/frontend/src/components/taskSelection/tests/actionSidebars.test.js
index 413881ec70..bfd2770f9b 100644
--- a/frontend/src/components/taskSelection/tests/actionSidebars.test.js
+++ b/frontend/src/components/taskSelection/tests/actionSidebars.test.js
@@ -19,12 +19,16 @@ import {
import { setupFaultyHandlers } from '../../../network/tests/server';
import messages from '../messages';
import { store } from '../../../store';
+import { TaskMapAction } from '../action';
+import { getProjectSummary } from '../../../network/tests/mockData/projects';
+import tasksGeojson from '../../../utils/tests/snippets/tasksGeometry';
+import { userMultipleLockedTasksDetails } from '../../../network/tests/mockData/userStats';
jest.mock('react-hot-toast', () => ({
error: jest.fn(),
}));
-describe('Appeareance of unsaved map changes to be dealt with while mapping', () => {
+describe('Appearance of unsaved map changes to be dealt with while mapping', () => {
test('when splitting a task', async () => {
const { user } = renderWithRouter(
@@ -183,7 +187,7 @@ describe('Miscellaneous modals and prompts', () => {
});
});
-describe('Appeareance of unsaved map changes to be dealt with while validating', () => {
+describe('Appearance of unsaved map changes to be dealt with while validating', () => {
test('when stopping validation session', async () => {
const { user } = renderWithRouter(
@@ -340,31 +344,34 @@ describe('Toggling display of the sidebar', () => {
});
it('should call the sidebar toggle function for RAPID editor', async () => {
- const restartMock = jest.fn();
+ // Testing the resize call cannot be done currently, due to the following reasons:
+ // 1. Jest cannot mock/spy on the function call
+ // 2. The test environment doesn't have width/height information
+ // 3. The resize call in Rapid cannot be mocked since it is difficult to (a) get the context and (b) mock the call prior to full initialization.
+ // const resizeMock = jest.fn();
+ // expect(resizeMock).toHaveBeenCalledTimes(1); // This should be at the end of the test
const user = userEvent.setup();
- const context = {
- ui: jest.fn().mockReturnValue({
- restart: restartMock,
- }),
- };
- act(() => {
- store.dispatch({ type: 'SET_RAPIDEDITOR', context: context });
- });
- const setShowSidebarMock = jest.fn();
- render(
+ const { getByRole, queryByRole } = renderWithRouter(
-
+
,
);
await user.click(
- screen.getByRole('button', {
- name: /hide sidebar/i,
+ getByRole('button', {
+ name: 'Hide sidebar',
}),
);
- expect(setShowSidebarMock).toHaveBeenCalledTimes(1);
- expect(restartMock).toHaveBeenCalledTimes(1);
+ expect(getByRole('generic', { name: 'Show sidebar' })).toBeVisible();
+ expect(queryByRole('button', { name: 'Hide sidebar' })).toBeNull();
});
});
diff --git a/frontend/src/components/teamsAndOrgs/featureStats.js b/frontend/src/components/teamsAndOrgs/featureStats.js
index 6b6e8f9968..ffeeb5bd31 100644
--- a/frontend/src/components/teamsAndOrgs/featureStats.js
+++ b/frontend/src/components/teamsAndOrgs/featureStats.js
@@ -2,20 +2,25 @@ import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { FormattedMessage } from 'react-intl';
+import messages from './messages';
import userDetailMessages from '../userDetail/messages';
-import { HOMEPAGE_STATS_API_URL } from '../../config';
+import { OHSOME_STATS_BASE_URL } from '../../config';
import { RoadIcon, HomeIcon, WavesIcon, MarkerIcon } from '../svgIcons';
import { StatsCard } from '../statsCard';
+import StatsTimestamp from '../statsTimestamp';
export const FeatureStats = () => {
const [stats, setStats] = useState({ edits: 0, buildings: 0, roads: 0, pois: 0, waterways: 0 });
const getStats = async () => {
try {
- const response = await axios.get(HOMEPAGE_STATS_API_URL);
+ const response = await axios.get(
+ `${OHSOME_STATS_BASE_URL}/stats/hotosm-project-%2A`,
+ );
+ const { edits, buildings, roads } = response.data.result;
setStats({
- edits: response.data.edits,
- buildings: response.data.building_count_add,
- roads: response.data.road_km_add,
+ edits,
+ buildings,
+ roads,
pois: response.data.poi_count_add,
waterways: response.data.waterway_km_add,
});
@@ -32,31 +37,39 @@ export const FeatureStats = () => {
const iconStyle = { height: '45px' };
return (
-
- }
- description={}
- value={stats.buildings || 0}
- className={'w-25-l w-50-m w-100 mv1'}
- />
- }
- description={}
- value={stats.roads || 0}
- className={'w-25-l w-50-m w-100 mv1'}
- />
- }
- description={}
- value={stats.pois || 0}
- className={'w-25-l w-50-m w-100 mv1'}
- />
- }
- description={}
- value={stats.waterways || 0}
- className={'w-25-l w-50-m w-100 mv1'}
- />
-
+ <>
+
+
+
+
+
+
+
+ }
+ description={}
+ value={stats.buildings || 0}
+ className={'w-25-l w-50-m w-100 mv1'}
+ />
+ }
+ description={}
+ value={stats.roads || 0}
+ className={'w-25-l w-50-m w-100 mv1'}
+ />
+ }
+ description={}
+ value={stats.pois || 0}
+ className={'w-25-l w-50-m w-100 mv1'}
+ />
+ }
+ description={}
+ value={stats.waterways || 0}
+ className={'w-25-l w-50-m w-100 mv1'}
+ />
+
+ >
);
};
diff --git a/frontend/src/components/teamsAndOrgs/messages.js b/frontend/src/components/teamsAndOrgs/messages.js
index 74e77d5834..ace6caf287 100644
--- a/frontend/src/components/teamsAndOrgs/messages.js
+++ b/frontend/src/components/teamsAndOrgs/messages.js
@@ -553,4 +553,8 @@ export default defineMessages({
id: 'management.stats.overview',
defaultMessage: 'Overview',
},
+ totalFeatures: {
+ id: 'management.stats.features',
+ defaultMessage: 'Total features',
+ },
});
diff --git a/frontend/src/components/teamsAndOrgs/tests/featureStats.test.js b/frontend/src/components/teamsAndOrgs/tests/featureStats.test.js
index 0562cf4cdf..1dae46c965 100644
--- a/frontend/src/components/teamsAndOrgs/tests/featureStats.test.js
+++ b/frontend/src/components/teamsAndOrgs/tests/featureStats.test.js
@@ -23,6 +23,7 @@ test('FeatureStats renders the correct values and labels', async () => {
expect(screen.getByText('Km waterways mapped')).toBeInTheDocument();
await waitFor(() => expect(screen.getByText('2,380,562')).toBeInTheDocument());
expect(screen.getByText('101,367,027')).toBeInTheDocument();
- expect(screen.getByText('183,011')).toBeInTheDocument();
- expect(screen.getByText('350,906')).toBeInTheDocument();
+ // Uncomment the following when POIs and waterways become available
+ // expect(screen.getByText('183,011')).toBeInTheDocument();
+ // expect(screen.getByText('350,906')).toBeInTheDocument();
});
diff --git a/frontend/src/components/userDetail/editsByNumbers.js b/frontend/src/components/userDetail/editsByNumbers.js
index 67720a8583..45546d9ec5 100644
--- a/frontend/src/components/userDetail/editsByNumbers.js
+++ b/frontend/src/components/userDetail/editsByNumbers.js
@@ -15,13 +15,13 @@ const EditsByNumbers = ({ osmStats }) => {
let reference = [
{
label: intl.formatMessage(typesMessages.buildings),
- field: 'total_building_count_add',
+ field: 'buildings',
backgroundColor: CHART_COLOURS.red,
borderColor: CHART_COLOURS.white,
},
{
label: intl.formatMessage(typesMessages.roads),
- field: 'total_road_km_add',
+ field: 'roads',
backgroundColor: CHART_COLOURS.green,
borderColor: CHART_COLOURS.white,
},
diff --git a/frontend/src/components/userDetail/elementsMapped.js b/frontend/src/components/userDetail/elementsMapped.js
index f8cebf460f..72c9a58126 100644
--- a/frontend/src/components/userDetail/elementsMapped.js
+++ b/frontend/src/components/userDetail/elementsMapped.js
@@ -1,6 +1,5 @@
import React from 'react';
import humanizeDuration from 'humanize-duration';
-import ReactTooltip from 'react-tooltip';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
@@ -10,11 +9,11 @@ import {
HomeIcon,
WavesIcon,
MarkerIcon,
- QuestionCircleIcon,
MappedIcon,
ValidatedIcon,
} from '../svgIcons';
import { StatsCard } from '../statsCard';
+import StatsTimestamp from '../statsTimestamp';
export const TaskStats = ({ userStats, username }) => {
const {
@@ -137,35 +136,26 @@ export const ElementsMapped = ({ userStats, osmStats }) => {
}
description={}
- value={osmStats.total_building_count_add || 0}
+ value={osmStats.buildings || 0}
/>
}
description={}
- value={osmStats.total_road_km_add || 0}
+ value={osmStats.roads || 0}
/>
}
description={}
- value={osmStats.total_poi_count_add || 0}
+ value={osmStats.total_poi_count_add || '-'}
/>
}
description={}
- value={osmStats.total_waterway_km_add || 0}
+ value={osmStats.total_waterway_km_add || '-'}
/>
-
- {(msg) => (
-
- )}
-
-
+
);
diff --git a/frontend/src/components/userDetail/messages.js b/frontend/src/components/userDetail/messages.js
index 209382b094..fa4035a956 100644
--- a/frontend/src/components/userDetail/messages.js
+++ b/frontend/src/components/userDetail/messages.js
@@ -121,11 +121,6 @@ export default defineMessages({
id: 'users.detail.heatmapLegendLess',
defaultMessage: 'less',
},
- delayPopup: {
- id: 'users.detail.delay_popup',
- defaultMessage:
- 'These statistics need heavy calculations and changes are showing up with a delay of around one hour.',
- },
teams: {
id: 'users.detail.teams',
defaultMessage: 'Teams',
diff --git a/frontend/src/components/userDetail/tests/editByNumbers.test.js b/frontend/src/components/userDetail/tests/editByNumbers.test.js
index 4b4f653ae6..e8d370e897 100644
--- a/frontend/src/components/userDetail/tests/editByNumbers.test.js
+++ b/frontend/src/components/userDetail/tests/editByNumbers.test.js
@@ -27,12 +27,10 @@ describe('EditsByNumbers card', () => {
it('renders the chart if osmStats data is present', () => {
const stats = {
- total_building_count_add: 3282,
- total_building_count_mod: 7959,
+ buildings: 3282,
total_waterway_count_add: 11493,
total_poi_count_add: 10805,
- total_road_km_add: 5571.84370201545,
- total_road_km_mod: 4203.47860727417,
+ roads: 5571.84370201545,
total_waterway_km_add: 512.706405358494,
total_road_count_add: 13345,
total_road_count_mod: 51730,
diff --git a/frontend/src/components/userDetail/tests/elementsMapped.test.js b/frontend/src/components/userDetail/tests/elementsMapped.test.js
index 0350156d19..592e1e6a68 100644
--- a/frontend/src/components/userDetail/tests/elementsMapped.test.js
+++ b/frontend/src/components/userDetail/tests/elementsMapped.test.js
@@ -12,7 +12,7 @@ describe('ElementsMapped & TaskStats components', () => {
};
const osmStats = {
total_building_count_add: 10,
- total_road_km_add: 229.113,
+ roads: 229.113,
total_poi_count_add: 15,
total_waterway_count_add: 20,
};
diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js
index 1b0845da0a..41074cd781 100644
--- a/frontend/src/config/index.js
+++ b/frontend/src/config/index.js
@@ -3,12 +3,9 @@ export const API_VERSION = process.env.REACT_APP_API_VERSION || 'v2';
export const API_URL = process.env.REACT_APP_API_URL
? new URL('/api/' + API_VERSION + '/', process.env.REACT_APP_API_URL)
: 'http://127.0.0.1:5000/api/' + API_VERSION + '/';
-export const HOMEPAGE_STATS_API_URL =
- process.env.REACT_APP_HOMEPAGE_STATS_API_URL ||
- 'https://osmstats-api.hotosm.org/wildcard/?key=hotosm-project-*';
-export const USER_STATS_API_URL =
- process.env.REACT_APP_USER_STATS_API_URL ||
- 'https://osm-stats-production-api.azurewebsites.net/users/';
+export const OHSOME_STATS_BASE_URL =
+ process.env.REACT_APP_OHSOME_STATS_BASE_URL || 'https://stats.now.ohsome.org/api';
+export const OHSOME_STATS_TOKEN = process.env.REACT_APP_OHSOME_STATS_TOKEN || '';
// APPLICATION SETTINGS
export const DEFAULT_LOCALE = process.env.REACT_APP_DEFAULT_LOCALE || 'en';
diff --git a/frontend/src/config/tests/config.test.js b/frontend/src/config/tests/config.test.js
index 795c2427ca..67e100878f 100644
--- a/frontend/src/config/tests/config.test.js
+++ b/frontend/src/config/tests/config.test.js
@@ -7,11 +7,8 @@ it('exports API_URL', () => {
it('exports API_VERSION', () => {
expect(['object', 'string']).toContain(typeof config.API_VERSION);
});
-it('exports HOMEPAGE_STATS_API_URL', () => {
- expect(typeof config.HOMEPAGE_STATS_API_URL).toBe('string');
-});
-it('exports USER_STATS_API_URL', () => {
- expect(typeof config.USER_STATS_API_URL).toBe('string');
+it('exports OHSOME_STATS_BASE_URL', () => {
+ expect(typeof config.OHSOME_STATS_BASE_URL).toBe('string');
});
it('exports ORG_URL', () => {
expect(typeof config.ORG_URL).toBe('string');
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index e74401293b..38d961f9fa 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -616,7 +616,8 @@
"project.stats.totalEdits": "Total map edits",
"project.stats.changesets": "Changesets",
"project.stats.edits": "Edits",
- "project.stats.edits.info": "These stats are retrieved using the default changeset comment of the project",
+ "stats.ohsome.timestamp.generic": "These statistics come from ohsomeNow Stats and were last updated at {formattedDate} ({timeZone}). Missing fields will be made available soon!",
+ "stats.ohsome.timestamp.project": "These stats were retrieved using the default changeset comment of the project and were last updated at {formattedDate} ({timeZone}).",
"project.tasks.unsaved_map_changes.title": "You have some unsaved map changes",
"project.tasks.unsaved_map_changes.split": "Save or undo it to be able to split the task",
"project.tasks.unsaved_map_changes.unlock": "Save or undo it to be able to select another task",
@@ -945,6 +946,7 @@
"management.stats.new_users.email_verified": "Confirmed email address",
"management.stats.title": "Statistics",
"management.stats.overview": "Overview",
+ "management.stats.features": "Total features",
"user.nextLevel": "{changesets} / {nextLevelThreshold} changesets to {level}",
"user.personalInfo": "Personal information",
"user.name": "Name",
@@ -1065,7 +1067,6 @@
"users.detail.heatmapContributions": "contributions",
"users.detail.heatmapLegendMore": "more",
"users.detail.heatmapLegendLess": "less",
- "users.detail.delay_popup": "These statistics need heavy calculations and changes are showing up with a delay of around one hour.",
"users.detail.teams": "Teams",
"error.page.title": "An error occurred",
"error.page.description": "Something did not work well...",
@@ -1083,7 +1084,6 @@
"management.managers": "Managers",
"management.users.title": "Manage users",
"management.stats.users.title": "New users",
- "management.stats.features": "Total features",
"teamsAndOrgs.management.organisation.creation": "Create new organization",
"teamsAndOrgs.management.organisation.orgCreationNameExistsError": "Organisation name already exists",
"teamsAndOrgs.management.organisation.edit": "Edit organization",
diff --git a/frontend/src/network/genericJSONRequest.js b/frontend/src/network/genericJSONRequest.js
index 9af29cbfe2..f877c9813a 100644
--- a/frontend/src/network/genericJSONRequest.js
+++ b/frontend/src/network/genericJSONRequest.js
@@ -1,12 +1,19 @@
import { handleErrors } from '../utils/promise';
-import { API_URL } from '../config';
+import { API_URL, OHSOME_STATS_TOKEN } from '../config';
+
+export function fetchExternalJSONAPI(url, isSetToken = false): Promise<*> {
+ const headers = {
+ 'Content-Type': 'application/json',
+ };
+
+ // Passing token only for ohsomeNow stats
+ if (isSetToken) {
+ headers['Authorization'] = `Basic ${OHSOME_STATS_TOKEN}`;
+ }
-export function fetchExternalJSONAPI(url): Promise<*> {
return fetch(url, {
method: 'GET',
- headers: {
- 'Content-Type': 'application/json',
- },
+ headers: headers,
})
.then(handleErrors)
.then((res) => {
diff --git a/frontend/src/network/tests/mockData/homepageStats.js b/frontend/src/network/tests/mockData/homepageStats.js
index 34422d4b4a..5d5f63300f 100644
--- a/frontend/src/network/tests/mockData/homepageStats.js
+++ b/frontend/src/network/tests/mockData/homepageStats.js
@@ -1,15 +1,10 @@
export const homepageStats = {
- road_count_add: 5850597,
- road_count_mod: 6688860,
- building_count_add: 101367027,
- building_count_mod: 12680746,
- waterway_count_add: 3267982,
- poi_count_add: 183011,
- poi_count_mod: 251157,
- road_km_add: 2380562.9900000077,
- road_km_mod: 1499361.339999999,
- waterway_km_add: 350906.79999999935,
- waterway_km_mod: 199147.73999999985,
- edits: 134184186,
- users: 493238,
+ result: {
+ changesets: 10251848,
+ users: 493238,
+ roads: 2380562.9900000077,
+ buildings: 101367027,
+ edits: 134184186,
+ latest: '2023-10-03T15:28:07Z',
+ },
};
diff --git a/frontend/src/network/tests/mockData/miscellaneous.js b/frontend/src/network/tests/mockData/miscellaneous.js
index 5ef4c468e4..f28b8cf330 100644
--- a/frontend/src/network/tests/mockData/miscellaneous.js
+++ b/frontend/src/network/tests/mockData/miscellaneous.js
@@ -34,3 +34,10 @@ export const systemStats = {
totalMappers: 3,
totalProjects: 10,
};
+
+export const ohsomeNowMetadata = {
+ result: {
+ max_timestamp: '2023-10-03T15:47:49Z',
+ min_timestamp: '2009-04-21T20:02:04Z',
+ },
+};
diff --git a/frontend/src/network/tests/mockData/userStats.js b/frontend/src/network/tests/mockData/userStats.js
index b151d51324..0e2b1efd38 100644
--- a/frontend/src/network/tests/mockData/userStats.js
+++ b/frontend/src/network/tests/mockData/userStats.js
@@ -69,126 +69,25 @@ export const userStats = {
],
};
-export const osmStatsProd = {
- id: 10291369,
- name: 'helnershingthapa',
- changesets: '361',
- geo_extent: null,
- total_building_count_add: 6771,
- total_building_count_mod: 786,
- total_waterway_count_add: 2,
- total_poi_count_add: 81,
- total_road_km_add: 29.0827659890004,
- total_road_km_mod: 34.4722514676097,
- total_waterway_km_add: 0.497270130447684,
- total_josm_edit_count: 298,
- total_gps_trace_count_add: 0,
- total_gps_trace_updated_from_osm: 0,
- total_road_count_add: 59,
- total_road_count_mod: 250,
- total_tm_done_count: 0,
- total_tm_val_count: 0,
- total_tm_inval_count: 0,
- badges: [
- {
- updated_at: '2020-11-20T12:29:36.265Z',
- id: 11,
- category: 4,
- level: 2,
- name: 'The Wright Stuff',
- },
- {
- updated_at: '2021-08-11T13:05:02.367Z',
- id: 12,
- category: 4,
- level: 3,
- name: 'The Wright Stuff',
- },
- {
- updated_at: '2022-12-28T08:23:30.755Z',
- id: 26,
- category: 9,
- level: 2,
- name: 'World Renown',
- },
- ],
- changeset_count: 361,
- latest: {
- id: '130862065',
- road_count_add: 0,
- road_count_mod: 1,
- building_count_add: 7,
- building_count_mod: 2,
- waterway_count_add: 0,
- poi_count_add: 0,
- gpstrace_count_add: 0,
- road_km_add: 0,
- road_km_mod: 0.00768819386078029,
- waterway_km_add: 0,
- gpstrace_km_add: 0,
- editor: 'JOSM/1.5 (18583 en)',
+export const ohsomeNowUserStats = {
+ result: {
+ building_count: 2,
+ road_length: 572.750505196795,
+ edits: 12618,
user_id: 10291369,
- created_at: '2023-01-04T11:15:22.000Z',
- countries: [
- {
- id: 175,
- name: 'Nepal',
- code: 'NPL',
- },
- ],
- hashtags: [
- {
- id: 4,
- hashtag: 'awesome',
- },
- {
- id: 2101,
- hashtag: 'hot',
- },
- {
- id: 14913965,
- hashtag: 'ootd',
- },
- {
- id: 17849271,
- hashtag: 'art',
- },
- ],
- },
- edit_times: [
- '2019-09-11T00:00:00.000Z',
- '2019-09-12T00:00:00.000Z',
- '2019-11-15T00:00:00.000Z',
- '2020-01-27T00:00:00.000Z',
- '2020-02-28T00:00:00.000Z',
- '2020-11-20T00:00:00.000Z',
- '2021-02-01T00:00:00.000Z',
- '2021-02-04T00:00:00.000Z',
- '2021-02-12T00:00:00.000Z',
- '2021-02-14T00:00:00.000Z',
- ],
- country_count: 3,
- country_list: {
- NPL: 248,
- MNG: 28,
- IND: 11,
- },
- hashtags: {
- awesome: 139,
- hot: 126,
- ootd: 97,
- art: 78,
+ object_edits: 291,
},
};
export const osmStatsProject = {
- changesets: 987654321,
- users: 112,
- roads: 5658.62006919192,
- buildings: 12923,
- edits: 123456789,
- latest: '2020-10-05T23:21:22.000Z',
- hashtag: `hotosm-project-1`,
+ result: {
+ changesets: 987654321,
+ users: 112,
+ roads: 5658.62006919192,
+ buildings: 12923,
+ edits: 123456789,
+ latest: '2020-10-05T23:21:22.000Z',
+ },
};
export const userLockedTasksDetails = {
diff --git a/frontend/src/network/tests/server-handlers.js b/frontend/src/network/tests/server-handlers.js
index e55b43ae30..66851658df 100644
--- a/frontend/src/network/tests/server-handlers.js
+++ b/frontend/src/network/tests/server-handlers.js
@@ -18,10 +18,10 @@ import {
import { featuredProjects } from './mockData/featuredProjects';
import {
newUsersStats,
- osmStatsProd,
+ userStats,
osmStatsProject,
userLockedTasksDetails,
- userStats,
+ ohsomeNowUserStats,
} from './mockData/userStats';
import { projectContributions, projectContributionsByDay } from './mockData/contributions';
import {
@@ -61,9 +61,15 @@ import {
} from './mockData/teams';
import { userTasks } from './mockData/tasksStats';
import { homepageStats } from './mockData/homepageStats';
-import { banner, countries, josmRemote, systemStats } from './mockData/miscellaneous';
+import {
+ banner,
+ countries,
+ josmRemote,
+ systemStats,
+ ohsomeNowMetadata,
+} from './mockData/miscellaneous';
import tasksGeojson from '../../utils/tests/snippets/tasksGeometry';
-import { API_URL } from '../../config';
+import { API_URL, OHSOME_STATS_BASE_URL } from '../../config';
import { notifications, ownCountUnread } from './mockData/notifications';
import { authLogin, setUser, userRegister } from './mockData/auth';
import {
@@ -343,21 +349,18 @@ const handlers = [
return res(ctx.json(systemStats));
}),
// EXTERNAL API
- rest.get('https://osmstats-api.hotosm.org/wildcard', (req, res, ctx) => {
+ rest.get(`${OHSOME_STATS_BASE_URL}/stats/hotosm-project-%2A`, (req, res, ctx) => {
return res(ctx.json(homepageStats));
}),
- rest.get(
- 'https://osm-stats-production-api.azurewebsites.net/users/:username',
- (req, res, ctx) => {
- return res(ctx.json(osmStatsProd));
- },
- ),
- rest.get(
- 'https://osm-stats-production-api.azurewebsites.net/stats/:projectId',
- (req, res, ctx) => {
- return res(ctx.json(osmStatsProject));
- },
- ),
+ rest.get(`${OHSOME_STATS_BASE_URL}/hot-tm-user`, (req, res, ctx) => {
+ return res(ctx.json(ohsomeNowUserStats));
+ }),
+ rest.get(`${OHSOME_STATS_BASE_URL}/stats/:projectId`, (req, res, ctx) => {
+ return res(ctx.json(osmStatsProject));
+ }),
+ rest.get(`${OHSOME_STATS_BASE_URL}/metadata`, (req, res, ctx) => {
+ return res(ctx.json(ohsomeNowMetadata));
+ }),
rest.get('http://127.0.0.1:8111/version', (req, res, ctx) => {
return res(ctx.json(josmRemote));
}),
diff --git a/frontend/src/utils/internationalization.js b/frontend/src/utils/internationalization.js
index fbfbe457b1..cc8e419f15 100644
--- a/frontend/src/utils/internationalization.js
+++ b/frontend/src/utils/internationalization.js
@@ -131,6 +131,7 @@ let ConnectedIntl = (props) => {
locale={props.locale ? props.locale.substr(0, 2) : config.DEFAULT_LOCALE}
textComponent={React.Fragment}
messages={getTranslatedMessages(props.locale)}
+ timeZone={Intl.DateTimeFormat().resolvedOptions().timeZone}
>
{props.children}
diff --git a/frontend/src/views/contributions.js b/frontend/src/views/contributions.js
index a6e79dda22..fd1b4eaffc 100644
--- a/frontend/src/views/contributions.js
+++ b/frontend/src/views/contributions.js
@@ -46,7 +46,12 @@ export const ContributionsPage = () => {
);
};
diff --git a/frontend/src/views/home.js b/frontend/src/views/home.js
index 796ee67f5c..1483a89474 100644
--- a/frontend/src/views/home.js
+++ b/frontend/src/views/home.js
@@ -9,6 +9,7 @@ import { WhoIsMapping } from '../components/homepage/whoIsMapping';
import { Testimonials } from '../components/homepage/testimonials';
import { Alert } from '../components/alert';
import homeMessages from '../components/homepage/messages';
+import StatsTimestamp from '../components/statsTimestamp/';
export function Home() {
return (
@@ -24,6 +25,9 @@ export function Home() {
}
>
+
+
+
diff --git a/frontend/src/views/messages.js b/frontend/src/views/messages.js
index 92d046938e..4bfe23c3ef 100644
--- a/frontend/src/views/messages.js
+++ b/frontend/src/views/messages.js
@@ -33,7 +33,7 @@ export default defineMessages({
defaultMessage: 'Project timeline',
},
timelineDataError: {
- id: 'project.stats.timeline.fetching.error',
+ id: 'project.stats.contributions.timeline.fetching.error',
defaultMessage: 'An error occured while loading contributions data',
},
editsStatsError: {
@@ -84,10 +84,6 @@ export default defineMessages({
id: 'management.stats.users.title',
defaultMessage: 'New users',
},
- totalFeatures: {
- id: 'management.stats.features',
- defaultMessage: 'Total features',
- },
newOrganisation: {
id: 'teamsAndOrgs.management.organisation.creation',
defaultMessage: 'Create new organization',
diff --git a/frontend/src/views/settings.js b/frontend/src/views/settings.js
index 4a558c6b05..8aa65032ec 100644
--- a/frontend/src/views/settings.js
+++ b/frontend/src/views/settings.js
@@ -34,7 +34,7 @@ export function Settings() {
-
+ {userDetails?.username &&
}
diff --git a/frontend/src/views/stats.js b/frontend/src/views/stats.js
index 309ddaebd0..c98f99541f 100644
--- a/frontend/src/views/stats.js
+++ b/frontend/src/views/stats.js
@@ -45,9 +45,6 @@ export const Stats = () => {
-
-
-
diff --git a/frontend/src/views/teams.js b/frontend/src/views/teams.js
index 41c4fd99d2..a11ac3f254 100644
--- a/frontend/src/views/teams.js
+++ b/frontend/src/views/teams.js
@@ -426,7 +426,7 @@ export function TeamDetail() {
const [error, loading, team] = useFetch(`teams/${id}/`);
// eslint-disable-next-line
const [projectsError, projectsLoading, projects] = useFetch(
- `projects/?teamId=${id}&omitMapResults=true`,
+ `projects/?teamId=${id}&omitMapResults=true&projectStatuses=PUBLISHED,DRAFT,ARCHIVED`,
id,
);
const [isMember, setIsMember] = useState(false);
diff --git a/frontend/src/views/userDetail.js b/frontend/src/views/userDetail.js
index 8bb6b5791e..305d508f86 100644
--- a/frontend/src/views/userDetail.js
+++ b/frontend/src/views/userDetail.js
@@ -12,7 +12,7 @@ import { CountriesMapped } from '../components/userDetail/countriesMapped';
import { TopProjects } from '../components/userDetail/topProjects';
import { ContributionTimeline } from '../components/userDetail/contributionTimeline';
import { NotFound } from './notFound';
-import { USER_STATS_API_URL } from '../config';
+import { OHSOME_STATS_BASE_URL, OSM_SERVER_URL } from '../config';
import { fetchExternalJSONAPI } from '../network/genericJSONRequest';
import { useFetch } from '../hooks/UseFetch';
import { useSetTitleTag } from '../hooks/UseMetaTags';
@@ -33,6 +33,7 @@ export const UserDetail = ({ withHeader = true }) => {
const token = useSelector((state) => state.auth.token);
const currentUser = useSelector((state) => state.auth.userDetails);
const [osmStats, setOsmStats] = useState({});
+ const [userOsmDetails, setUserOsmDetails] = useState({});
const [errorDetails, loadingDetails, userDetails] = useFetch(
`users/queries/${username}/`,
username !== undefined,
@@ -53,12 +54,15 @@ export const UserDetail = ({ withHeader = true }) => {
}, [navigate, token]);
useEffect(() => {
- if (token && username) {
- fetchExternalJSONAPI(`${USER_STATS_API_URL}${username}`)
- .then((res) => setOsmStats(res))
+ if (userDetails.id) {
+ fetchExternalJSONAPI(`${OSM_SERVER_URL}/api/0.6/user/${userDetails.id}.json`, false)
+ .then((res) => setUserOsmDetails(res?.user))
+ .catch((e) => console.log(e));
+ fetchExternalJSONAPI(`${OHSOME_STATS_BASE_URL}/hot-tm-user?userId=${userDetails.id}`, true)
+ .then((res) => setOsmStats(res.result))
.catch((e) => console.log(e));
}
- }, [token, username]);
+ }, [userDetails.id]);
const titleClass = 'contributions-titles fw5 ttu barlow-condensed blue-dark mt0';
@@ -74,7 +78,7 @@ export const UserDetail = ({ withHeader = true }) => {
rows={5}
ready={!errorDetails && !loadingDetails}
>
-
+
)}
diff --git a/scripts/aws/cloudformation/tasking-manager.template.js b/scripts/aws/cloudformation/tasking-manager.template.js
index e03637dc9e..7c66644165 100644
--- a/scripts/aws/cloudformation/tasking-manager.template.js
+++ b/scripts/aws/cloudformation/tasking-manager.template.js
@@ -562,7 +562,9 @@ const Resources = {
Name: cf.stackName,
SecurityGroups: [cf.importValue(cf.join('-', ['hotosm-network-production', cf.ref('NetworkEnvironment'), 'elbs-security-group', cf.region]))],
Subnets: cf.ref('ELBSubnets'),
- Type: 'application'
+ Type: 'application',
+ IpAddressType: 'dualstack',
+ Tags: [ { "Key": "stack_name", "Value": cf.stackName } ]
}
},
TaskingManagerLoadBalancerRoute53: {
@@ -651,6 +653,7 @@ const Resources = {
DBInstanceClass: cf.ref('DatabaseInstanceType'),
DBSnapshotIdentifier: cf.if('UseASnapshot', cf.ref('DBSnapshot'), cf.noValue),
VPCSecurityGroups: [cf.importValue(cf.join('-', ['hotosm-network-production', cf.ref('NetworkEnvironment'), 'ec2s-security-group', cf.region]))],
+ PubliclyAccessible: false
}
},
TaskingManagerReactBucket: {