diff --git a/server/mergin/stats/tasks.py b/server/mergin/stats/tasks.py index 4770a90e..345d68cd 100644 --- a/server/mergin/stats/tasks.py +++ b/server/mergin/stats/tasks.py @@ -64,6 +64,7 @@ def send_statistics(): "workspaces_count": current_app.ws_handler.workspace_count(), "last_change": str(last_change_item.updated) + "Z" if last_change_item else "", "server_version": current_app.config["VERSION"], + "monthly_contributors": current_app.ws_handler.monthly_contributors_count(), } try: diff --git a/server/mergin/sync/interfaces.py b/server/mergin/sync/interfaces.py index 8084b091..4cf12650 100644 --- a/server/mergin/sync/interfaces.py +++ b/server/mergin/sync/interfaces.py @@ -154,6 +154,13 @@ def workspace_count(): """ pass + @staticmethod + def monthly_contributors_count(): + """ + Return number of workspace contributors in current month and year + """ + pass + class AbstractProjectHandler(ABC): @abstractmethod diff --git a/server/mergin/sync/workspace.py b/server/mergin/sync/workspace.py index 86360fef..7781f7be 100644 --- a/server/mergin/sync/workspace.py +++ b/server/mergin/sync/workspace.py @@ -2,14 +2,20 @@ # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-MerginMaps-Commercial -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from typing import Dict, Tuple, Optional, Set, List from flask_login import current_user -from sqlalchemy import or_, and_, Column, literal +from sqlalchemy import or_, and_, Column, literal, extract from sqlalchemy.orm import joinedload from .errors import UpdateProjectAccessError -from .models import Project, ProjectAccess, AccessRequest, ProjectAccessDetail +from .models import ( + Project, + ProjectAccess, + AccessRequest, + ProjectAccessDetail, + ProjectVersion, +) from .permissions import projects_query, ProjectPermissions from .public_api_controller import parse_project_access_update_request from .. import db @@ -248,6 +254,21 @@ def filter_projects( def workspace_count(): return 1 + @staticmethod + def monthly_contributors_count(): + today = datetime.now(timezone.utc) + year = today.year + month = today.month + return ( + db.session.query(ProjectVersion.author_id) + .filter( + extract("year", ProjectVersion.created) == year, + extract("month", ProjectVersion.created) == month, + ) + .group_by(ProjectVersion.author_id) + .count() + ) + def projects_query(self, name=None, workspace=None): ws = self.factory_method() query = db.session.query( diff --git a/server/mergin/tests/test_statistics.py b/server/mergin/tests/test_statistics.py index b952039b..f076d882 100644 --- a/server/mergin/tests/test_statistics.py +++ b/server/mergin/tests/test_statistics.py @@ -46,10 +46,12 @@ def test_send_statistics(app, caplog): "workspaces_count", "last_change", "server_version", + "monthly_contributors", } assert data["workspaces_count"] == 1 assert data["service_uuid"] == app.config["SERVICE_ID"] assert data["licence"] == "ce" + assert data["monthly_contributors"] == 1 # repeated action does not do anything task = send_statistics.s().apply() diff --git a/server/mergin/tests/test_workspace.py b/server/mergin/tests/test_workspace.py index 33b53bcb..e273d2f1 100644 --- a/server/mergin/tests/test_workspace.py +++ b/server/mergin/tests/test_workspace.py @@ -56,7 +56,8 @@ def test_workspace_implementation(client): diff=null(), change=PushChangeType.CREATE, ) - file_history.version = project.get_latest_version() + latest_version = project.get_latest_version() + file_history.version = latest_version file_history.project_version_name = file_history.version.name default_project_usage = ws.disk_usage() db.session.add(file_history) @@ -69,6 +70,19 @@ def test_workspace_implementation(client): db.session.commit() assert ws.disk_usage() == default_project_usage + current_time = datetime.datetime.now(datetime.timezone.utc) + latest_version.created = datetime.datetime.combine( + current_time.replace(day=1), datetime.time.max + ) - datetime.timedelta(days=1) + db.session.commit() + assert handler.monthly_contributors_count() == 1 + + # test group by author_id + create_project("test_second_project", ws, user) + latest_version.created = current_time + db.session.commit() + assert handler.monthly_contributors_count() == 2 + def test_workspace(client): """Test get global workspace""" diff --git a/server/mergin/tests/utils.py b/server/mergin/tests/utils.py index 121acf2f..a9743920 100644 --- a/server/mergin/tests/utils.py +++ b/server/mergin/tests/utils.py @@ -182,7 +182,6 @@ def initialize(): "added": project_files, "updated": [], "removed": [], - "renamed": [], } ) pv = ProjectVersion(p, 1, user.id, upload_changes, "127.0.0.1")