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

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

3# SPDX-License-Identifier: GPL-3.0-or-later 

4 

5from __future__ import annotations 

6 

7from datetime import timedelta 

8from typing import TYPE_CHECKING, ClassVar 

9 

10from django.db.models import Q, Sum 

11from django.utils import timezone 

12from django.utils.translation import gettext_lazy 

13 

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 

18 

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 

21 

22 from django.db.models import QuerySet 

23 

24 from weblate.trans.models import Component 

25 

26 

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" 

34 

35 def get_cutoff(self) -> datetime: 

36 age = self.instance.configuration["age"] 

37 return timezone.now() - timedelta(days=age) 

38 

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() 

45 

46 

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.") 

51 

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 ) 

59 

60 

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 

66 

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 )