argus-alm 0.12.10__py3-none-any.whl → 0.13.1__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 (94) 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 +22 -2
  5. argus/client/generic/client.py +22 -0
  6. argus/client/generic_result.py +3 -3
  7. argus/client/sct/client.py +5 -4
  8. argus/client/sirenada/client.py +1 -1
  9. {argus_alm-0.12.10.dist-info → argus_alm-0.13.1.dist-info}/METADATA +2 -4
  10. argus_alm-0.13.1.dist-info/RECORD +20 -0
  11. argus/backend/.gitkeep +0 -0
  12. argus/backend/cli.py +0 -41
  13. argus/backend/controller/__init__.py +0 -0
  14. argus/backend/controller/admin.py +0 -20
  15. argus/backend/controller/admin_api.py +0 -354
  16. argus/backend/controller/api.py +0 -529
  17. argus/backend/controller/auth.py +0 -67
  18. argus/backend/controller/client_api.py +0 -108
  19. argus/backend/controller/main.py +0 -274
  20. argus/backend/controller/notification_api.py +0 -72
  21. argus/backend/controller/notifications.py +0 -13
  22. argus/backend/controller/team.py +0 -126
  23. argus/backend/controller/team_ui.py +0 -18
  24. argus/backend/controller/testrun_api.py +0 -482
  25. argus/backend/controller/view_api.py +0 -162
  26. argus/backend/db.py +0 -100
  27. argus/backend/error_handlers.py +0 -21
  28. argus/backend/events/event_processors.py +0 -34
  29. argus/backend/models/__init__.py +0 -0
  30. argus/backend/models/result.py +0 -138
  31. argus/backend/models/web.py +0 -389
  32. argus/backend/plugins/__init__.py +0 -0
  33. argus/backend/plugins/core.py +0 -225
  34. argus/backend/plugins/driver_matrix_tests/controller.py +0 -63
  35. argus/backend/plugins/driver_matrix_tests/model.py +0 -421
  36. argus/backend/plugins/driver_matrix_tests/plugin.py +0 -22
  37. argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -62
  38. argus/backend/plugins/driver_matrix_tests/service.py +0 -60
  39. argus/backend/plugins/driver_matrix_tests/udt.py +0 -42
  40. argus/backend/plugins/generic/model.py +0 -79
  41. argus/backend/plugins/generic/plugin.py +0 -16
  42. argus/backend/plugins/generic/types.py +0 -13
  43. argus/backend/plugins/loader.py +0 -40
  44. argus/backend/plugins/sct/controller.py +0 -185
  45. argus/backend/plugins/sct/plugin.py +0 -38
  46. argus/backend/plugins/sct/resource_setup.py +0 -178
  47. argus/backend/plugins/sct/service.py +0 -491
  48. argus/backend/plugins/sct/testrun.py +0 -272
  49. argus/backend/plugins/sct/udt.py +0 -101
  50. argus/backend/plugins/sirenada/model.py +0 -113
  51. argus/backend/plugins/sirenada/plugin.py +0 -17
  52. argus/backend/service/admin.py +0 -27
  53. argus/backend/service/argus_service.py +0 -688
  54. argus/backend/service/build_system_monitor.py +0 -188
  55. argus/backend/service/client_service.py +0 -122
  56. argus/backend/service/event_service.py +0 -18
  57. argus/backend/service/jenkins_service.py +0 -240
  58. argus/backend/service/notification_manager.py +0 -150
  59. argus/backend/service/release_manager.py +0 -230
  60. argus/backend/service/results_service.py +0 -317
  61. argus/backend/service/stats.py +0 -540
  62. argus/backend/service/team_manager_service.py +0 -83
  63. argus/backend/service/testrun.py +0 -559
  64. argus/backend/service/user.py +0 -307
  65. argus/backend/service/views.py +0 -258
  66. argus/backend/template_filters.py +0 -27
  67. argus/backend/tests/__init__.py +0 -0
  68. argus/backend/tests/argus_web.test.yaml +0 -39
  69. argus/backend/tests/conftest.py +0 -44
  70. argus/backend/tests/results_service/__init__.py +0 -0
  71. argus/backend/tests/results_service/test_best_results.py +0 -70
  72. argus/backend/util/common.py +0 -65
  73. argus/backend/util/config.py +0 -38
  74. argus/backend/util/encoders.py +0 -41
  75. argus/backend/util/logsetup.py +0 -81
  76. argus/backend/util/module_loaders.py +0 -30
  77. argus/backend/util/send_email.py +0 -91
  78. argus/client/generic_result_old.py +0 -143
  79. argus/db/.gitkeep +0 -0
  80. argus/db/argus_json.py +0 -14
  81. argus/db/cloud_types.py +0 -125
  82. argus/db/config.py +0 -135
  83. argus/db/db_types.py +0 -139
  84. argus/db/interface.py +0 -370
  85. argus/db/testrun.py +0 -740
  86. argus/db/utils.py +0 -15
  87. argus_alm-0.12.10.dist-info/RECORD +0 -96
  88. /argus/{backend → common}/__init__.py +0 -0
  89. /argus/{backend/util → common}/enums.py +0 -0
  90. /argus/{backend/plugins/sct/types.py → common/sct_types.py} +0 -0
  91. /argus/{backend/plugins/sirenada/types.py → common/sirenada_types.py} +0 -0
  92. {argus_alm-0.12.10.dist-info → argus_alm-0.13.1.dist-info}/LICENSE +0 -0
  93. {argus_alm-0.12.10.dist-info → argus_alm-0.13.1.dist-info}/WHEEL +0 -0
  94. {argus_alm-0.12.10.dist-info → argus_alm-0.13.1.dist-info}/entry_points.txt +0 -0
