devopsdriver 0.1.39__tar.gz → 0.1.41__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.
Files changed (34) hide show
  1. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/PKG-INFO +5 -5
  2. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/README.md +1 -1
  3. devopsdriver-0.1.41/devopsdriver/__init__.py +11 -0
  4. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/azdo/__init__.py +1 -1
  5. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/azdo/timestamp.py +54 -1
  6. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/azdo/workitem/wiql.py +36 -1
  7. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver.egg-info/PKG-INFO +5 -5
  8. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver.egg-info/requires.txt +2 -2
  9. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/pyproject.toml +6 -3
  10. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_azure_timestamp.py +61 -0
  11. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_azure_workitem_wiql.py +15 -1
  12. devopsdriver-0.1.39/devopsdriver/__init__.py +0 -5
  13. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/LICENSE +0 -0
  14. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/azdo/clients.py +0 -0
  15. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/azdo/workitem/__init__.py +0 -0
  16. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/azdo/workitem/client.py +0 -0
  17. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/azdo/workitem/workitem.py +0 -0
  18. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/manage_settings.py +0 -0
  19. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/sendmail.py +0 -0
  20. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/settings.py +0 -0
  21. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/template.py +0 -0
  22. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver/templates/manage_settings.txt.mako +0 -0
  23. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver.egg-info/SOURCES.txt +0 -0
  24. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver.egg-info/dependency_links.txt +0 -0
  25. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver.egg-info/entry_points.txt +0 -0
  26. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/devopsdriver.egg-info/top_level.txt +0 -0
  27. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/setup.cfg +0 -0
  28. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_azure_clients.py +0 -0
  29. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_azure_workitem.py +0 -0
  30. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_azure_workitem_client.py +0 -0
  31. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_manage_settings.py +0 -0
  32. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_sendmail.py +0 -0
  33. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_settings.py +0 -0
  34. {devopsdriver-0.1.39 → devopsdriver-0.1.41}/tests/test_template.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devopsdriver
3
- Version: 0.1.39
3
+ Version: 0.1.41
4
4
  Summary: DevOps tools
5
5
  Author-email: Marc Page <marcallenpage@gmail.com>
6
6
  License: This is free and unencumbered software released into the public domain.
@@ -32,7 +32,7 @@ Project-URL: Homepage, https://github.com/marcpage/devops-driver
32
32
  Project-URL: Documentation, https://github.com/marcpage/devops-driver
33
33
  Project-URL: Repository, https://github.com/marcpage/devops-driver.git
34
34
  Project-URL: Issues, https://github.com/marcpage/devops-driver/issues
35
- Project-URL: Changelog, https://github.com/marcpage/devops-driver
35
+ Project-URL: Changelog, https://github.com/marcpage/devops-driver/releases
36
36
  Keywords: azure,devops,jira,confluence,email,pipelines,tools
37
37
  Classifier: Development Status :: 1 - Planning
38
38
  Classifier: Environment :: Console
@@ -47,8 +47,8 @@ Requires-Python: >=3.10
47
47
  Description-Content-Type: text/markdown
48
48
  License-File: LICENSE
49
49
  Requires-Dist: PyYAML==6.0.1
50
- Requires-Dist: keyring==25.0.0
51
- Requires-Dist: setuptools==69.0.2
50
+ Requires-Dist: keyring==25.1.0
51
+ Requires-Dist: setuptools==69.2.0
52
52
  Requires-Dist: azure-devops==7.1.0b4
53
53
  Requires-Dist: Mako==1.3.2
54
54
  Provides-Extra: dev
@@ -60,7 +60,7 @@ Requires-Dist: coverage>=7.4.4; extra == "test"
60
60
  Provides-Extra: doc
61
61
 
