diff --git a/backend/__init__.py b/backend/__init__.py index 096405c00d..4e19622fde 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -216,6 +216,7 @@ def add_api_endpoints(app): ProjectsQueriesPriorityAreasAPI, ProjectsQueriesFeaturedAPI, ProjectQueriesSimilarProjectsAPI, + ProjectQueriesActiveProjectsAPI, ) from backend.api.projects.activities import ( ProjectsActivitiesAPI, @@ -428,6 +429,10 @@ def add_api_endpoints(app): ProjectQueriesSimilarProjectsAPI, format_url("projects/queries//similar-projects/"), ) + api.add_resource( + ProjectQueriesActiveProjectsAPI, + format_url("projects/queries/active/"), + ) # Projects' addtional resources api.add_resource( diff --git a/backend/api/projects/resources.py b/backend/api/projects/resources.py index 3831ccb071..d2799d355b 100644 --- a/backend/api/projects/resources.py +++ b/backend/api/projects/resources.py @@ -1164,3 +1164,48 @@ def get(self, project_id): project_id, authenticated_user_id, preferred_locale, limit ) return projects_dto.to_primitive(), 200 + + +class ProjectQueriesActiveProjectsAPI(Resource): + @token_auth.login_required(optional=True) + def get(self): + """ + Get active projects + --- + tags: + - projects + produces: + - application/json + parameters: + - in: header + name: Authorization + description: Base64 encoded session token + required: false + type: string + default: Token sessionTokenHere== + - name: interval + in: path + description: Time interval in hours to get active project + required: false + type: integer + default: 24 + responses: + 200: + description: Active projects geojson + 404: + description: Project not found or project is not published + 500: + description: Internal Server Error + """ + interval = request.args.get("interval", "24") + if not interval.isdigit(): + return { + "Error": "Interval must be a number greater than 0 and less than or equal to 24" + }, 400 + interval = int(interval) + if interval <= 0 or interval > 24: + return { + "Error": "Interval must be a number greater than 0 and less than or equal to 24" + }, 400 + projects_dto = ProjectService.get_active_projects(interval) + return projects_dto, 200 diff --git a/backend/services/project_service.py b/backend/services/project_service.py index 7d6dd1f7e3..e8c5b3f318 100644 --- a/backend/services/project_service.py +++ b/backend/services/project_service.py @@ -1,6 +1,8 @@ import threading from cachetools import TTLCache, cached from flask import current_app +import geojson +from datetime import datetime, timedelta, timezone from backend.exceptions import NotFound from backend.models.dtos.mapping_dto import TaskDTOs @@ -615,3 +617,37 @@ def send_email_on_project_progress(project_id): project_completion, ), ).start() + + @staticmethod + def get_active_projects(interval): + action_date = datetime.now(timezone.utc) - timedelta(hours=interval) + result = ( + TaskHistory.query.with_entities(TaskHistory.project_id) + .distinct() + .filter(TaskHistory.action_date >= action_date) + .all() + ) + project_ids = [row.project_id for row in result] + projects = ( + Project.query.with_entities( + Project.id, + Project.mapping_types, + Project.geometry.ST_AsGeoJSON().label("geometry"), + ) + .filter( + Project.status == ProjectStatus.PUBLISHED.value, + Project.id.in_(project_ids), + ) + .all() + ) + features = [] + for project in projects: + properties = { + "project_id": project.id, + "mapping_types": project.mapping_types, + } + feature = geojson.Feature( + geometry=geojson.loads(project.geometry), properties=properties + ) + features.append(feature) + return geojson.FeatureCollection(features)