Coverage for app/venv/lib/python3.14/site-packages/weblate/addons/removal.py: 70%
39 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
5from __future__ import annotations
7from datetime import timedelta
8from typing import TYPE_CHECKING, ClassVar
10from django.db.models import Q, Sum
11from django.utils import timezone
12from django.utils.translation import gettext_lazy
14from weblate.addons.base import BaseAddon
15from weblate.addons.events import AddonEvent
16from weblate.addons.forms import RemoveForm, RemoveSuggestionForm
17from weblate.trans.models import Comment, Suggestion
19if TYPE_CHECKING: 19 ↛ 20line 19 didn't jump to line 20 because the condition on line 19 was never true
20 from datetime import datetime
22 from django.db.models import QuerySet
24 from weblate.trans.models import Component
27class RemovalAddon(BaseAddon):
28 project_scope = True
29 events: ClassVar[set[AddonEvent]] = {
30 AddonEvent.EVENT_DAILY,
31 }
32 settings_form = RemoveForm
33 icon = "delete.svg"
35 def get_cutoff(self) -> datetime:
36 age = self.instance.configuration["age"]
37 return timezone.now() - timedelta(days=age)
39 def delete_older(
40 self, objects: QuerySet[Comment] | QuerySet[Suggestion], component: Component
41 ) -> None:
42 count = objects.filter(timestamp__lt=self.get_cutoff()).delete()[0]
43 if count:
44 component.invalidate_cache()
47class RemoveComments(RemovalAddon):
48 name = "weblate.removal.comments"
49 verbose = gettext_lazy("Stale comment removal")
50 description = gettext_lazy("Set a timeframe for removal of comments.")
52 def daily(self, component: Component, activity_log_id: int | None = None) -> None:
53 self.delete_older(
54 Comment.objects.filter(
55 unit__translation__component__project=component.project
56 ),
57 component,
58 )
61class RemoveSuggestions(RemovalAddon):
62 name = "weblate.removal.suggestions"
63 verbose = gettext_lazy("Stale suggestion removal")
64 description = gettext_lazy("Set a timeframe for removal of suggestions.")
65 settings_form = RemoveSuggestionForm
67 def daily(self, component: Component, activity_log_id: int | None = None) -> None:
68 self.delete_older(
69 Suggestion.objects.filter(
70 unit__translation__component__project=component.project
71 )
72 .annotate(Sum("vote__value"))
73 .filter(
74 Q(vote__value__sum__lte=self.instance.configuration.get("votes", 0))
75 | Q(vote__value__sum=None)
76 ),
77 component,
78 )