pytaskwarrior 2.0.5__tar.gz → 2.0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/PKG-INFO +1 -1
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/pyproject.toml +1 -1
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/PKG-INFO +1 -1
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/SOURCES.txt +2 -1
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/annotation_dto.py +2 -2
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/task_dto.py +13 -5
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/enums.py +20 -28
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/main.py +8 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/registry/uda_registry.py +8 -0
- pytaskwarrior-2.0.7/tests/test_main_get_udas.py +22 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/LICENSE +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/PYPI_README.md +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/README.md +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/setup.cfg +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/__init__.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/dependency_links.txt +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/requires.txt +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/pytaskwarrior.egg-info/top_level.txt +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/__init__.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/adapters/__init__.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/adapters/taskwarrior_adapter.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/config/config_store.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/config/uda_parser.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/__init__.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/context_dto.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/task_id.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/dto/uda_dto.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/exceptions.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/py.typed +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/registry/__init__.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/services/__init__.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/services/context_service.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/services/uda_service.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/utils/__init__.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/utils/conversions.py +0 -0
- {pytaskwarrior-2.0.5 → pytaskwarrior-2.0.7}/src/taskwarrior/utils/dto_converter.py +0 -0
|
@@ -30,4 +30,5 @@ src/taskwarrior/services/context_service.py
|
|
|
30
30
|
src/taskwarrior/services/uda_service.py
|
|
31
31
|
src/taskwarrior/utils/__init__.py
|
|
32
32
|
src/taskwarrior/utils/conversions.py
|
|
33
|
-
src/taskwarrior/utils/dto_converter.py
|
|
33
|
+
src/taskwarrior/utils/dto_converter.py
|
|
34
|
+
tests/test_main_get_udas.py
|
|
@@ -37,7 +37,7 @@ class AnnotationDTO(BaseModel):
|
|
|
37
37
|
|
|
38
38
|
@field_validator("entry", mode="before")
|
|
39
39
|
@classmethod
|
|
40
|
-
def parse_entry_date(cls, value: str | datetime
|
|
40
|
+
def parse_entry_date(cls, value: str | datetime) -> datetime:
|
|
41
41
|
"""Parse the entry date from TaskWarrior format.
|
|
42
42
|
|
|
43
43
|
Args:
|
|
@@ -48,4 +48,4 @@ class AnnotationDTO(BaseModel):
|
|
|
48
48
|
"""
|
|
49
49
|
if isinstance(value, datetime):
|
|
50
50
|
return value
|
|
51
|
-
return parse_taskwarrior_date(value
|
|
51
|
+
return parse_taskwarrior_date(value)
|
|
@@ -82,8 +82,14 @@ class TaskInputDTO(BaseModel):
|
|
|
82
82
|
default_factory=list, description="List of UUIDs of tasks this task depends on"
|
|
83
83
|
)
|
|
84
84
|
parent: UUID | None = Field(default=None, description="UUID of the template task")
|
|
85
|
-
recur: RecurrencePeriod | None = Field(
|
|
86
|
-
default=None,
|
|
85
|
+
recur: RecurrencePeriod | str | None = Field(
|
|
86
|
+
default=None,
|
|
87
|
+
description=(
|
|
88
|
+
"Recurrence period. "
|
|
89
|
+
"Use standard values: 'daily', 'weekly', 'monthly', 'yearly', 'quarterly', 'semiannually', 'hourly', 'minutely', 'secondly'. "
|
|
90
|
+
"OR use custom TaskWarrior expressions like '2weeks', '3days', 'every 10 days', '2months'. "
|
|
91
|
+
"TaskWarrior will validate the expression."
|
|
92
|
+
)
|
|
87
93
|
)
|
|
88
94
|
scheduled: str | None = Field(
|
|
89
95
|
default=None,
|
|
@@ -307,7 +313,7 @@ class TaskOutputDTO(BaseModel):
|
|
|
307
313
|
mode="before",
|
|
308
314
|
)
|
|
309
315
|
@classmethod
|
|
310
|
-
def parse_datetime_field(cls, value: str | datetime | None) -> datetime:
|
|
316
|
+
def parse_datetime_field(cls, value: str | datetime | None) -> datetime | None:
|
|
311
317
|
"""Parse datetime fields from TaskWarrior format.
|
|
312
318
|
|
|
313
319
|
Handles both TaskWarrior's compact format (20260101T193139Z)
|
|
@@ -317,11 +323,13 @@ class TaskOutputDTO(BaseModel):
|
|
|
317
323
|
value: The datetime value to parse, either as string or datetime.
|
|
318
324
|
|
|
319
325
|
Returns:
|
|
320
|
-
A datetime object with timezone info.
|
|
326
|
+
A datetime object with timezone info, or None if value is None.
|
|
321
327
|
"""
|
|
328
|
+
if value is None:
|
|
329
|
+
return None
|
|
322
330
|
if isinstance(value, datetime):
|
|
323
331
|
return value
|
|
324
|
-
return parse_taskwarrior_date(value
|
|
332
|
+
return parse_taskwarrior_date(value)
|
|
325
333
|
|
|
326
334
|
def get_uda(self, name: str, default: Any = None) -> Any:
|
|
327
335
|
"""Get a User Defined Attribute value by name.
|
|
@@ -57,34 +57,26 @@ class Priority(str, Enum):
|
|
|
57
57
|
class RecurrencePeriod(str, Enum):
|
|
58
58
|
"""Supported recurrence periods for recurring tasks.
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Example:
|
|
75
|
-
>>>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
... )
|
|
81
|
-
|
|
82
|
-
Note:
|
|
83
|
-
For custom periods not listed here (e.g. ``"2weeks"``, ``"10days"``,
|
|
84
|
-
``"6months"``), pass a plain string directly to the ``recur`` field —
|
|
85
|
-
TaskWarrior accepts any valid recurrence expression:
|
|
86
|
-
|
|
87
|
-
>>> custom_task = TaskInputDTO(description="Bi-weekly review", recur="2weeks")
|
|
60
|
+
TaskWarrior supports both standard keywords and custom duration expressions.
|
|
61
|
+
|
|
62
|
+
Standard Keywords (Recommended):
|
|
63
|
+
- DAILY, WEEKLY, MONTHLY, YEARLY
|
|
64
|
+
- QUARTERLY, SEMIANNUALLY
|
|
65
|
+
- HOURLY, MINUTELY, SECONDLY
|
|
66
|
+
|
|
67
|
+
Custom Expressions (Also Valid):
|
|
68
|
+
- "2weeks" (Every two weeks)
|
|
69
|
+
- "3days" (Every three days)
|
|
70
|
+
- "every 10 days"
|
|
71
|
+
- "2months"
|
|
72
|
+
- "6months"
|
|
73
|
+
|
|
74
|
+
Example (Standard):
|
|
75
|
+
>>> task = TaskInputDTO(description="Daily standup", recur=RecurrencePeriod.DAILY)
|
|
76
|
+
|
|
77
|
+
Example (Custom):
|
|
78
|
+
>>> task = TaskInputDTO(description="Bi-weekly report", recur="2weeks")
|
|
79
|
+
# Note: Passing a string directly bypasses the Enum but is fully supported by TaskWarrior.
|
|
88
80
|
"""
|
|
89
81
|
|
|
90
82
|
DAILY = "daily"
|
|
@@ -556,6 +556,14 @@ class TaskWarrior:
|
|
|
556
556
|
"""
|
|
557
557
|
return self.uda_service.registry.get_uda(name)
|
|
558
558
|
|
|
559
|
+
def get_udas(self) -> list[UdaConfig]:
|
|
560
|
+
"""Get full UDA definitions.
|
|
561
|
+
|
|
562
|
+
Returns:
|
|
563
|
+
List of UdaConfig objects for all defined UDAs.
|
|
564
|
+
"""
|
|
565
|
+
return self.uda_service.registry.get_udas()
|
|
566
|
+
|
|
559
567
|
def define_uda(self, uda: UdaConfig) -> None:
|
|
560
568
|
"""Define a new UDA via the TaskWarrior facade.
|
|
561
569
|
|
|
@@ -68,3 +68,11 @@ class UdaRegistry:
|
|
|
68
68
|
def is_uda_field(self, field_name: str) -> bool:
|
|
69
69
|
"""Check if a field name corresponds to a registered UDA."""
|
|
70
70
|
return field_name in self._udas
|
|
71
|
+
|
|
72
|
+
def get_udas(self) -> list[UdaConfig]:
|
|
73
|
+
"""Return all registered UdaConfig objects as a list.
|
|
74
|
+
|
|
75
|
+
This provides a direct way to retrieve the full UdaConfig objects
|
|
76
|
+
currently stored in the registry.
|
|
77
|
+
"""
|
|
78
|
+
return list(self._udas.values())
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
from taskwarrior import TaskWarrior
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_get_udas_empty(monkeypatch):
|
|
6
|
+
tw = TaskWarrior(task_cmd="task", taskrc_file="/tmp/nonexistent", data_location=None)
|
|
7
|
+
monkeypatch.setattr(tw.uda_service.registry, "get_udas", lambda: [])
|
|
8
|
+
assert tw.get_udas() == []
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_get_udas_multiple(monkeypatch):
|
|
12
|
+
tw = TaskWarrior(task_cmd="task", taskrc_file="/tmp/nonexistent", data_location=None)
|
|
13
|
+
|
|
14
|
+
class DummyUda:
|
|
15
|
+
def __init__(self, name):
|
|
16
|
+
self.name = name
|
|
17
|
+
|
|
18
|
+
names = {"sev", "est"}
|
|
19
|
+
monkeypatch.setattr(tw.uda_service.registry, "get_udas", lambda: [DummyUda("sev"), DummyUda("est")])
|
|
20
|
+
|
|
21
|
+
udas = tw.get_udas()
|
|
22
|
+
assert {u.name for u in udas} == names
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|