diff --git a/otterdog/webapp/db/__init__.py b/otterdog/webapp/db/__init__.py index 32271893..5699ae15 100644 --- a/otterdog/webapp/db/__init__.py +++ b/otterdog/webapp/db/__init__.py @@ -43,6 +43,7 @@ async def init_mongo_database(mongo: Mongo) -> None: ConfigurationModel, InstallationModel, PullRequestModel, + StatisticsModel, TaskModel, ) @@ -52,5 +53,6 @@ async def init_mongo_database(mongo: Mongo) -> None: TaskModel, ConfigurationModel, PullRequestModel, + StatisticsModel, ] # type: ignore ) diff --git a/otterdog/webapp/db/models.py b/otterdog/webapp/db/models.py index ac84d54c..943632ca 100644 --- a/otterdog/webapp/db/models.py +++ b/otterdog/webapp/db/models.py @@ -109,3 +109,9 @@ class PullRequestModel(Model): ), ], } + + +class StatisticsModel(Model): + project_name: str = Field(primary_field=True) + github_id: str = Field(index=True) + num_repos: int diff --git a/otterdog/webapp/db/service.py b/otterdog/webapp/db/service.py index cbb01578..e1d8396a 100644 --- a/otterdog/webapp/db/service.py +++ b/otterdog/webapp/db/service.py @@ -29,6 +29,7 @@ InstallationStatus, PullRequestModel, PullRequestStatus, + StatisticsModel, TaskModel, TaskStatus, ) @@ -400,3 +401,23 @@ async def get_merged_pull_requests_paged(params: dict[str, str]) -> tuple[list[P ), await mongo.odm.count(PullRequestModel, *queries), ) + + +async def save_statistics(model: StatisticsModel) -> None: + await mongo.odm.save(model) + + +async def get_statistics() -> tuple[int, int]: + pipeline = [ + { + "$group": { + "_id": None, + "num_projects": {"$sum": 1}, + "num_repos": {"$sum": "$num_repos"}, + }, + } + ] + + collection = mongo.odm.get_collection(StatisticsModel) + stats = await collection.aggregate(pipeline).next() + return stats["num_projects"], stats["num_repos"] diff --git a/otterdog/webapp/home/routes.py b/otterdog/webapp/home/routes.py index 7057326f..782b0c57 100644 --- a/otterdog/webapp/home/routes.py +++ b/otterdog/webapp/home/routes.py @@ -20,6 +20,7 @@ get_merged_pull_requests_count, get_open_or_incomplete_pull_requests, get_open_or_incomplete_pull_requests_count, + get_statistics, get_tasks, ) from otterdog.webapp.tasks.fetch_all_pull_requests import FetchAllPullRequestsTask @@ -38,12 +39,14 @@ async def index(): installations = await get_installations() configurations = await get_configurations() configurations_by_key = associate_by_key(configurations, lambda x: x.github_id) + statistics = await get_statistics() return await render_home_template( "index.html", open_pull_request_count=await get_open_or_incomplete_pull_requests_count(), merged_pull_request_count=await get_merged_pull_requests_count(), installations=installations, configurations=configurations_by_key, + total_repository_count=statistics[1], ) diff --git a/otterdog/webapp/tasks/__init__.py b/otterdog/webapp/tasks/__init__.py index e6124968..178fbbbe 100644 --- a/otterdog/webapp/tasks/__init__.py +++ b/otterdog/webapp/tasks/__init__.py @@ -6,9 +6,9 @@ # SPDX-License-Identifier: EPL-2.0 # ******************************************************************************* +import contextlib from abc import ABC, abstractmethod from collections.abc import AsyncIterator -from contextlib import asynccontextmanager from functools import cached_property from logging import Logger, getLogger from typing import Generic, Protocol, TypeVar @@ -103,7 +103,7 @@ async def rest_api(self) -> RestApi: # Ignore pycharm warning: # https://youtrack.jetbrains.com/issue/PY-66517/False-unexpected-argument-with-asynccontextmanager-defined-as-a-method - @asynccontextmanager + @contextlib.asynccontextmanager async def get_organization_config( self, initialize_template: bool = True, diff --git a/otterdog/webapp/tasks/fetch_config.py b/otterdog/webapp/tasks/fetch_config.py index 20cdf0fe..31c0e73a 100644 --- a/otterdog/webapp/tasks/fetch_config.py +++ b/otterdog/webapp/tasks/fetch_config.py @@ -8,9 +8,10 @@ from dataclasses import dataclass +from otterdog.models.github_organization import GitHubOrganization from otterdog.utils import jsonnet_evaluate_file -from otterdog.webapp.db.models import ConfigurationModel, TaskModel -from otterdog.webapp.db.service import save_config +from otterdog.webapp.db.models import ConfigurationModel, StatisticsModel, TaskModel +from otterdog.webapp.db.service import save_config, save_statistics from otterdog.webapp.tasks import InstallationBasedTask, Task from otterdog.webapp.utils import fetch_config_from_github @@ -48,6 +49,7 @@ async def _execute(self) -> None: config_file, ) + # save configuration config_data = jsonnet_evaluate_file(config_file) config = ConfigurationModel( # type: ignore github_id=self.org_id, @@ -57,5 +59,14 @@ async def _execute(self) -> None: ) await save_config(config) + # save statistics + github_organization = GitHubOrganization.from_model_data(config_data) + statistics = StatisticsModel( # type: ignore + project_name=org_config.name, + github_id=self.org_id, + num_repos=len(github_organization.repositories), + ) + await save_statistics(statistics) + def __repr__(self) -> str: return f"FetchConfigTask(repo={self.org_id}/{self.repo_name})" diff --git a/otterdog/webapp/templates/home/index.html b/otterdog/webapp/templates/home/index.html index 6f13c2ba..99a2748c 100644 --- a/otterdog/webapp/templates/home/index.html +++ b/otterdog/webapp/templates/home/index.html @@ -46,7 +46,7 @@
Open Pull Requests
@@ -74,7 +74,7 @@Total Repository Count
+