Skip to content

Commit ad10751

Browse files
committed
Simplify get_tags_from_labels
use the same parameter names in every service
1 parent 3844120 commit ad10751

9 files changed

Lines changed: 189 additions & 35 deletions

File tree

bugwarrior/docs/other-services/api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,5 @@ v2.0
2626
define ``KEYRING_SERVICE`` instead.
2727
- Added ``ServiceConfig.KEYRING_SERVICE`` as a format string for generating the
2828
keyring service identifier from service configuration fields.
29+
- Added ``Issue.render_tags_from_labels(labels)`` for rendering taskwarrior tags
30+
from service labels with ``label_template``.

bugwarrior/docs/services/pagure.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ Import Labels as Tags
7070

7171
The Pagure issue tracker allows you to attach tags to issues; to
7272
use those pagure tags as taskwarrior tags, you can use the
73-
``import_tags`` option:
73+
``import_labels_as_tags`` option:
7474

7575
.. config::
7676
:fragment: pagure
7777

78-
pagure.import_tags = True
78+
pagure.import_labels_as_tags = True
7979

8080
Also, if you would like to control how these taskwarrior tags are created, you
8181
can specify a template used for converting the Pagure tag into a Taskwarrior
@@ -88,7 +88,7 @@ add the following configuration option:
8888
.. config::
8989
:fragment: pagure
9090

91-
pagure.tag_template = pagure_{{label}}
91+
pagure.label_template = pagure_{{label}}
9292

9393
In addition to the context variable ``{{label}}``, you also have access
9494
to all fields on the Taskwarrior task if needed.

bugwarrior/docs/services/youtrack.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ to tasks by default. To disable this behavior, set:
110110
.. config::
111111
:fragment: youtrack
112112

113-
youtrack.import_tags = False
113+
youtrack.import_labels_as_tags = False
114114

115115
If you would like to control how these tags are formatted, you can
116116
specify a template used for converting the YouTrack tag into a Taskwarrior
@@ -123,9 +123,9 @@ add the following configuration option:
123123
.. config::
124124
:fragment: youtrack
125125

126-
youtrack.tag_template = yt_{{tag|lower}}
126+
youtrack.label_template = yt_{{label|lower}}
127127

128-
In addition to the context variable ``{{tag}}``, you also have access
128+
In addition to the context variable ``{{label}}``, you also have access
129129
to all fields on the Taskwarrior task if needed.
130130

131131
.. note::

