feat(workspaces): add statistics and metrics#20784
Conversation
|
@amCap1712 I'd like to hear your opinion, especially on linking |
There was a problem hiding this comment.
💡 Codex Review
https://github.com/WeblateOrg/weblate/blob/e3e9a36b63c5e5879f229f91e88dd0e5a897b863/templates/workspace.html#L119-L120
Restrict workspace statistics to visible projects
When a visitor can access any public/protected project in a workspace but not another private project, detail() permits the workspace page based on request.user.allowed_projects while the information tab renders the unfiltered workspace.stats. WorkspaceStats.get_child_objects() aggregates workspace.projects without the request user's access filter, so this newly exposed tab reveals private projects' aggregate string/word/check/progress data (and historical contributor totals) to users who cannot view those projects; the existing project list correctly omits them.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Adds workspace-scoped aggregate translation statistics and historical metrics, integrating workspaces into the existing stats/metrics infrastructure while optimizing the management listing to avoid per-workspace queries.
Changes:
- Introduces
WorkspaceStatsaggregation and schedules workspace stats refreshes when projects move or component topology changes. - Adds
Workspace.metric_id(integer sequence) to support integer-indexed workspace metrics, plus workspace metric collection and lifecycle hooks. - Updates workspace UI and management pages to display workspace info/metrics and to support sortable listings with preserved search parameters.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| weblate/workspaces/tests.py | Adds tests for metric_id behavior and workspace stats aggregation/refresh triggers. |
| weblate/workspaces/models.py | Adds metric_id, WorkspaceStats wiring, and billing_or_none helper. |
| weblate/workspaces/migrations/0004_workspace_metric_id.py | Creates the metric ID sequence and adds the metric_id field. |
| weblate/wladmin/views.py | Batches workspace listing annotations/prefetching and prepares language counts for uncached workspace stats. |
| weblate/wladmin/tests.py | Extends management UI tests for stats sorting, search preservation, and query batching. |
| weblate/utils/tasks.py | Adds update_workspace_stats task and ensures topology updates include workspace aggregates. |
| weblate/utils/stats.py | Adds cache helper has_cached_data, workspace aggregation class, and workspace update ordering. |
| weblate/trans/templatetags/translations.py | Extends show_info context to accept a workspace. |
| weblate/trans/models/project.py | Schedules workspace stats refresh when a project’s workspace changes. |
| weblate/trans/mixins.py | Updates cache key typing protocol to allow UUID IDs. |
| weblate/templates/workspace.html | Adds “Information” tab and renders workspace info + metrics. |
| weblate/templates/snippets/list-objects.html | Reuses listing table for workspaces, adds columns, and preserves query_string across sorts. |
| weblate/templates/snippets/info.html | Adds workspace-specific quick metrics blocks and disables contributor link for workspace context. |
| weblate/templates/manage/workspaces.html | Switches workspace management page to the shared list-objects snippet. |
| weblate/metrics/tests.py | Adds workspace metric collection and deletion lifecycle tests. |
| weblate/metrics/templatetags/metrics.py | Enables ` |
| weblate/metrics/tasks.py | Extends periodic metrics collection to include workspaces. |
| weblate/metrics/models.py | Adds workspace metric scope, collection, and create/delete hooks. |
| weblate/billing/views.py | Triggers workspace stats refresh when billing merge moves projects between workspaces. |
| weblate/billing/models.py | Triggers workspace stats refresh when billing changes a project’s workspace. |
| docs/changes.rst | Adds changelog entry announcing workspace statistics/metrics. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @property | ||
| def billing_or_none(self) -> Billing | None: | ||
| """Associated billing, or none for unbilled workspaces.""" | ||
| with suppress(ObjectDoesNotExist): |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f535dea499
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "components": Component.objects.filter( | ||
| project__workspace=workspace | ||
| ).count(), | ||
| "translations": Translation.objects.filter( | ||
| component__project__workspace=workspace | ||
| ).count(), |
There was a problem hiding this comment.
Include linked components in workspace metrics
For a workspace containing a project that consumes a component through ComponentLink, these filters only count components owned by a project in the workspace, excluding the linked component and its translations (and the analogous screenshots filter below). This disagrees with WorkspaceStats, whose project aggregates include shared components, so the new workspace Overview can show hosted strings from the linked component but zero/understated Components and Translations counts. Build these metrics from the workspace's effective project components, including component__links__workspace, as is done for category metrics.
Useful? React with 👍 / 👎.
| <div class="tab-pane" id="information"> | ||
| {% show_info workspace=workspace stats=workspace.stats metrics=workspace|metrics show_source=True %} |
There was a problem hiding this comment.
Restrict workspace statistics to visible projects
When a workspace contains both an accessible public/protected project and a private project, this renders the unfiltered workspace.stats and workspace metrics to a visitor who is admitted solely through the accessible project. The detail view deliberately filters the project listing through request.user.allowed_projects, but WorkspaceStats aggregates every workspace.projects entry; consequently the Overview exposes private-project string, translation, check, and contributor totals to users who cannot access that project. Compute the displayed stats from the permitted projects or require workspace-level access before rendering this tab.
Useful? React with 👍 / 👎.
| sort_by = self.request.GET.get("sort_by") | ||
| if sort_by and sort_by.lstrip("-") != "name": | ||
| queryset = prefetch_stats(queryset) | ||
| self.prepare_workspace_stats(queryset) |
There was a problem hiding this comment.
Treat reverse name sorting as stat-free
When an administrator clicks the Name header to request sort_by=-name, this guard skips the explicit prefetch, but get_paginator(..., stats=True) treats every value other than exactly name as requiring statistics and calls prefetch_stats across the entire workspace queryset before sorting. Reverse alphabetical sorting therefore performs unnecessary cache/stat work for every workspace, which can make this otherwise simple management operation slow on large installations. Normalize the leading - before deciding whether stats are needed.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
weblate/workspaces/models.py:310
- billing_or_none only suppresses ObjectDoesNotExist, but when the billing app is disabled the reverse accessor (Workspace.billing) does not exist and this property will raise AttributeError if accessed. It should be safe to call regardless of INSTALLED_APPS.
@property
def billing_or_none(self) -> Billing | None:
"""Associated billing, or none for unbilled workspaces."""
with suppress(ObjectDoesNotExist):
return self.billing
return None
| queryset = result["object_list"] | ||
| sort_by = self.request.GET.get("sort_by") | ||
| if sort_by and sort_by.lstrip("-") != "name": | ||
| queryset = prefetch_stats(queryset) | ||
| self.prepare_workspace_stats(queryset) | ||
| workspaces = get_paginator( | ||
| self.request, | ||
| queryset, | ||
| page_limit=50, | ||
| stats=True, | ||
| sort_by=sort_by, | ||
| ) |
| result["show_review_columns"] = ( | ||
| Project.objects.filter(workspace__in=queryset) | ||
| .filter(Q(source_review=True) | Q(translation_review=True)) | ||
| .exists() | ||
| ) |
Expose aggregate translation health and historical trends at workspace scope. Keep metric lookups integer-indexed and batch the management listing to avoid per-workspace queries.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
weblate/wladmin/views.py:1094
prepare_workspace_stats()can run the (potentially expensive) translation-language union query twice when sorting by non-name columns: it’s called once before pagination (lines 1117–1120) and again after pagination (line 1127). Becausemissingonly checkshas_cached_data, the second call repeats the work even thoughstats_languageswas already populated.
Consider skipping workspaces that already have stats_languages set so the second call becomes a no-op for those objects.
"""Batch language counts needed to initialize uncached workspace stats."""
missing = [
workspace for workspace in workspaces if not workspace.stats.has_cached_data
]
Expose aggregate translation health and historical trends at workspace scope. Keep metric lookups integer-indexed and batch the management listing to avoid per-workspace queries.