62
62
  ![status sheild](https://img.shields.io/static/v1?label=status&message=beta&color=blue&style=plastic)
63
- [![status sheild](https://img.shields.io/static/v1?label=released&message=v0.1.39&color=active&style=plastic)](https://pypi.org/project/devopsdriver/0.1.39/)
63
+ [![status sheild](https://img.shields.io/static/v1?label=released&message=v0.1.41&color=active&style=plastic)](https://pypi.org/project/devopsdriver/0.1.41/)
64
64
  [![GitHub](https://img.shields.io/github/license/marcpage/devops-driver?style=plastic)](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
65
65
  [![GitHub contributors](https://img.shields.io/github/contributors/marcpage/devops-driver?style=flat)](https://github.com/marcpage/devops-driver/graphs/contributors)
66
66
  [![PR's Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
@@ -1,5 +1,5 @@
1
1
  ![status sheild](https://img.shields.io/static/v1?label=status&message=beta&color=blue&style=plastic)
2
- [![status sheild](https://img.shields.io/static/v1?label=released&message=v0.1.39&color=active&style=plastic)](https://pypi.org/project/devopsdriver/0.1.39/)
2
+ [![status sheild](https://img.shields.io/static/v1?label=released&message=v0.1.41&color=active&style=plastic)](https://pypi.org/project/devopsdriver/0.1.41/)
3
3
  [![GitHub](https://img.shields.io/github/license/marcpage/devops-driver?style=plastic)](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
4
4
  [![GitHub contributors](https://img.shields.io/github/contributors/marcpage/devops-driver?style=flat)](https://github.com/marcpage/devops-driver/graphs/contributors)
5
5
  [![PR's Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
@@ -0,0 +1,11 @@
1
+ """ DevOps tools """
2
+
3
+ from .settings import Settings
4
+ from .sendmail import send_email
5
+ from .template import Template
6
+ from .azdo.clients import Azure
7
+
8
+
9
+ __version__ = "0.1.41"
10
+ __author__ = "Marc Page"
11
+ __credits__ = ""
@@ -6,6 +6,6 @@ from .timestamp import Timestamp
6
6
 
7
7
  from .workitem.workitem import WorkItem
8
8
  from .workitem.wiql import Wiql, Value, Field
9
- from .workitem.wiql import Ascending, Descending, And, Or
9
+ from .workitem.wiql import Ascending, Descending, And, Or, In, NotIn
10
10
  from .workitem.wiql import Equal, NotEqual, LessThanOrEqual, GreaterThanOrEqual
11
11
  from .workitem.wiql import IsEmpty, IsNotEmpty, LessThan, GreaterThan
@@ -4,9 +4,11 @@
4
4
  """ Tools that help when working with Azure """
5
5
 
6
6
 
7
- from datetime import datetime, timezone
7
+ from datetime import datetime, timezone, timedelta
8
+ from functools import total_ordering
8
9
 
9
10
 
11
+ @total_ordering
10
12
  class Timestamp:
11
13
  """An Azure timestamp"""
12
14
 
@@ -37,6 +39,11 @@ class Timestamp:
37
39
  except ValueError:
38
40
  return False
39
41
 
42
+ @staticmethod
43
+ def now():
44
+ """Returns a timestamp representing now"""
45
+ return Timestamp(datetime.now(tz=timezone.utc))
46
+
40
47
  def __init__(self, value: datetime | str | float | int):
41
48
  if isinstance(value, datetime):
42
49
  self.value = value
@@ -50,6 +57,39 @@ class Timestamp:
50
57
  def __str__(self) -> str:
51
58
  return self.to_string()
52
59
 
60
+ def __lt__(self, other) -> bool:
61
+ match Timestamp.__comparison_type(other):
62
+ case 1:
63
+ return self.value < other.value
64
+ case 2:
65
+ return self.value < other
66
+ case _:
67
+ return NotImplemented
68
+
69
+ def __eq__(self, other) -> bool:
70
+ match Timestamp.__comparison_type(other):
71
+ case 1:
72
+ return self.value == other.value
73
+ case 2:
74
+ return self.value == other
75
+ case _:
76
+ return NotImplemented
77
+
78
+ def __sub__(self, other):
79
+ match Timestamp.__comparison_type(other):
80
+ case 1:
81
+ return self.value - other.value
82
+ case 2 | 3:
83
+ return self.value - other
84
+ case _:
85
+ return NotImplemented
86
+
87
+ def __add__(self, other):
88
+ if Timestamp.__comparison_type(other) != 3:
89
+ return NotImplemented
90
+
91
+ return Timestamp(self.value + other)
92
+
53
93
  def to_string(self) -> str:
54
94
  """Returns the Azure formatted timestamp
55
95
 
@@ -78,3 +118,16 @@ class Timestamp:
78
118
  return result.replace(
79
119
  microsecond=int(fractional_seconds * Timestamp.US_PER_SEC)
80
120
  ).replace(tzinfo=timezone.utc)
121
+
122
+ @staticmethod
123
+ def __comparison_type(other) -> int:
124
+ if isinstance(other, timedelta):
125
+ return 3
126
+
127
+ if isinstance(other, datetime):
128
+ return 2
129
+
130
+ if hasattr(other, "value") and isinstance(other.value, datetime):
131
+ return 1
132
+
133
+ return 0
@@ -133,15 +133,50 @@ class Compare: # pylint: disable=too-few-public-methods
133
133
  field: Field | str,
134
134
  value: Value | str | date | datetime | int | float,
135
135
  operator: str,
136
+ value_is_computed=False,
136
137
  ):
137
138
  self.left = field if isinstance(field, Field) else Field(field)
138
- self.right = value if isinstance(value, Value) else Value(value)
139
+ self.right = (
140
+ value if value_is_computed or isinstance(value, Value) else Value(value)
141
+ )
139
142
  self.operator = operator
140
143
 
141
144
  def __str__(self) -> str:
142
145
  return f"{str(self.left)} {self.operator} {str(self.right)}"
143
146
 
144
147
 
148
+ class In(Compare): # pylint: disable=too-few-public-methods
149
+ """checks for field in a list of values"""
150
+
151
+ def __init__(
152
+ self,
153
+ field: Field | str,
154
+ *values: list[Value | str | date | datetime | int | float],
155
+ ):
156
+ super().__init__(
157
+ field,
158
+ f"({', '.join(str(v if isinstance(v, Value) else Value(v)) for v in values)})",
159
+ "IN",
160
+ value_is_computed=True,
161
+ )
162
+
163
+
164
+ class NotIn(Compare): # pylint: disable=too-few-public-methods
165
+ """checks for field in a list of values"""
166
+
167
+ def __init__(
168
+ self,
169
+ field: Field | str,
170
+ *values: list[Value | str | date | datetime | int | float],
171
+ ):
172
+ super().__init__(
173
+ field,
174
+ f"({', '.join(str(v if isinstance(v, Value) else Value(v)) for v in values)})",
175
+ "NOT IN",
176
+ value_is_computed=True,
177
+ )
178
+
179
+
145
180
  class Equal(Compare): # pylint: disable=too-few-public-methods
146
181
  """checks for equality"""
147
182
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devopsdriver
3
- Version: 0.1.39
3
+ Version: 0.1.41
4
4
  Summary: DevOps tools
5
5
  Author-email: Marc Page <marcallenpage@gmail.com>
6
6
  License: This is free and unencumbered software released into the public domain.
@@ -32,7 +32,7 @@ Project-URL: Homepage, https://github.com/marcpage/devops-driver
32
32
  Project-URL: Documentation, https://github.com/marcpage/devops-driver
33
33
  Project-URL: Repository, https://github.com/marcpage/devops-driver.git
34
34
  Project-URL: Issues, https://github.com/marcpage/devops-driver/issues
35
- Project-URL: Changelog, https://github.com/marcpage/devops-driver
35
+ Project-URL: Changelog, https://github.com/marcpage/devops-driver/releases
36
36
  Keywords: azure,devops,jira,confluence,email,pipelines,tools
37
37
  Classifier: Development Status :: 1 - Planning
38
38
  Classifier: Environment :: Console
@@ -47,8 +47,8 @@ Requires-Python: >=3.10
47
47
  Description-Content-Type: text/markdown
48
48
  License-File: LICENSE
49
49
  Requires-Dist: PyYAML==6.0.1
50
- Requires-Dist: keyring==25.0.0
51
- Requires-Dist: setuptools==69.0.2
50
+ Requires-Dist: keyring==25.1.0
51
+ Requires-Dist: setuptools==69.2.0
52
52
  Requires-Dist: azure-devops==7.1.0b4
53
53
  Requires-Dist: Mako==1.3.2
54
54
  Provides-Extra: dev
@@ -60,7 +60,7 @@ Requires-Dist: coverage>=7.4.4; extra == "test"
60
60
  Provides-Extra: doc
61
61
 
62
62
  ![status sheild](https://img.shields.io/static/v1?label=status&message=beta&color=blue&style=plastic)
63
- [![status sheild](https://img.shields.io/static/v1?label=released&message=v0.1.39&color=active&style=plastic)](https://pypi.org/project/devopsdriver/0.1.39/)
63
+ [![status sheild](https://img.shields.io/static/v1?label=released&message=v0.1.41&color=active&style=plastic)](https://pypi.org/project/devopsdriver/0.1.41/)
64
64
  [![GitHub](https://img.shields.io/github/license/marcpage/devops-driver?style=plastic)](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
65
65
  [![GitHub contributors](https://img.shields.io/github/contributors/marcpage/devops-driver?style=flat)](https://github.com/marcpage/devops-driver/graphs/contributors)
66
66
  [![PR's Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
@@ -1,6 +1,6 @@
1
1
  PyYAML==6.0.1
2
- keyring==25.0.0
3
- setuptools==69.0.2
2
+ keyring==25.1.0
3
+ setuptools==69.2.0
4
4
  azure-devops==7.1.0b4
5
5
  Mako==1.3.2
6
6
 
@@ -7,8 +7,8 @@ dynamic = ["version"]
7
7
  requires-python = ">= 3.10"
8
8
  dependencies = [
9
9
  "PyYAML==6.0.1",
10
- "keyring==25.0.0",
11
- "setuptools==69.0.2", # neded for azure-devops to use 7.1 API
10
+ "keyring==25.1.0",
11
+ "setuptools==69.2.0", # neded for azure-devops to use 7.1 API
12
12
  "azure-devops==7.1.0b4",
13
13
  "Mako==1.3.2",
14
14
  ]
@@ -34,6 +34,9 @@ settings = "devopsdriver.manage_settings:main"
34
34
  [tool.setuptools.package-data]
35
35
  "*" = ["*.mako"]
36
36
 
37
+ [tool.setuptools.packages.find]
38
+ include = ["devopsdriver*"]
39
+
37
40
  [project.optional-dependencies]
38
41
  dev = [
39
42
  "black>=24.3.0",
@@ -50,7 +53,7 @@ Homepage = "https://github.com/marcpage/devops-driver"
50
53
  Documentation = "https://github.com/marcpage/devops-driver"
51
54
  Repository = "https://github.com/marcpage/devops-driver.git"
52
55
  Issues = "https://github.com/marcpage/devops-driver/issues"
53
- Changelog = "https://github.com/marcpage/devops-driver"
56
+ Changelog = "https://github.com/marcpage/devops-driver/releases"
54
57
 
55
58
  [tool.setuptools.dynamic]
56
59
  version = {attr = "devopsdriver.__version__"}
@@ -2,6 +2,7 @@
2
2
 
3
3
  """ Test Azure Timestamp """
4
4
 
5
+ from datetime import datetime, timezone, timedelta
5
6
  from devopsdriver.azdo import Timestamp
6
7
 
7
8
  TEST_TIMESTAMPS = [
@@ -243,5 +244,65 @@ def test_basic() -> None:
243
244
  ), f"{timestamp} != {Timestamp(value_under_test.value).to_timestamp()}"
244
245
 
245
246
 
247
+ def test_comparison() -> None:
248
+ """test comparison operators"""
249
+ time1 = datetime.now(tz=timezone.utc)
250
+ time2 = time1 + timedelta(days=7)
251
+ assert time2 > time1
252
+ assert Timestamp(time2) > time1
253
+ assert Timestamp(time2) > Timestamp(time1)
254
+ assert time1 < time2
255
+ assert Timestamp(time1) < time2
256
+ assert Timestamp(time1) < Timestamp(time2)
257
+ assert time1 <= time1
258
+ assert Timestamp(time1) <= time1
259
+ assert Timestamp(time1) <= Timestamp(time1)
260
+ assert time2 >= time2
261
+ assert Timestamp(time2) >= time2
262
+ assert Timestamp(time2) >= Timestamp(time2)
263
+ assert Timestamp(time1) == time1
264
+ assert Timestamp(time2) == Timestamp(time2)
265
+ assert Timestamp(time1) != time2
266
+ assert Timestamp(time2) != Timestamp(time1)
267
+ assert Timestamp(time1) != 5
268
+
269
+ try:
270
+ assert Timestamp(time2) < 5
271
+
272
+ except TypeError as error:
273
+ assert "Timestamp" in str(error) and "int" in str(error), error
274
+
275
+
276
+ def test_now() -> None:
277
+ """Test the now() method"""
278
+ now1 = Timestamp.now()
279
+ now2 = Timestamp.now() + timedelta(milliseconds=50)
280
+ assert now2 > now1
281
+ assert (now2.value - now1.value).total_seconds() < 1
282
+
283
+
284
+ def test_math() -> None:
285
+ """test addition and subtraction"""
286
+ now1 = Timestamp.now()
287
+ now2 = now1 + timedelta(days=7)
288
+ assert (now2 - now1).days == 7
289
+ assert now2 - timedelta(days=7) == now1
290
+
291
+ try:
292
+ assert now1 - 5 is False
293
+
294
+ except TypeError as error:
295
+ assert "Timestamp" in str(error) and "int" in str(error), error
296
+
297
+ try:
298
+ assert now2 + 5 is False
299
+
300
+ except TypeError as error:
301
+ assert "Timestamp" in str(error) and "int" in str(error), error
302
+
303
+
246
304
  if __name__ == "__main__":
305
+ test_math()
306
+ test_now()
307
+ test_comparison()
247
308
  test_basic()
@@ -6,7 +6,7 @@ from datetime import date, datetime
6
6
 
7
7
  from devopsdriver.azdo import Wiql
8
8
  from devopsdriver.azdo import Ascending, Descending, Value
9
- from devopsdriver.azdo import IsEmpty, IsNotEmpty, And, Or
9
+ from devopsdriver.azdo import IsEmpty, IsNotEmpty, And, Or, In, NotIn
10
10
  from devopsdriver.azdo import GreaterThan, LessThan, Equal, NotEqual
11
11
  from devopsdriver.azdo import GreaterThanOrEqual, LessThanOrEqual
12
12
 
@@ -65,7 +65,21 @@ def test_invalid_value_type() -> None:
65
65
  pass
66
66
 
67
67
 
68
+ def test_in_and_not_in() -> None:
69
+ """Test in and not in operators"""
70
+ builder = Wiql().where(
71
+ And(In("State", "New", "Ready for Development"), NotIn("Priority", 1, 2))
72
+ )
73
+ expected = (
74
+ """SELECT [System.Id] FROM workitems WHERE [System.State] """
75
+ + """IN ("New", "Ready for Development") AND [Microsoft.VSTS.Common.Priority] """
76
+ + """NOT IN (1, 2)"""
77
+ )
78
+ assert str(builder) == expected, str(builder)
79
+
80
+
68
81
  if __name__ == "__main__":
82
+ test_in_and_not_in()
69
83
  test_invalid_value_type()
70
84
  test_expressions()
71
85
  test_no_params()
@@ -1,5 +0,0 @@
1
- """ DevOps tools """
2
-
3
- __version__ = "0.1.39"
4
- __author__ = "Marc Page"
5
- __credits__ = ""
File without changes
File without changes