bugwarrior/services/__init__.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,30 +134,46 @@ def get_default_description(self) -> str:
134134
"""
135135
raise NotImplementedError()
136136

137+
def render_tags_from_labels(self, labels: list[str]) -> list[str]:
138+
"""Transform labels into suitable taskwarrior tags using the label template."""
139+
140+
return [
141+
Template(self.config.label_template).render(
142+
{**self.record, "label": re.sub(r'[^a-zA-Z0-9]', '_', label)}
143+
)
144+
for label in labels
145+
]
146+
137147
def get_tags_from_labels(
138148
self,
139149
labels: list[str],
140150
toggle_option: str = 'import_labels_as_tags',
141151
template_option: str = 'label_template',
142152
template_variable: str = 'label',
143153
) -> list[str]:
144-
"""Transform labels into suitable taskwarrior tags, respecting configuration options.
145-
146-
:param `labels`: Returned from the service.
147-
:param `toggle_option`: Option which, if false, would not import labels as tags.
148-
:param `template_option`: Configuration to use as the
149-
:ref:`field template<common_configuration:Field Templates>` for each label.
150-
:param `template_variable`: Name to use in the
151-
:ref:`field template<common_configuration:Field Templates>` context to refer to the
152-
label.
153-
"""
154-
tags: list[str] = []
154+
"""Transform labels into suitable taskwarrior tags, respecting configuration options."""
155+
using_deprecated_parameters = (
156+
toggle_option != 'import_labels_as_tags'
157+
or template_option != 'label_template'
158+
or template_variable != 'label'
159+
)
160+
if using_deprecated_parameters:
161+
log.warning(
162+
"Deprecation Warning: Issue.get_tags_from_labels's toggle_option, "
163+
"template_option, and template_variable parameters are deprecated and "
164+
"will be removed in a future API version."
165+
)
155166

156167
if not getattr(self.config, toggle_option):
157-
return tags
168+
return []
169+
170+
if not using_deprecated_parameters:
171+
return self.render_tags_from_labels(labels)
158172

173+
# deprecated path, to be removed once we remove the deprecated parameters.
159174
context = self.record.copy()
160175
label_template = Template(getattr(self.config, template_option))
176+
tags = []
161177

162178
for label in labels:
163179
normalized_label = re.sub(r'[^a-zA-Z0-9]', '_', label)

bugwarrior/services/jira.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,10 @@ def get_tags(self) -> list[str]:
236236
label_tags = self.get_tags_from_labels(labels)
237237

238238
sprints = [sprint['name'] for sprint in self.__get_sprints()]
239-
sprint_tags = self.get_tags_from_labels(
240-
sprints, toggle_option='import_sprints_as_tags'
239+
sprint_tags = (
240+
self.render_tags_from_labels(sprints)
241+
if self.config.import_sprints_as_tags
242+
else []
241243
)
242244

243245
return label_tags + sprint_tags

bugwarrior/services/pagure.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import typing
55
from typing import Any
66

7-
from pydantic import model_validator
7+
from pydantic import AliasChoices, Field, model_validator
88
import requests
99

1010
from bugwarrior import config
@@ -25,8 +25,25 @@ class PagureConfig(config.ServiceConfig):
2525
# optional
2626
include_repos: config.ConfigList = []
2727
exclude_repos: config.ConfigList = []
28-
import_tags: bool = False
29-
tag_template: str = '{{label}}'
28+
import_labels_as_tags: bool = Field(
29+
False, validation_alias=AliasChoices('import_labels_as_tags', 'import_tags')
30+
)
31+
label_template: str = Field(
32+
'{{label}}', validation_alias=AliasChoices('label_template', 'tag_template')
33+
)
34+
35+
@model_validator(mode='before')
36+
@classmethod
37+
def deprecate_legacy_tag_options(cls, values: Any) -> Any:
38+
if not isinstance(values, dict):
39+
return values
40+
41+
if 'import_tags' in values:
42+
log.warning('import_tags is deprecated in favor of import_labels_as_tags')
43+
if 'tag_template' in values:
44+
log.warning('tag_template is deprecated in favor of label_template')
45+
46+
return values
3047

3148
@model_validator(mode='after')
3249
def require_tag_or_repo(self) -> "PagureConfig":
@@ -75,11 +92,7 @@ def to_taskwarrior(self) -> dict[str, Any]:
7592
}
7693

7794
def get_tags(self) -> list[str]:
78-
return self.get_tags_from_labels(
79-
self.record.get('tags', []),
80-
toggle_option='import_tags',
81-
template_option='tag_template',
82-
)
95+
return self.get_tags_from_labels(self.record.get('tags', []))
8396

8497
def get_default_description(self) -> str:
8598
return self.build_default_description(

bugwarrior/services/youtrack.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
import typing
44
from typing import Any
55

6-
from pydantic import computed_field
6+
from pydantic import (
7+
AliasChoices,
8+
Field,
9+
computed_field,
10+
field_validator,
11+
model_validator,
12+
)
713
import requests
814
import urllib3
915

@@ -27,12 +33,42 @@ class YoutrackConfig(config.ServiceConfig):
2733
incloud_instance: bool = False
2834
query: str = 'for:me #Unresolved'
2935
query_limit: int = 100
30-
import_tags: bool = True
31-
tag_template: str = '{{tag|lower}}'
36+
import_labels_as_tags: bool = Field(
37+
True, validation_alias=AliasChoices('import_labels_as_tags', 'import_tags')
38+
)
39+
label_template: str = Field(
40+
'{{label|lower}}',
41+
validation_alias=AliasChoices('label_template', 'tag_template'),
42+
)
3243

3344
only_if_assigned: config.UnsupportedOption[str] = ''
3445
also_unassigned: config.UnsupportedOption[bool] = False
3546

47+
@model_validator(mode='before')
48+
@classmethod
49+
def deprecate_legacy_tag_options(cls, values: Any) -> Any:
50+
if not isinstance(values, dict):
51+
return values
52+
53+
if 'import_tags' in values:
54+
log.warning('import_tags is deprecated in favor of import_labels_as_tags')
55+
if 'tag_template' in values:
56+
log.warning('tag_template is deprecated in favor of label_template')
57+
58+
template = values.get('label_template', values.get('tag_template'))
59+
if isinstance(template, str) and 'tag' in template:
60+
log.warning(
61+
"The 'tag' variable in YouTrack label templates is deprecated "
62+
"in favor of 'label'."
63+
)
64+
65+
return values
66+
67+
@field_validator('label_template', mode='after')
68+
@classmethod
69+
def migrate_legacy_tag_template(cls, value: str) -> str:
70+
return value.replace('tag', 'label')
71+
3672
@computed_field
3773
@property
3874
def base_url(self) -> str:
@@ -103,10 +139,7 @@ def get_default_description(self) -> str:
103139

104140
def get_tags(self) -> list[str]:
105141
return self.get_tags_from_labels(
106-
[tag['name'] for tag in self.record.get('tags', [])],
107-
toggle_option='import_tags',
108-
template_option='tag_template',
109-
template_variable='tag',
142+
[tag['name'] for tag in self.record.get('tags', [])]
110143
)
111144

112145

tests/test_pagure.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from bugwarrior.collect import TaskConstructor
2+
from bugwarrior.config import schema
3+
from bugwarrior.services.pagure import PagureIssue, PagureService
4+
5+
from .base import ServiceTest
6+
7+
8+
class TestPagureIssue(ServiceTest):
9+
arbitrary_issue = {
10+
'html_url': 'https://pagure.io/repo/issue/1',
11+
'repo': 'repo',
12+
'title': 'Hello World',
13+
'id': 1,
14+
'date_created': '0',
15+
'tags': ['Bug', 'Needs Work'],
16+
}
17+
arbitrary_extra = {'type': 'issue', 'project': 'repo'}
18+
19+
def get_issue(self):
20+
service_config = PagureService.CONFIG_SCHEMA(
21+
service='pagure',
22+
target='pagure',
23+
base_url='https://pagure.io',
24+
repo='repo',
25+
import_tags=True,
26+
tag_template='pg_{{label}}',
27+
)
28+
main_config = schema.MainSectionConfig(
29+
targets=['pagure'], annotation_length=100, description_length=100
30+
)
31+
return PagureIssue(
32+
self.arbitrary_issue, service_config, main_config, self.arbitrary_extra
33+
)
34+
35+
def test_get_tags_from_labels_uses_legacy_tag_options(self):
36+
issue = self.get_issue()
37+
38+
self.assertEqual(issue.get_tags(), ['pg_Bug', 'pg_Needs_Work'])
39+
self.assertIn(
40+
'import_tags is deprecated in favor of import_labels_as_tags',
41+
self.caplog.text,
42+
)
43+
self.assertIn(
44+
'tag_template is deprecated in favor of label_template', self.caplog.text
45+
)
46+
47+
def test_refine_record_does_not_apply_legacy_tag_template_as_field_template(self):
48+
issue = self.get_issue()
49+
50+
self.assertEqual(issue.config.templates, {})
51+
self.assertEqual(
52+
TaskConstructor(issue).get_taskwarrior_record()['tags'],
53+
['pg_Bug', 'pg_Needs_Work'],
54+
)

tests/test_youtrak.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,40 @@ def setUp(self):
4949
super().setUp()
5050
self.service = self.get_mock_service(YoutrackService)
5151

52+
def test_get_tags_from_labels_uses_legacy_tag_options(self):
53+
service = self.get_mock_service(
54+
YoutrackService,
55+
config_overrides={'import_tags': True, 'tag_template': 'yt_{{tag|lower}}'},
56+
)
57+
issue = service.get_issue_for_record(self.arbitrary_issue, self.arbitrary_extra)
58+
59+
self.assertEqual(service.config.label_template, 'yt_{{label|lower}}')
60+
self.assertEqual(issue.get_tags(), ['yt_bug', 'yt_new_feature'])
61+
self.assertIn(
62+
'import_tags is deprecated in favor of import_labels_as_tags',
63+
self.caplog.text,
64+
)
65+
self.assertIn(
66+
'tag_template is deprecated in favor of label_template', self.caplog.text
67+
)
68+
self.assertIn(
69+
"The 'tag' variable in YouTrack label templates is deprecated in favor of 'label'.",
70+
self.caplog.text,
71+
)
72+
73+
def test_refine_record_does_not_apply_legacy_tag_template_as_field_template(self):
74+
service = self.get_mock_service(
75+
YoutrackService,
76+
config_overrides={'import_tags': True, 'tag_template': 'yt_{{tag|lower}}'},
77+
)
78+
issue = service.get_issue_for_record(self.arbitrary_issue, self.arbitrary_extra)
79+
80+
self.assertEqual(service.config.templates, {})
81+
self.assertEqual(
82+
TaskConstructor(issue).get_taskwarrior_record()['tags'],
83+
['yt_bug', 'yt_new_feature'],
84+
)
85+
5286
def test_to_taskwarrior(self):
5387
self.service.import_tags = True
5488
issue = self.service.get_issue_for_record(

0 commit comments

Comments
 (0)