@@ -1,688 +0,0 @@
1
- from math import ceil
2
- from dataclasses import dataclass
3
- import subprocess
4
- import json
5
- import logging
6
- import datetime
7
- from types import NoneType
8
- from uuid import UUID
9
- from cassandra.util import uuid_from_time # pylint: disable=no-name-in-module
10
- from flask import current_app
11
- from argus.backend.db import ScyllaCluster
12
- from argus.backend.plugins.loader import AVAILABLE_PLUGINS, all_plugin_models
13
- from argus.backend.plugins.sct.testrun import SCTTestRun
14
- from argus.backend.service.notification_manager import NotificationManagerService
15
- from argus.backend.models.web import (
16
- ArgusRelease,
17
- ArgusGroup,
18
- ArgusTest,
19
- ArgusSchedule,
20
- ArgusScheduleAssignee,
21
- ArgusScheduleGroup,
22
- ArgusScheduleTest,
23
- ArgusTestRunComment,
24
- ArgusEvent,
25
- ReleasePlannerComment,
26
- User,
27
- )
28
- from argus.backend.events.event_processors import EVENT_PROCESSORS
29
- from argus.backend.service.testrun import TestRunService
30
- from argus.backend.util.common import chunk
31
-
32
- LOGGER = logging.getLogger(__name__)
33
-
34
- @dataclass(init=True, frozen=True)
35
- class ScheduleUpdateRequest:
36
- release_id: UUID
37
- schedule_id: UUID
38
- assignee: UUID
39
- new_tests: list[UUID]
40
- old_tests: list[UUID]
41
- comments: dict[UUID, str]
42
-
43
-
44
-
45
- class ArgusService:
46
- # pylint: disable=no-self-use,too-many-arguments,too-many-instance-attributes,too-many-locals, too-many-public-methods
47
- def __init__(self, database_session=None):
48
- self.session = database_session if database_session else ScyllaCluster.get_session()
49
- self.database = ScyllaCluster.get()
50
- self.notification_manager = NotificationManagerService()
51
- self.github_headers = {
52
- "Accept": "application/vnd.github.v3+json",
53
- "Authorization": f"token {current_app.config['GITHUB_ACCESS_TOKEN']}"
54
- }
55
- self.build_id_and_url_statement = self.database.prepare(
56
- f"SELECT build_id, build_job_url, test_id FROM {SCTTestRun.table_name()} WHERE id = ?"
57
- ) # TODO: transfer to PluginModelBase
58
- self.scylla_versions_by_release = self.database.prepare(
59
- f"SELECT scylla_version FROM {SCTTestRun.table_name()} WHERE release_id = ?"
60
- ) # TODO: Moved to PluginModelBase
61
-
62
- def get_version(self) -> str:
63
- try:
64
- proc = subprocess.run(["git", "rev-parse", "HEAD"], check=True, capture_output=True)
65
- except subprocess.CalledProcessError:
66
- proc = None
67
- if proc:
68
- return proc.stdout.decode(encoding="utf-8").strip()
69
- else:
70
- try:
71
- with open("./.argus_version", 'rt', encoding="utf-8") as version_file:
72
- version = version_file.read().strip()
73
- return version
74
- except FileNotFoundError:
75
- return "version_unknown"
76
-
77
- def create_release(self, payload: dict) -> dict:
78
- response = {}
79
- for release_name in payload:
80
- try:
81
- ArgusRelease.get(name=release_name)
82
- response[release_name] = {
83
- "status": "error",
84
- "message": f"Release {release_name} already exists"
85
- }
86
- continue
87
- except ArgusRelease.DoesNotExist:
88
- pass
89
-
90
- new_release = ArgusRelease()
91
- new_release.name = release_name
92
- new_release.save()
93
- response[release_name] = {}
94
- response[release_name]["groups"] = self.create_groups(
95
- groups=payload[release_name]["groups"],
96
- parent_release_id=new_release.id
97
- )
98
-
99
- return response
100
-
101
- def create_groups(self, groups: dict, parent_release_id) -> dict:
102
- response = {}
103
- for group_name, group_definition in groups.items():
104
- new_group = ArgusGroup()
105
- new_group.release_id = parent_release_id
106
- new_group.name = group_name
107
- new_group.pretty_name = group_definition.get("pretty_name")
108
- new_group.save()
109
- response[group_name] = {}
110
- response[group_name]["status"] = "created"
111
- response[group_name]["tests"] = self.create_tests(
112
- tests=group_definition.get("tests", []),
113
- parent_group_id=new_group.id,
114
- parent_release_id=parent_release_id
115
- )
116
- return response
117
-
118
- def create_tests(self, tests: dict, parent_group_id: UUID, parent_release_id: UUID) -> dict:
119
- response = {}
120
-
121
- for test_name in tests:
122
- new_test = ArgusTest()
123
- new_test.release_id = parent_release_id
124
- new_test.group_id = parent_group_id
125
- new_test.name = test_name
126
- new_test.save()
127
- response[test_name] = "created"
128
-
129
- return response
130
-
131
- def get_comment(self, comment_id: UUID) -> ArgusTestRunComment | None:
132
- try:
133
- return ArgusTestRunComment.get(id=comment_id)
134
- except ArgusTestRunComment.DoesNotExist:
135
- return None
136
-
137
- def get_releases(self):
138
- releases = list(ArgusRelease.all())
139
- releases = sorted(releases, key=lambda r: r.name)
140
- releases = sorted(releases, key=lambda r: r.dormant)
141
- return releases
142
-
143
- def get_groups(self, release_id: UUID) -> list[ArgusGroup]:
144
- groups = list(ArgusGroup.filter(release_id=release_id).all())
145
- return sorted(groups, key=lambda g: g.pretty_name if g.pretty_name else g.name)
146
-
147
- def get_groups_for_release(self, release: ArgusRelease):
148
- groups = ArgusGroup.filter(release_id=release.id).all()
149
- return sorted(groups, key=lambda g: g.pretty_name if g.pretty_name else g.name)
150
-
151
- def get_tests(self, group_id: UUID) -> list[ArgusTest]:
152
- return list(ArgusTest.filter(group_id=group_id).all())
153
-
154
- def get_test_info(self, test_id: UUID) -> dict:
155
- test = ArgusTest.get(id=test_id)
156
- group = ArgusGroup.get(id=test.group_id)
157
- release = ArgusRelease.get(id=test.release_id)
158
- return {
159
- "test": dict(test.items()),
160
- "group": dict(group.items()),
161
- "release": dict(release.items()),
162
- }
163
-
164
- def get_data_for_release_dashboard(self, release_name: str):
165
- release = ArgusRelease.get(name=release_name)
166
- release_groups = ArgusGroup.filter(release_id=release.id).all()
167
- release_tests = ArgusTest.filter(release_id=release.id).all()
168
-
169
- return release, release_groups, release_tests
170
-
171
- def get_distinct_release_versions(self, release_id: UUID | str) -> list[str]:
172
- release_id = UUID(release_id) if isinstance(release_id, str) else release_id
173
- release = ArgusRelease.get(id=release_id)
174
- unique_versions = {ver for plugin in all_plugin_models()
175
- for ver in plugin.get_distinct_product_versions(release=release)}
176
-
177
- return sorted(list(unique_versions), reverse=True)
178
-
179
- def poll_test_runs(self, test_id: UUID, additional_runs: list[UUID], limit: int = 10):
180
- test: ArgusTest = ArgusTest.get(id=test_id)
181
-
182
- rows: list[SCTTestRun] = list(SCTTestRun.filter(build_id=test.build_system_id).all().limit(limit))
183
-
184
- rows_ids = [row.id for row in rows]
185
-
186
- for run_id in additional_runs:
187
- if run_id not in rows_ids:
188
- row: SCTTestRun = SCTTestRun.get(id=run_id)
189
- rows.append(row)
190
-
191
- for row in rows:
192
- try:
193
- setattr(row, "build_number", int(row["build_job_url"].rstrip("/").split("/")[-1]))
194
- except ValueError:
195
- setattr(row, "build_number", -1)
196
-
197
- return sorted(rows, reverse=True, key=lambda r: r.build_number)
198
-
199
- def poll_test_runs_single(self, runs: list[UUID]):
200
- rows: list[SCTTestRun] = []
201
- for run_id in runs:
202
- try:
203
- row: SCTTestRun = SCTTestRun.get(id=run_id)
204
- rows.append(row)
205
- except SCTTestRun.DoesNotExist:
206
- pass
207
-
208
- response = {str(row.id): row for row in rows}
209
- return response
210
-
211
- def fetch_release_activity(self, release_name: str) -> dict:
212
- response = {}
213
- release = ArgusRelease.get(name=release_name)
214
- all_events = ArgusEvent.filter(release_id=release.id).all()
215
- all_events = sorted(all_events, key=lambda ev: ev.created_at)
216
- response["release_id"] = release.id
217
- response["raw_events"] = [dict(event.items()) for event in all_events]
218
- response["events"] = {str(event.id): EVENT_PROCESSORS.get(
219
- event.kind)(json.loads(event.body)) for event in all_events}
220
- return response
221
-
222
- def assign_runs_for_scheduled_test(self, schedule: ArgusSchedule, test_id: UUID, new_assignee: UUID):
223
- test: ArgusTest = ArgusTest.get(id=test_id)
224
- affected_rows: list[SCTTestRun] = list(SCTTestRun.filter(
225
- build_id=test.build_system_id,
226
- start_time__gte=schedule.period_start,
227
- start_time__lte=schedule.period_end
228
- ).all()
229
- )
230
- for row in affected_rows:
231
- if row.assignee != new_assignee:
232
- row.assignee = new_assignee
233
- row.save()
234
-
235
- def assign_runs_for_scheduled_group(self, schedule: ArgusSchedule, group_id: UUID, new_assignee: UUID):
236
- tests = ArgusTest.filter(group_id=group_id).all()
237
- build_ids = [test.build_system_id for test in tests]
238
- affected_rows: list[SCTTestRun] = list(SCTTestRun.filter(
239
- start_time__gte=schedule.period_start,
240
- start_time__lte=schedule.period_end
241
- ).all()
242
- )
243
- for row in affected_rows:
244
- if row.build_id in build_ids and row.assignee != new_assignee:
245
- row.assignee = new_assignee
246
- row.save()
247
-
248
- def submit_new_schedule(self, release: str | UUID, start_time: str, end_time: str, tests: list[str | UUID],
249
- groups: list[str | UUID], assignees: list[str | UUID], tag: str, comments: dict[str, str] | None, group_ids: dict[str, str] | None) -> dict:
250
- release = UUID(release) if isinstance(release, str) else release
251
- if len(assignees) == 0:
252
- raise Exception("Assignees not specified in the new schedule")
253
-
254
- if len(tests) == 0 and len(groups) == 0:
255
- raise Exception("Schedule does not contain scheduled objects")
256
-
257
- schedule = ArgusSchedule()
258
- schedule.release_id = release
259
- schedule.period_start = datetime.datetime.fromisoformat(start_time)
260
- schedule.period_end = datetime.datetime.fromisoformat(end_time)
261
- schedule.id = uuid_from_time(schedule.period_start)
262
- schedule.tag = tag
263
- schedule.save()
264
-
265
- response = dict(schedule.items())
266
- response["assignees"] = []
267
- response["tests"] = []
268
- response["groups"] = []
269
-
270
- for test_id in tests:
271
- test_entity = ArgusScheduleTest()
272
- test_entity.id = uuid_from_time(schedule.period_start)
273
- test_entity.schedule_id = schedule.id
274
- test_entity.test_id = UUID(test_id) if isinstance(test_id, str) else test_id
275
- test_entity.release_id = release
276
- test_entity.save()
277
- self.assign_runs_for_scheduled_test(schedule, test_entity.test_id, UUID(assignees[0]))
278
- response["tests"].append(test_id)
279
-
280
- for group_id in groups:
281
- group_entity = ArgusScheduleGroup()
282
- group_entity.id = uuid_from_time(schedule.period_start)
283
- group_entity.schedule_id = schedule.id
284
- group_entity.group_id = UUID(group_id) if isinstance(group_id, str) else group_id
285
- group_entity.release_id = release
286
- group_entity.save()
287
- self.assign_runs_for_scheduled_group(schedule, group_entity.group_id, UUID(assignees[0]))
288
- response["groups"].append(group_id)
289
-
290
- for assignee_id in assignees:
291
- assignee_entity = ArgusScheduleAssignee()
292
- assignee_entity.id = uuid_from_time(schedule.period_start)
293
- assignee_entity.schedule_id = schedule.id
294
- assignee_entity.assignee = UUID(assignee_id) if isinstance(assignee_id, str) else assignee_id
295
- assignee_entity.release_id = release
296
- assignee_entity.save()
297
- response["assignees"].append(assignee_id)
298
-
299
- if comments:
300
- for test_id, new_comment in comments.items():
301
- try:
302
- comment = ReleasePlannerComment.get(release=release, group=group_ids[test_id], test=test_id)
303
- except ReleasePlannerComment.DoesNotExist:
304
- comment = ReleasePlannerComment()
305
- comment.release = release
306
- comment.group = group_ids[test_id]
307
- comment.test = test_id
308
-
309
- comment.comment = new_comment
310
- comment.save()
311
-
312
- return response
313
-
314
- def get_schedules_for_release(self, release_id: str | UUID) -> dict:
315
- """
316
- {
317
- "release": "hex-uuid"
318
- }
319
- """
320
- release_id = UUID(release_id) if isinstance(release_id, str) else release_id
321
- release: ArgusRelease = ArgusRelease.get(id=release_id)
322
- if release.perpetual:
323
- today = datetime.datetime.utcnow()
324
- six_months_ago = today - datetime.timedelta(days=180)
325
- uuid_six_months = uuid_from_time(six_months_ago)
326
- schedules = ArgusSchedule.filter(release_id=release_id, id__gte=uuid_six_months).all()
327
- else:
328
- schedules = ArgusSchedule.filter(release_id=release_id).all()
329
- response = {
330
- "schedules": []
331
- }
332
- for schedule in schedules:
333
- serialized_schedule = dict(schedule.items())
334
- tests = ArgusScheduleTest.filter(schedule_id=schedule.id).all()
335
- serialized_schedule["tests"] = [test.test_id for test in tests]
336
- groups = ArgusScheduleGroup.filter(schedule_id=schedule.id).all()
337
- serialized_schedule["groups"] = [group.group_id for group in groups]
338
- assignees = ArgusScheduleAssignee.filter(schedule_id=schedule.id).all()
339
- serialized_schedule["assignees"] = [assignee.assignee for assignee in assignees]
340
- response["schedules"].append(serialized_schedule)
341
-
342
- return response
343
-
344
- def update_schedule_assignees(self, payload: dict) -> dict:
345
- """
346
- {
347
- "releaseId": "hex-uuid",
348
- "scheduleId": "hex-uuid",
349
- }
350
- """
351
- release_id = payload.get("releaseId")
352
- if not release_id:
353
- raise Exception("Release name not specified in the request")
354
-
355
- schedule_id = payload.get("scheduleId")
356
- if not schedule_id:
357
- raise Exception("No schedule Id provided")
358
-
359
- assignees = payload.get("newAssignees")
360
- if not assignees:
361
- raise Exception("No assignees provided")
362
-
363
- release = ArgusRelease.get(id=release_id)
364
- schedule = ArgusSchedule.get(release_id=release.id, id=schedule_id)
365
- schedule_tests = ArgusScheduleTest.filter(schedule_id=schedule.id).all()
366
- schedule_groups = ArgusScheduleGroup.filter(schedule_id=schedule.id).all()
367
- for test in schedule_tests:
368
- self.assign_runs_for_scheduled_test(schedule, test.test_id, UUID(assignees[0]))
369
-
370
- for group in schedule_groups:
371
- self.assign_runs_for_scheduled_group(schedule, group.group_id, UUID(assignees[0]))
372
-
373
- old_assignees = list(ArgusScheduleAssignee.filter(schedule_id=schedule.id).all())
374
- for new_assignee in assignees:
375
- assignee = ArgusScheduleAssignee()
376
- assignee.release_id = release.id
377
- assignee.schedule_id = schedule.id
378
- assignee.assignee = UUID(new_assignee)
379
- assignee.save()
380
-
381
- for old_assignee in old_assignees:
382
- old_assignee.delete()
383
-
384
- response = {
385
- "scheduleId": schedule_id,
386
- "status": "changed assignees",
387
- "newAssignees": assignees,
388
- }
389
-
390
- return response
391
-
392
- def update_schedule_comment(self, payload: dict) -> dict:
393
- new_comment = payload.get("newComment")
394
- release_id = payload.get("releaseId")
395
- group_id = payload.get("groupId")
396
- test_id = payload.get("testId")
397
-
398
- if not release_id:
399
- raise Exception("No release provided")
400
- if not group_id:
401
- raise Exception("No group provided")
402
- if not test_id:
403
- raise Exception("No test provided")
404
-
405
- if isinstance(new_comment, NoneType):
406
- raise Exception("No comment provided in the body of request")
407
-
408
- try:
409
- comment = ReleasePlannerComment.get(release=release_id, group=group_id, test=test_id)
410
- except ReleasePlannerComment.DoesNotExist:
411
- comment = ReleasePlannerComment()
412
- comment.release = release_id
413
- comment.group = group_id
414
- comment.test = test_id
415
-
416
- comment.comment = new_comment
417
- comment.save()
418
-
419
- return {
420
- "releaseId": release_id,
421
- "groupId": group_id,
422
- "testId": test_id,
423
- "newComment": new_comment,
424
- }
425
-
426
- def update_schedule(self, release_id: UUID | str, schedule_id: UUID | str, old_tests: list[UUID | str], new_tests: list[UUID | str], comments: dict[str, str], assignee: UUID | str):
427
- schedule: ArgusSchedule = ArgusSchedule.get(release_id=release_id, id=schedule_id)
428
- new_tests: set[UUID] = {UUID(id) for id in new_tests}
429
- old_tests: set[UUID] = {UUID(id) for id in old_tests}
430
-
431
- all_test_ids = old_tests.union(new_tests)
432
- tests = []
433
- for batch in chunk(all_test_ids):
434
- tests.extend(ArgusTest.filter(id__in=batch).all())
435
- tests_by_id: dict[UUID, ArgusTest] = { test.id: test for test in tests }
436
-
437
- all_scheduled_tests: list[ArgusScheduleTest] = list(ArgusScheduleTest.filter(schedule_id=schedule_id).all())
438
- tests_to_remove = all_test_ids.difference(new_tests)
439
- for scheduled_test in all_scheduled_tests:
440
- if scheduled_test.test_id in tests_to_remove:
441
- test = tests_by_id.get(scheduled_test.test_id)
442
- scheduled_test.delete()
443
- if test:
444
- self.update_schedule_comment({"newComment": "", "releaseId": test.release_id, "groupId": test.group_id, "testId": test.id})
445
-
446
- tests_to_add = new_tests.difference(old_tests)
447
- for test_id in tests_to_add:
448
- entity = ArgusScheduleTest()
449
- entity.id = uuid_from_time(schedule.period_start)
450
- entity.schedule_id = schedule.id
451
- entity.test_id = UUID(test_id) if isinstance(test_id, str) else test_id
452
- entity.release_id = release_id
453
- entity.save()
454
- self.assign_runs_for_scheduled_test(schedule, entity.test_id, assignee)
455
-
456
- for test_id, comment in comments.items():
457
- test = tests_by_id.get(UUID(test_id))
458
- if test:
459
- self.update_schedule_comment({"newComment": comment, "releaseId": test.release_id, "groupId": test.group_id, "testId": test.id})
460
-
461
- schedule_assignee: ArgusScheduleAssignee = ArgusScheduleAssignee.get(schedule_id=schedule_id)
462
- new_assignee = ArgusScheduleAssignee()
463
- new_assignee.assignee = assignee
464
- new_assignee.release_id = schedule_assignee.release_id
465
- new_assignee.schedule_id = schedule_assignee.schedule_id
466
- new_assignee.save()
467
- schedule_assignee.delete()
468
-
469
- return True
470
-
471
- def delete_schedule(self, payload: dict) -> dict:
472
- """
473
- {
474
- "release": hex-uuid,
475
- "schedule_id": uuid1,
476
- "deleteComments": bool
477
- }
478
- """
479
- release_id = payload.get("releaseId")
480
- if not release_id:
481
- raise Exception("Release name not specified in the request")
482
-
483
- schedule_id = payload.get("scheduleId")
484
- if not schedule_id:
485
- raise Exception("Schedule id not specified in the request")
486
-
487
- delete_comments = payload.get("deleteComments", False)
488
-
489
- release = ArgusRelease.get(id=release_id)
490
- schedule = ArgusSchedule.get(release_id=release.id, id=schedule_id)
491
- tests = ArgusScheduleTest.filter(schedule_id=schedule.id).all()
492
- groups = ArgusScheduleGroup.filter(schedule_id=schedule.id).all()
493
- assignees = ArgusScheduleAssignee.filter(schedule_id=schedule.id).all()
494
-
495
- full_schedule = dict(schedule)
496
- full_schedule["tests"] = [test.test_id for test in tests]
497
- full_schedule["groups"] = [group.group_id for group in groups]
498
- full_schedule["assignees"] = [assignee.assignee for assignee in assignees]
499
-
500
- if len(assignees) > 0:
501
- try:
502
- schedule_user = User.get(id=assignees[0].assignee)
503
- except User.DoesNotExist:
504
- schedule_user = User()
505
- schedule_user.id = assignees[0].assignee
506
- LOGGER.warning("Deleting orphaned user assignments")
507
- service = TestRunService()
508
-
509
- for model in all_plugin_models():
510
- for run in model.get_jobs_assigned_to_user(schedule_user.id):
511
- if run["release_id"] != release.id:
512
- continue
513
- if run["test_id"] not in full_schedule["tests"]:
514
- continue
515
- if schedule.period_start < run["start_time"] < schedule.period_end:
516
- service.change_run_assignee(test_id=run["test_id"], run_id=run["id"], new_assignee=None)
517
-
518
- for entities in [tests, groups, assignees]:
519
- for entity in entities:
520
- entity.delete()
521
-
522
- schedule.delete()
523
-
524
- if delete_comments:
525
- tests = []
526
- for batch in chunk(full_schedule["tests"]):
527
- tests.extend(ArgusTest.filter(id__in=batch).all())
528
-
529
- for test in tests:
530
- self.update_schedule_comment({"newComment": "", "releaseId": test.release_id, "groupId": test.group_id, "testId": test.id})
531
-
532
- return {
533
- "releaseId": release.id,
534
- "scheduleId": schedule_id,
535
- "result": "deleted"
536
- }
537
-
538
- def get_planner_data(self, release_id: UUID | str) -> dict:
539
-
540
- release = ArgusRelease.get(id=release_id)
541
- release_comments = list(ReleasePlannerComment.filter(release=release.id).all())
542
- groups = ArgusGroup.filter(release_id=release.id).all()
543
- groups_by_group_id = {str(group.id): dict(group.items()) for group in groups if group.enabled}
544
- tests = ArgusTest.filter(release_id=release.id).all()
545
- tests = [dict(t.items()) for t in tests if t.enabled]
546
- tests_by_group = {}
547
- for test in tests:
548
- group = groups_by_group_id.get(str(test["group_id"]))
549
- if not group:
550
- continue
551
- test["group_name"] = group["name"]
552
- test["pretty_group_name"] = groups_by_group_id[str(test["group_id"])]["pretty_name"]
553
- try:
554
- comment = next(filter(lambda c: c.test == test["id"], release_comments))
555
- except StopIteration:
556
- comment = None
557
- test["comment"] = comment.comment if comment else ""
558
- group_tests = tests_by_group.get(test["group_name"], [])
559
- group_tests.append(test)
560
- tests_by_group[test["group_name"]] = group_tests
561
-
562
- response = {
563
- "release": dict(release.items()),
564
- "groups": groups_by_group_id,
565
- "tests": tests,
566
- "tests_by_group": tests_by_group,
567
- }
568
-
569
- return response
570
-
571
- def _batch_get_schedules_from_ids(self, release_id: UUID, schedule_ids: list[UUID]) -> list[ArgusSchedule]:
572
- schedules = []
573
- step_size = 90
574
- for step in range(0, ceil(len(schedule_ids) / step_size)):
575
- start_pos = step*step_size
576
- next_slice = schedule_ids[start_pos:start_pos+step_size]
577
- schedules.extend(ArgusSchedule.filter(release_id=release_id, id__in=next_slice).all())
578
- return schedules
579
-
580
- def get_groups_assignees(self, release_id: UUID | str):
581
- release_id = UUID(release_id) if isinstance(release_id, str) else release_id
582
- release = ArgusRelease.get(id=release_id)
583
-
584
- groups = ArgusGroup.filter(release_id=release_id).all()
585
- group_ids = [group.id for group in groups if group.enabled]
586
-
587
- schedule_ids = set()
588
- group_schedules =[]
589
- step_size = 90
590
-
591
- for step in range(0, ceil(len(group_ids) / step_size)):
592
- start_pos = step*step_size
593
- next_slice = group_ids[start_pos:start_pos+step_size]
594
- group_batch = list(ArgusScheduleGroup.filter(release_id=release.id, group_id__in=next_slice).all())
595
- group_schedules.extend(group_batch)
596
- batch_ids = {schedule.schedule_id for schedule in group_batch}
597
- schedule_ids = schedule_ids.union(batch_ids)
598
-
599
- schedules = self._batch_get_schedules_from_ids(release.id, list(schedule_ids))
600
-
601
- valid_schedules = schedules
602
- if release.perpetual:
603
- today = datetime.datetime.utcnow()
604
- valid_schedules = [s for s in schedules if s.period_start <= today <= s.period_end]
605
-
606
- response = {}
607
- for schedule in valid_schedules:
608
- assignees = ArgusScheduleAssignee.filter(schedule_id=schedule.id).all()
609
- assignees_uuids = [assignee.assignee for assignee in assignees]
610
- schedule_groups = filter(lambda g: g.schedule_id == schedule.id, group_schedules)
611
- groups = {str(group.group_id): assignees_uuids for group in schedule_groups}
612
- response = {**groups, **response}
613
-
614
- return response
615
-
616
- def get_tests_assignees(self, group_id: UUID | str):
617
- group_id = UUID(group_id) if isinstance(group_id, str) else group_id
618
- group = ArgusGroup.get(id=group_id)
619
-
620
- release = ArgusRelease.get(id=group.release_id)
621
- tests = ArgusTest.filter(group_id=group_id).all()
622
-
623
- test_ids = [test.id for test in tests if test.enabled]
624
-
625
- schedule_ids = set()
626
- test_schedules = []
627
- step_size = 90
628
-
629
- for step in range(0, ceil(len(test_ids) / step_size)):
630
- start_pos = step*step_size
631
- next_slice = test_ids[start_pos:start_pos+step_size]
632
- test_batch = ArgusScheduleTest.filter(release_id=release.id, test_id__in=next_slice).all()
633
- test_schedules.extend(test_batch)
634
- batch_ids = {schedule.schedule_id for schedule in test_batch}
635
- schedule_ids = schedule_ids.union(batch_ids)
636
-
637
- schedules = self._batch_get_schedules_from_ids(release.id, list(schedule_ids))
638
-
639
- if release.perpetual:
640
- today = datetime.datetime.utcnow()
641
- schedules = list(filter(lambda s: s.period_start <= today <= s.period_end, schedules))
642
-
643
- response = {}
644
- for schedule in schedules:
645
- assignees = ArgusScheduleAssignee.filter(schedule_id=schedule.id).all()
646
- assignees_uuids = [assignee.assignee for assignee in assignees]
647
- schedule_tests = filter(lambda t: t.schedule_id == schedule.id, test_schedules)
648
- tests = {str(test.test_id): assignees_uuids for test in schedule_tests}
649
- response = {**tests, **response}
650
-
651
- return response
652
-
653
- def get_jobs_for_user(self, user: User):
654
- today = datetime.datetime.now()
655
- validity_period = today - datetime.timedelta(days=current_app.config.get("JOB_VALIDITY_PERIOD_DAYS", 30))
656
- for plugin in all_plugin_models():
657
- for run in plugin.get_jobs_assigned_to_user(user_id=user.id):
658
- if run["start_time"] >= validity_period:
659
- yield run
660
-
661
- def get_schedules_for_user(self, user: User) -> list[dict]:
662
- all_assigned_schedules = ArgusScheduleAssignee.filter(assignee=user.id).all()
663
- schedule_keys = [(schedule_assignee.release_id, schedule_assignee.schedule_id)
664
- for schedule_assignee in all_assigned_schedules]
665
- schedules = []
666
- today = datetime.datetime.utcnow()
667
- for release_id, schedule_id in schedule_keys:
668
- try:
669
- schedule = dict(ArgusSchedule.get(release_id=release_id, id=schedule_id).items())
670
- except ArgusSchedule.DoesNotExist:
671
- continue
672
- if schedule["period_start"] <= today <= schedule["period_end"]:
673
- tests = ArgusScheduleTest.filter(schedule_id=schedule_id).all()
674
- schedule["tests"] = [test.test_id for test in tests]
675
- groups = ArgusScheduleGroup.filter(schedule_id=schedule_id).all()
676
- schedule["groups"] = [group.group_id for group in groups]
677
- assignees = ArgusScheduleAssignee.filter(schedule_id=schedule_id).all()
678
- schedule["assignees"] = [assignee.assignee for assignee in assignees]
679
- schedules.append(schedule)
680
-
681
- return schedules
682
-
683
- def get_planner_comment_by_test(self, test_id):
684
- try:
685
- test = ArgusTest.get(id=test_id)
686
- return ReleasePlannerComment.get(test=test.id, release=test.release_id, group=test.group_id).comment
687
- except ReleasePlannerComment.DoesNotExist:
688
- return ""