argus-alm 0.12.10__py3-none-any.whl → 0.13.0__py3-none-any.whl

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 (92) hide show
  1. argus/client/base.py +1 -1
  2. argus/client/driver_matrix_tests/cli.py +2 -2
  3. argus/client/driver_matrix_tests/client.py +1 -1
  4. argus/client/generic/cli.py +2 -2
  5. argus/client/sct/client.py +3 -3
  6. argus/client/sirenada/client.py +1 -1
  7. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/METADATA +2 -4
  8. argus_alm-0.13.0.dist-info/RECORD +20 -0
  9. argus/backend/.gitkeep +0 -0
  10. argus/backend/cli.py +0 -41
  11. argus/backend/controller/__init__.py +0 -0
  12. argus/backend/controller/admin.py +0 -20
  13. argus/backend/controller/admin_api.py +0 -354
  14. argus/backend/controller/api.py +0 -529
  15. argus/backend/controller/auth.py +0 -67
  16. argus/backend/controller/client_api.py +0 -108
  17. argus/backend/controller/main.py +0 -274
  18. argus/backend/controller/notification_api.py +0 -72
  19. argus/backend/controller/notifications.py +0 -13
  20. argus/backend/controller/team.py +0 -126
  21. argus/backend/controller/team_ui.py +0 -18
  22. argus/backend/controller/testrun_api.py +0 -482
  23. argus/backend/controller/view_api.py +0 -162
  24. argus/backend/db.py +0 -100
  25. argus/backend/error_handlers.py +0 -21
  26. argus/backend/events/event_processors.py +0 -34
  27. argus/backend/models/__init__.py +0 -0
  28. argus/backend/models/result.py +0 -138
  29. argus/backend/models/web.py +0 -389
  30. argus/backend/plugins/__init__.py +0 -0
  31. argus/backend/plugins/core.py +0 -225
  32. argus/backend/plugins/driver_matrix_tests/controller.py +0 -63
  33. argus/backend/plugins/driver_matrix_tests/model.py +0 -421
  34. argus/backend/plugins/driver_matrix_tests/plugin.py +0 -22
  35. argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -62
  36. argus/backend/plugins/driver_matrix_tests/service.py +0 -60
  37. argus/backend/plugins/driver_matrix_tests/udt.py +0 -42
  38. argus/backend/plugins/generic/model.py +0 -79
  39. argus/backend/plugins/generic/plugin.py +0 -16
  40. argus/backend/plugins/generic/types.py +0 -13
  41. argus/backend/plugins/loader.py +0 -40
  42. argus/backend/plugins/sct/controller.py +0 -185
  43. argus/backend/plugins/sct/plugin.py +0 -38
  44. argus/backend/plugins/sct/resource_setup.py +0 -178
  45. argus/backend/plugins/sct/service.py +0 -491
  46. argus/backend/plugins/sct/testrun.py +0 -272
  47. argus/backend/plugins/sct/udt.py +0 -101
  48. argus/backend/plugins/sirenada/model.py +0 -113
  49. argus/backend/plugins/sirenada/plugin.py +0 -17
  50. argus/backend/service/admin.py +0 -27
  51. argus/backend/service/argus_service.py +0 -688
  52. argus/backend/service/build_system_monitor.py +0 -188
  53. argus/backend/service/client_service.py +0 -122
  54. argus/backend/service/event_service.py +0 -18
  55. argus/backend/service/jenkins_service.py +0 -240
  56. argus/backend/service/notification_manager.py +0 -150
  57. argus/backend/service/release_manager.py +0 -230
  58. argus/backend/service/results_service.py +0 -317
  59. argus/backend/service/stats.py +0 -540
  60. argus/backend/service/team_manager_service.py +0 -83
  61. argus/backend/service/testrun.py +0 -559
  62. argus/backend/service/user.py +0 -307
  63. argus/backend/service/views.py +0 -258
  64. argus/backend/template_filters.py +0 -27
  65. argus/backend/tests/__init__.py +0 -0
  66. argus/backend/tests/argus_web.test.yaml +0 -39
  67. argus/backend/tests/conftest.py +0 -44
  68. argus/backend/tests/results_service/__init__.py +0 -0
  69. argus/backend/tests/results_service/test_best_results.py +0 -70
  70. argus/backend/util/common.py +0 -65
  71. argus/backend/util/config.py +0 -38
  72. argus/backend/util/encoders.py +0 -41
  73. argus/backend/util/logsetup.py +0 -81
  74. argus/backend/util/module_loaders.py +0 -30
  75. argus/backend/util/send_email.py +0 -91
  76. argus/client/generic_result_old.py +0 -143
  77. argus/db/.gitkeep +0 -0
  78. argus/db/argus_json.py +0 -14
  79. argus/db/cloud_types.py +0 -125
  80. argus/db/config.py +0 -135
  81. argus/db/db_types.py +0 -139
  82. argus/db/interface.py +0 -370
  83. argus/db/testrun.py +0 -740
  84. argus/db/utils.py +0 -15
  85. argus_alm-0.12.10.dist-info/RECORD +0 -96
  86. /argus/{backend → common}/__init__.py +0 -0
  87. /argus/{backend/util → common}/enums.py +0 -0
  88. /argus/{backend/plugins/sct/types.py → common/sct_types.py} +0 -0
  89. /argus/{backend/plugins/sirenada/types.py → common/sirenada_types.py} +0 -0
  90. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/LICENSE +0 -0
  91. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/WHEEL +0 -0
  92. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/entry_points.txt +0 -0
@@ -1,540 +0,0 @@
1
- from collections import defaultdict
2
- from functools import reduce
3
- import logging
4
-
5
- from datetime import datetime
6
- from typing import TypedDict
7
- from uuid import UUID
8
-
9
- from cassandra.cqlengine.models import Model
10
- from argus.backend.plugins.loader import all_plugin_models
11
- from argus.backend.util.common import chunk, get_build_number
12
- from argus.backend.util.enums import TestStatus, TestInvestigationStatus
13
- from argus.backend.models.web import ArgusGithubIssue, ArgusRelease, ArgusGroup, ArgusTest,\
14
- ArgusScheduleTest, ArgusTestRunComment, ArgusUserView
15
- from argus.backend.db import ScyllaCluster
16
-
17
- LOGGER = logging.getLogger(__name__)
18
-
19
-
20
- class TestRunStatRow(TypedDict):
21
- build_id: str
22
- status: TestStatus
23
- investigation_status: TestInvestigationStatus
24
- assignee: UUID
25
- scylla_version: str # TODO: rework SCT specific field
26
- start_time: datetime
27
- end_time: datetime
28
- heartbeat: int
29
- id: UUID
30
- test_id: UUID
31
- group_id: UUID
32
- release_id: UUID
33
- build_job_url: str
34
-
35
-
36
- class ComparableTestStatus:
37
- PRIORITY_MAP = {
38
- TestStatus.FAILED: 10,
39
- TestStatus.TEST_ERROR: 10,
40
- TestStatus.ABORTED: 9,
41
- TestStatus.RUNNING: 8,
42
- TestStatus.CREATED: 7,
43
- TestStatus.PASSED: 6,
44
- }
45
-
46
- def __init__(self, status: TestStatus):
47
- self._status = status
48
-
49
- def _get_prio(self):
50
- return self.PRIORITY_MAP.get(self._status, 0)
51
-
52
- def __eq__(self, __o: object) -> bool:
53
- if not isinstance(__o, ComparableTestStatus):
54
- return False
55
- return self._get_prio() == __o._get_prio()
56
-
57
- def __ne__(self, __o: object) -> bool:
58
- if not isinstance(__o, ComparableTestStatus):
59
- return False
60
- return not self.__eq__(__o)
61
-
62
- def __lt__(self, __o: object) -> bool:
63
- if not isinstance(__o, ComparableTestStatus):
64
- return False
65
- return self._get_prio() < __o._get_prio()
66
-
67
- def __gt__(self, __o: object) -> bool:
68
- if not isinstance(__o, ComparableTestStatus):
69
- return False
70
- return self._get_prio() > __o._get_prio()
71
-
72
- def __ge__(self, __o: object) -> bool:
73
- if not isinstance(__o, ComparableTestStatus):
74
- return False
75
- return self._get_prio() >= __o._get_prio()
76
-
77
- def __le__(self, __o: object) -> bool:
78
- if not isinstance(__o, ComparableTestStatus):
79
- return False
80
- return self._get_prio() <= __o._get_prio()
81
-
82
-
83
- class ComparableTestInvestigationStatus:
84
- PRIORITY_MAP = {
85
- TestInvestigationStatus.NOT_INVESTIGATED: 10,
86
- TestInvestigationStatus.IN_PROGRESS: 9,
87
- TestInvestigationStatus.INVESTIGATED: 8,
88
- TestInvestigationStatus.IGNORED: 7,
89
- }
90
-
91
- def __init__(self, status: TestInvestigationStatus):
92
- self._status = status
93
-
94
- def _get_prio(self):
95
- return self.PRIORITY_MAP.get(self._status, 0)
96
-
97
- def __eq__(self, __o: object) -> bool:
98
- if not isinstance(__o, ComparableTestInvestigationStatus):
99
- return False
100
- return self._get_prio() == __o._get_prio()
101
-
102
- def __ne__(self, __o: object) -> bool:
103
- if not isinstance(__o, ComparableTestInvestigationStatus):
104
- return False
105
- return not self.__eq__(__o)
106
-
107
- def __lt__(self, __o: object) -> bool:
108
- if not isinstance(__o, ComparableTestInvestigationStatus):
109
- return False
110
- return self._get_prio() < __o._get_prio()
111
-
112
- def __gt__(self, __o: object) -> bool:
113
- if not isinstance(__o, ComparableTestInvestigationStatus):
114
- return False
115
- return self._get_prio() > __o._get_prio()
116
-
117
- def __ge__(self, __o: object) -> bool:
118
- if not isinstance(__o, ComparableTestInvestigationStatus):
119
- return False
120
- return self._get_prio() >= __o._get_prio()
121
-
122
- def __le__(self, __o: object) -> bool:
123
- if not isinstance(__o, ComparableTestInvestigationStatus):
124
- return False
125
- return self._get_prio() <= __o._get_prio()
126
-
127
-
128
- def generate_field_status_map(
129
- last_runs: list[TestRunStatRow],
130
- field_name = "status",
131
- container_class = TestStatus,
132
- cmp_class = ComparableTestStatus
133
- ) -> dict[int, tuple[str, TestRunStatRow]]:
134
-
135
- status_map = {}
136
- for run in last_runs:
137
- run_number = get_build_number(run["build_job_url"])
138
- match status := status_map.get(run_number):
139
- case str():
140
- if cmp_class(container_class(status)) < cmp_class(container_class(run[field_name])):
141
- status_map[run_number] = run[field_name]
142
- case _:
143
- status_map[run_number] = (run[field_name], run)
144
- return status_map
145
-
146
- class ViewStats:
147
- def __init__(self, release: ArgusUserView) -> None:
148
- self.release = release
149
- self.groups: list[GroupStats] = []
150
- self.status_map = {status: 0 for status in TestStatus}
151
- self.total_tests = 0
152
- self.last_status = TestStatus.NOT_PLANNED
153
- self.last_investigation_status = TestInvestigationStatus.NOT_INVESTIGATED
154
- self.has_bug_report = False
155
- self.issues: list[ArgusGithubIssue] = []
156
- self.comments: list[ArgusTestRunComment] = []
157
- self.test_schedules: dict[UUID, ArgusScheduleTest] = {}
158
- self.forced_collection = False
159
- self.rows = []
160
- self.releases = {}
161
- self.all_tests = []
162
-
163
- def to_dict(self) -> dict:
164
- converted_groups = {str(group.group.id): group.to_dict() for group in self.groups}
165
- aggregated_investigation_status = {}
166
- for group in converted_groups.values():
167
- for investigation_status in TestInvestigationStatus:
168
- current_status = aggregated_investigation_status.get(investigation_status.value, {})
169
- result = {
170
- status.value: current_status.get(status.value, 0) + group.get(investigation_status.value, {}).get(status, 0)
171
- for status in TestStatus
172
- }
173
- aggregated_investigation_status[investigation_status.value] = result
174
-
175
- return {
176
- "release": dict(self.release.items()),
177
- "releases": self.releases,
178
- "groups": converted_groups,
179
- "total": self.total_tests,
180
- **self.status_map,
181
- "disabled": False,
182
- "perpetual": False,
183
- "lastStatus": self.last_investigation_status,
184
- "lastInvestigationStatus": self.last_investigation_status,
185
- "hasBugReport": self.has_bug_report,
186
- **aggregated_investigation_status
187
- }
188
-
189
- def _fetch_multiple_release_queries(self, entity: Model, releases: list[str]):
190
- result_set = []
191
- for release_id in releases:
192
- result_set.extend(entity.filter(release_id=release_id).all())
193
- return result_set
194
-
195
- def collect(self, rows: list[TestRunStatRow], limited=False, force=False, dict: dict[str, TestRunStatRow] | None = None, tests: list[ArgusTest] = None) -> None:
196
- self.forced_collection = force
197
- all_release_ids = list({t.release_id for t in tests})
198
- if not limited:
199
- self.test_schedules = reduce(
200
- lambda acc, row: acc[row["test_id"]].append(row) or acc,
201
- self._fetch_multiple_release_queries(ArgusScheduleTest, all_release_ids),
202
- defaultdict(list)
203
- )
204
-
205
- self.rows = rows
206
- self.dict = dict
207
- if not limited or force:
208
- self.issues = reduce(
209
- lambda acc, row: acc[row["run_id"]].append(row) or acc,
210
- self._fetch_multiple_release_queries(ArgusGithubIssue, all_release_ids),
211
- defaultdict(list)
212
- )
213
- self.comments = reduce(
214
- lambda acc, row: acc[row["test_run_id"]].append(row) or acc,
215
- self._fetch_multiple_release_queries(ArgusTestRunComment, all_release_ids),
216
- defaultdict(list)
217
- )
218
- self.all_tests = tests
219
- groups = []
220
- for slice in chunk(list({t.release_id for t in tests})):
221
- self.releases.update({str(release.id): release for release in ArgusRelease.filter(id__in=slice).all()})
222
-
223
- for slice in chunk(list({t.group_id for t in tests})):
224
- groups.extend(ArgusGroup.filter(id__in=slice).all())
225
- for group in groups:
226
- if group.enabled:
227
- stats = GroupStats(group=group, parent_release=self)
228
- stats.collect(limited=limited)
229
- self.groups.append(stats)
230
-
231
- def increment_status(self, status=TestStatus.NOT_PLANNED):
232
- self.total_tests += 1
233
- self.status_map[TestStatus(status)] += 1
234
- self.last_status = TestStatus(status)
235
-
236
-
237
- class ReleaseStats:
238
- def __init__(self, release: ArgusRelease) -> None:
239
- self.release = release
240
- self.groups: list[GroupStats] = []
241
- self.status_map = {status: 0 for status in TestStatus}
242
- self.total_tests = 0
243
- self.last_status = TestStatus.NOT_PLANNED
244
- self.last_investigation_status = TestInvestigationStatus.NOT_INVESTIGATED
245
- self.has_bug_report = False
246
- self.issues: list[ArgusGithubIssue] = []
247
- self.comments: list[ArgusTestRunComment] = []
248
- self.test_schedules: dict[UUID, ArgusScheduleTest] = {}
249
- self.forced_collection = False
250
- self.rows = []
251
- self.all_tests = []
252
-
253
- def to_dict(self) -> dict:
254
- converted_groups = {str(group.group.id): group.to_dict() for group in self.groups}
255
- aggregated_investigation_status = {}
256
- for group in converted_groups.values():
257
- for investigation_status in TestInvestigationStatus:
258
- current_status = aggregated_investigation_status.get(investigation_status.value, {})
259
- result = {
260
- status.value: current_status.get(status.value, 0) + group.get(investigation_status.value, {}).get(status, 0)
261
- for status in TestStatus
262
- }
263
- aggregated_investigation_status[investigation_status.value] = result
264
-
265
- return {
266
- "release": dict(self.release.items()),
267
- "groups": converted_groups,
268
- "total": self.total_tests,
269
- **self.status_map,
270
- "disabled": not self.release.enabled,
271
- "perpetual": self.release.perpetual,
272
- "lastStatus": self.last_investigation_status,
273
- "lastInvestigationStatus": self.last_investigation_status,
274
- "hasBugReport": self.has_bug_report,
275
- **aggregated_investigation_status
276
- }
277
-
278
- def collect(self, rows: list[TestRunStatRow], limited=False, force=False, dict: dict | None = None, tests=None) -> None:
279
- self.forced_collection = force
280
- if not self.release.enabled and not force:
281
- return
282
-
283
- if not self.release.perpetual and not limited:
284
- self.test_schedules = reduce(
285
- lambda acc, row: acc[row["test_id"]].append(row) or acc,
286
- ArgusScheduleTest.filter(release_id=self.release.id).all(),
287
- defaultdict(list)
288
- )
289
-
290
- self.rows = rows
291
- self.dict = dict
292
- if not limited or force:
293
- self.issues = reduce(
294
- lambda acc, row: acc[row["run_id"]].append(row) or acc,
295
- ArgusGithubIssue.filter(release_id=self.release.id).all(),
296
- defaultdict(list)
297
- )
298
- self.comments = reduce(
299
- lambda acc, row: acc[row["test_run_id"]].append(row) or acc,
300
- ArgusTestRunComment.filter(release_id=self.release.id).all(),
301
- defaultdict(list)
302
- )
303
- self.all_tests = ArgusTest.filter(release_id=self.release.id).all() if not tests else tests
304
- groups: list[ArgusGroup] = ArgusGroup.filter(release_id=self.release.id).all()
305
- for group in groups:
306
- if group.enabled:
307
- stats = GroupStats(group=group, parent_release=self)
308
- stats.collect(limited=limited)
309
- self.groups.append(stats)
310
-
311
- def increment_status(self, status=TestStatus.NOT_PLANNED):
312
- self.total_tests += 1
313
- self.status_map[TestStatus(status)] += 1
314
- self.last_status = TestStatus(status)
315
-
316
-
317
- class GroupStats:
318
- def __init__(self, group: ArgusGroup, parent_release: ReleaseStats) -> None:
319
- self.group = group
320
- self.parent_release = parent_release
321
- self.status_map = {status: 0 for status in TestStatus}
322
- self.total_tests = 0
323
- self.last_status = TestStatus.NOT_PLANNED
324
- self.last_investigation_status = TestInvestigationStatus.NOT_INVESTIGATED
325
- self.disabled = False
326
- self.tests: list[TestStats] = []
327
-
328
- def to_dict(self) -> dict:
329
- converted_tests = {str(test.test.id): test.to_dict() for test in self.tests}
330
- investigation_progress = {}
331
- for test in converted_tests.values():
332
- progress_for_status = investigation_progress.get(test["investigation_status"], {})
333
- status_count = progress_for_status.get(test["status"], 0)
334
- status_count += 1
335
- progress_for_status[test["status"]] = status_count
336
- investigation_progress[test["investigation_status"]] = progress_for_status
337
-
338
- return {
339
- "group": dict(self.group.items()),
340
- "total": self.total_tests,
341
- **self.status_map,
342
- "lastStatus": self.last_status,
343
- "lastInvestigationStatus": self.last_investigation_status,
344
- "disabled": self.disabled,
345
- "tests": converted_tests,
346
- **investigation_progress
347
- }
348
-
349
- def collect(self, limited=False):
350
- tests = [test for test in self.parent_release.all_tests if test.group_id == self.group.id]
351
-
352
- for test in tests:
353
- if test.enabled:
354
- stats = TestStats(
355
- test=test,
356
- parent_group=self,
357
- schedules=self.parent_release.test_schedules.get(test.id, [])
358
- )
359
- stats.collect(limited=limited)
360
- self.tests.append(stats)
361
-
362
- def increment_status(self, status=TestStatus.NOT_PLANNED):
363
- self.status_map[TestStatus(status)] += 1
364
- self.total_tests += 1
365
- self.last_status = TestStatus(status)
366
- self.parent_release.increment_status(status)
367
-
368
-
369
- class TestStats:
370
- def __init__(
371
- self,
372
- test: ArgusTest,
373
- parent_group: GroupStats,
374
- schedules: list[ArgusScheduleTest] | None = None
375
- ) -> None:
376
- self.test = test
377
- self.parent_group = parent_group
378
- self.start_time = datetime.fromtimestamp(0)
379
- self.status = TestStatus.NOT_PLANNED
380
- self.investigation_status = TestInvestigationStatus.NOT_INVESTIGATED
381
- self.last_runs: list[dict] = []
382
- self.has_bug_report = False
383
- self.has_comments = False
384
- self.schedules = schedules if schedules else tuple()
385
- self.is_scheduled = len(self.schedules) > 0
386
- self.tracked_run_number = None
387
-
388
- def to_dict(self) -> dict:
389
- return {
390
- "test": dict(self.test.items()),
391
- "status": self.status,
392
- "investigation_status": self.investigation_status,
393
- "last_runs": self.last_runs,
394
- "start_time": self.start_time,
395
- "hasBugReport": self.has_bug_report,
396
- "hasComments": self.has_comments,
397
- "buildNumber": self.tracked_run_number,
398
- "buildId": self.test.build_system_id,
399
- }
400
-
401
- def collect(self, limited=False):
402
-
403
- # TODO: Parametrize run limit
404
- # FIXME: This is only a mitigation, build_number overflows on the build system side.
405
- if not self.parent_group.parent_release.dict:
406
- last_runs = [r for r in self.parent_group.parent_release.rows if r["build_id"] == self.test.build_system_id]
407
- else:
408
- last_runs = self.parent_group.parent_release.dict.get(self.test.build_system_id, [])
409
- last_runs: list[TestRunStatRow] = sorted(
410
- last_runs, reverse=True, key=lambda r: get_build_number(r["build_job_url"]))
411
- try:
412
- last_run = last_runs[0]
413
- except IndexError:
414
- self.status = TestStatus.NOT_RUN if self.is_scheduled else TestStatus.NOT_PLANNED
415
- self.parent_group.increment_status(status=self.status)
416
- return
417
- status_map = generate_field_status_map(last_runs)
418
-
419
- worst_case = status_map.get(get_build_number(last_run["build_job_url"]))
420
- self.status = worst_case[0]
421
- self.investigation_status = worst_case[1]["investigation_status"]
422
- self.start_time = last_run["start_time"]
423
-
424
- self.parent_group.increment_status(status=self.status)
425
- if limited and not self.parent_group.parent_release.forced_collection:
426
- return
427
-
428
- self.last_runs = [
429
- {
430
- "id": run["id"],
431
- "status": run["status"],
432
- "build_number": get_build_number(run["build_job_url"]),
433
- "build_job_name": run["build_id"],
434
- "start_time": run["start_time"],
435
- "assignee": run["assignee"],
436
- "issues": [dict(issue.items()) for issue in self.parent_group.parent_release.issues[run["id"]]],
437
- "comments": [dict(comment.items()) for comment in self.parent_group.parent_release.comments[run["id"]]],
438
- }
439
- for run in last_runs
440
- ]
441
- try:
442
- target_run = next(run for run in self.last_runs if run["id"] == worst_case[1]["id"])
443
- except StopIteration:
444
- target_run = worst_case[1]
445
- target_run["issues"] = [dict(issue.items()) for issue in self.parent_group.parent_release.issues[target_run["id"]]]
446
- target_run["comments"] = [dict(comment.items()) for comment in self.parent_group.parent_release.comments[target_run["id"]]]
447
- self.has_bug_report = len(target_run["issues"]) > 0
448
- self.parent_group.parent_release.has_bug_report = self.has_bug_report or self.parent_group.parent_release.has_bug_report
449
- self.has_comments = len(target_run["comments"]) > 0
450
- self.last_runs = self.last_runs[:5]
451
- self.tracked_run_number = target_run.get("build_number", get_build_number(target_run.get("build_job_url")))
452
-
453
-
454
- class ReleaseStatsCollector:
455
- def __init__(self, release_name: str, release_version: str | None = None) -> None:
456
- self.database = ScyllaCluster.get()
457
- self.session = self.database.get_session()
458
- self.release = None
459
- self.release_stats = None
460
- self.release_rows = []
461
- self.release_name = release_name
462
- self.release_version = release_version
463
-
464
- def collect(self, limited=False, force=False, include_no_version=False) -> dict:
465
- self.release: ArgusRelease = ArgusRelease.get(name=self.release_name)
466
- all_tests: list[ArgusTest] = list(ArgusTest.filter(release_id=self.release.id).all())
467
- build_ids = reduce(lambda acc, test: acc[test.plugin_name or "unknown"].append(test.build_system_id) or acc, all_tests, defaultdict(list))
468
- self.release_rows = [futures for plugin in all_plugin_models()
469
- for futures in plugin.get_stats_for_release(release=self.release, build_ids=build_ids.get(plugin._plugin_name, []))]
470
- self.release_rows = [row for future in self.release_rows for row in future.result()]
471
- if self.release.dormant and not force:
472
- return {
473
- "dormant": True
474
- }
475
- if self.release_version:
476
- if include_no_version:
477
- expr = lambda row: row["scylla_version"] == self.release_version or not row["scylla_version"]
478
- elif self.release_version == "!noVersion":
479
- expr = lambda row: not row["scylla_version"]
480
- else:
481
- expr = lambda row: row["scylla_version"] == self.release_version
482
- else:
483
- if include_no_version:
484
- expr = lambda row: row
485
- else:
486
- expr = lambda row: row["scylla_version"]
487
- self.release_rows = list(filter(expr, self.release_rows))
488
- self.release_dict = {}
489
- for row in self.release_rows:
490
- runs = self.release_dict.get(row["build_id"], [])
491
- runs.append(row)
492
- self.release_dict[row["build_id"]] = runs
493
-
494
- self.release_stats = ReleaseStats(release=self.release)
495
- self.release_stats.collect(rows=self.release_rows, limited=limited, force=force, dict=self.release_dict, tests=all_tests)
496
- return self.release_stats.to_dict()
497
-
498
-
499
- class ViewStatsCollector:
500
- def __init__(self, view_id: UUID, filter: str | None = None) -> None:
501
- self.database = ScyllaCluster.get()
502
- self.session = self.database.get_session()
503
- self.view = None
504
- self.view_stats = None
505
- self.view_rows = []
506
- self.runs_by_build_id = {}
507
- self.view_id = view_id
508
- self.filter = filter
509
-
510
- def collect(self, limited=False, force=False, include_no_version=False) -> dict:
511
- self.view: ArgusUserView = ArgusUserView.get(id=self.view_id)
512
- all_tests: list[ArgusTest] = []
513
- for slice in chunk(self.view.tests):
514
- all_tests.extend(ArgusTest.filter(id__in=slice).all())
515
- build_ids = reduce(lambda acc, test: acc[test.plugin_name or "unknown"].append(test.build_system_id) or acc, all_tests, defaultdict(list))
516
- self.view_rows = [futures for plugin in all_plugin_models()
517
- for futures in plugin.get_stats_for_release(release=self.view, build_ids=build_ids.get(plugin._plugin_name, []))]
518
- self.view_rows = [row for future in self.view_rows for row in future.result()]
519
-
520
- if self.filter:
521
- if include_no_version:
522
- expr = lambda row: row["scylla_version"] == self.filter or not row["scylla_version"]
523
- elif self.filter == "!noVersion":
524
- expr = lambda row: not row["scylla_version"]
525
- else:
526
- expr = lambda row: row["scylla_version"] == self.filter
527
- else:
528
- if include_no_version:
529
- expr = lambda row: row
530
- else:
531
- expr = lambda row: row["scylla_version"]
532
- self.view_rows = list(filter(expr, self.view_rows))
533
- for row in self.view_rows:
534
- runs = self.runs_by_build_id.get(row["build_id"], [])
535
- runs.append(row)
536
- self.runs_by_build_id[row["build_id"]] = runs
537
-
538
- self.view_stats = ViewStats(release=self.view)
539
- self.view_stats.collect(rows=self.view_rows, limited=limited, force=force, dict=self.runs_by_build_id, tests=all_tests)
540
- return self.view_stats.to_dict()
@@ -1,83 +0,0 @@
1
- import logging
2
- from uuid import UUID
3
-
4
- from flask.globals import g
5
- from argus.backend.db import ScyllaCluster
6
- from argus.backend.models.web import Team, User
7
-
8
- LOGGER = logging.getLogger(__name__)
9
-
10
-
11
- class TeamManagerException(Exception):
12
- pass
13
-
14
-
15
- class TeamManagerService:
16
- # pylint: disable=no-self-use
17
- def __init__(self, database: ScyllaCluster | None = None) -> None:
18
- if not database:
19
- database = ScyllaCluster.get()
20
-
21
- self.session = database.get_session()
22
- self.database = database
23
-
24
- def create_team(self, name: str, leader: UUID, members: list[UUID]):
25
- team = Team()
26
- team.name = name
27
- team.leader = leader
28
- team.members = [leader, *members]
29
-
30
- team.save()
31
- return team
32
-
33
- def get_teams_for_user(self, user_id: UUID) -> list[Team]:
34
- return list(Team.filter(leader=user_id).all())
35
-
36
- def get_team_by_id(self, team_id: UUID) -> Team:
37
- try:
38
- return Team.get(id=team_id)
39
- except Team.DoesNotExist as exc:
40
- raise TeamManagerException(f"Team {team_id} does not exist", team_id) from exc
41
-
42
- def edit_team(self, team_id: UUID, name: str, members: list[UUID]):
43
- user: User = g.user
44
- try:
45
- team: Team = Team.get(id=team_id)
46
- if team.leader != user.id:
47
- raise TeamManagerException(f"Cannot edit team \"{team.name}\" as it doesn't belong to the user.")
48
- team.name = name
49
- team.members = [team.leader, *members] if team.leader not in members else members
50
-
51
- team.save()
52
-
53
- return team
54
- except Team.DoesNotExist as exc:
55
- raise TeamManagerException(f"Team {team_id} doesn't exist!", team_id) from exc
56
-
57
- def edit_team_motd(self, team_id: UUID, message: str):
58
- user: User = g.user
59
- try:
60
- team: Team = Team.get(id=team_id)
61
- if team.leader != user.id:
62
- raise TeamManagerException(f"Cannot edit team \"{team.name}\"'s MOTD as it doesn't belong to the user.")
63
- team.motd = message
64
-
65
- team.save()
66
- except Team.DoesNotExist as exc:
67
- raise TeamManagerException(f"Team {team_id} doesn't exist!", team_id) from exc
68
-
69
- def delete_team(self, team_id: UUID):
70
- user: User = g.user
71
- try:
72
- team: Team = Team.get(id=team_id)
73
- if team.leader != user.id:
74
- raise TeamManagerException(f"Cannot delete team \"{team.name}\" as it doesn't belong to the user.")
75
- team.delete()
76
- except Team.DoesNotExist as exc:
77
- raise TeamManagerException(f"Team {team_id} doesn't exist!", team_id) from exc
78
-
79
- def get_users_teams(self, user_id: UUID) -> list[Team]:
80
- teams_containing_user = list(Team.all())
81
- users_teams = self.get_teams_for_user(user_id)
82
- created_teams_ids = [team.id for team in users_teams]
83
- return [*users_teams, *[team for team in teams_containing_user if team.id not in created_teams_ids]]