argus-alm 0.14.2__py3-none-any.whl → 0.15.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 (118) hide show
  1. argus/_version.py +21 -0
  2. argus/backend/.gitkeep +0 -0
  3. argus/backend/__init__.py +0 -0
  4. argus/backend/cli.py +57 -0
  5. argus/backend/controller/__init__.py +0 -0
  6. argus/backend/controller/admin.py +20 -0
  7. argus/backend/controller/admin_api.py +355 -0
  8. argus/backend/controller/api.py +589 -0
  9. argus/backend/controller/auth.py +67 -0
  10. argus/backend/controller/client_api.py +109 -0
  11. argus/backend/controller/main.py +316 -0
  12. argus/backend/controller/notification_api.py +72 -0
  13. argus/backend/controller/notifications.py +13 -0
  14. argus/backend/controller/planner_api.py +194 -0
  15. argus/backend/controller/team.py +129 -0
  16. argus/backend/controller/team_ui.py +19 -0
  17. argus/backend/controller/testrun_api.py +513 -0
  18. argus/backend/controller/view_api.py +188 -0
  19. argus/backend/controller/views_widgets/__init__.py +0 -0
  20. argus/backend/controller/views_widgets/graphed_stats.py +54 -0
  21. argus/backend/controller/views_widgets/graphs.py +68 -0
  22. argus/backend/controller/views_widgets/highlights.py +135 -0
  23. argus/backend/controller/views_widgets/nemesis_stats.py +26 -0
  24. argus/backend/controller/views_widgets/summary.py +43 -0
  25. argus/backend/db.py +98 -0
  26. argus/backend/error_handlers.py +41 -0
  27. argus/backend/events/event_processors.py +34 -0
  28. argus/backend/models/__init__.py +0 -0
  29. argus/backend/models/argus_ai.py +24 -0
  30. argus/backend/models/github_issue.py +60 -0
  31. argus/backend/models/plan.py +24 -0
  32. argus/backend/models/result.py +187 -0
  33. argus/backend/models/runtime_store.py +58 -0
  34. argus/backend/models/view_widgets.py +25 -0
  35. argus/backend/models/web.py +403 -0
  36. argus/backend/plugins/__init__.py +0 -0
  37. argus/backend/plugins/core.py +248 -0
  38. argus/backend/plugins/driver_matrix_tests/controller.py +66 -0
  39. argus/backend/plugins/driver_matrix_tests/model.py +429 -0
  40. argus/backend/plugins/driver_matrix_tests/plugin.py +21 -0
  41. argus/backend/plugins/driver_matrix_tests/raw_types.py +62 -0
  42. argus/backend/plugins/driver_matrix_tests/service.py +61 -0
  43. argus/backend/plugins/driver_matrix_tests/udt.py +42 -0
  44. argus/backend/plugins/generic/model.py +86 -0
  45. argus/backend/plugins/generic/plugin.py +15 -0
  46. argus/backend/plugins/generic/types.py +14 -0
  47. argus/backend/plugins/loader.py +39 -0
  48. argus/backend/plugins/sct/controller.py +224 -0
  49. argus/backend/plugins/sct/plugin.py +37 -0
  50. argus/backend/plugins/sct/resource_setup.py +177 -0
  51. argus/backend/plugins/sct/service.py +682 -0
  52. argus/backend/plugins/sct/testrun.py +288 -0
  53. argus/backend/plugins/sct/udt.py +100 -0
  54. argus/backend/plugins/sirenada/model.py +118 -0
  55. argus/backend/plugins/sirenada/plugin.py +16 -0
  56. argus/backend/service/admin.py +26 -0
  57. argus/backend/service/argus_service.py +696 -0
  58. argus/backend/service/build_system_monitor.py +185 -0
  59. argus/backend/service/client_service.py +127 -0
  60. argus/backend/service/event_service.py +18 -0
  61. argus/backend/service/github_service.py +233 -0
  62. argus/backend/service/jenkins_service.py +269 -0
  63. argus/backend/service/notification_manager.py +159 -0
  64. argus/backend/service/planner_service.py +608 -0
  65. argus/backend/service/release_manager.py +229 -0
  66. argus/backend/service/results_service.py +690 -0
  67. argus/backend/service/stats.py +610 -0
  68. argus/backend/service/team_manager_service.py +82 -0
  69. argus/backend/service/test_lookup.py +172 -0
  70. argus/backend/service/testrun.py +489 -0
  71. argus/backend/service/user.py +308 -0
  72. argus/backend/service/views.py +219 -0
  73. argus/backend/service/views_widgets/__init__.py +0 -0
  74. argus/backend/service/views_widgets/graphed_stats.py +180 -0
  75. argus/backend/service/views_widgets/highlights.py +374 -0
  76. argus/backend/service/views_widgets/nemesis_stats.py +34 -0
  77. argus/backend/template_filters.py +27 -0
  78. argus/backend/tests/__init__.py +0 -0
  79. argus/backend/tests/client_service/__init__.py +0 -0
  80. argus/backend/tests/client_service/test_submit_results.py +79 -0
  81. argus/backend/tests/conftest.py +180 -0
  82. argus/backend/tests/results_service/__init__.py +0 -0
  83. argus/backend/tests/results_service/test_best_results.py +178 -0
  84. argus/backend/tests/results_service/test_cell.py +65 -0
  85. argus/backend/tests/results_service/test_chartjs_additional_functions.py +259 -0
  86. argus/backend/tests/results_service/test_create_chartjs.py +220 -0
  87. argus/backend/tests/results_service/test_result_metadata.py +100 -0
  88. argus/backend/tests/results_service/test_results_service.py +203 -0
  89. argus/backend/tests/results_service/test_validation_rules.py +213 -0
  90. argus/backend/tests/view_widgets/__init__.py +0 -0
  91. argus/backend/tests/view_widgets/test_highlights_api.py +532 -0
  92. argus/backend/util/common.py +65 -0
  93. argus/backend/util/config.py +38 -0
  94. argus/backend/util/encoders.py +56 -0
  95. argus/backend/util/logsetup.py +80 -0
  96. argus/backend/util/module_loaders.py +30 -0
  97. argus/backend/util/send_email.py +91 -0
  98. argus/client/base.py +1 -3
  99. argus/client/driver_matrix_tests/cli.py +17 -8
  100. argus/client/generic/cli.py +4 -2
  101. argus/client/generic/client.py +1 -0
  102. argus/client/generic_result.py +48 -9
  103. argus/client/sct/client.py +1 -3
  104. argus/client/sirenada/client.py +4 -1
  105. argus/client/tests/__init__.py +0 -0
  106. argus/client/tests/conftest.py +19 -0
  107. argus/client/tests/test_package.py +45 -0
  108. argus/client/tests/test_results.py +224 -0
  109. argus/common/sct_types.py +3 -0
  110. argus/common/sirenada_types.py +1 -1
  111. {argus_alm-0.14.2.dist-info → argus_alm-0.15.1.dist-info}/METADATA +43 -19
  112. argus_alm-0.15.1.dist-info/RECORD +122 -0
  113. {argus_alm-0.14.2.dist-info → argus_alm-0.15.1.dist-info}/WHEEL +2 -1
  114. argus_alm-0.15.1.dist-info/entry_points.txt +3 -0
  115. argus_alm-0.15.1.dist-info/top_level.txt +1 -0
  116. argus_alm-0.14.2.dist-info/RECORD +0 -20
  117. argus_alm-0.14.2.dist-info/entry_points.txt +0 -4
  118. {argus_alm-0.14.2.dist-info → argus_alm-0.15.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,109 @@
1
+ from flask import Blueprint, request
2
+
3
+ from argus.backend.error_handlers import handle_api_exception
4
+ from argus.backend.service.user import api_login_required
5
+ from argus.backend.service.client_service import ClientService
6
+ from argus.backend.util.common import get_payload
7
+ from argus.backend.plugins.loader import AVAILABLE_PLUGINS
8
+
9
+ bp = Blueprint("client_api", __name__, url_prefix="/client")
10
+ bp.register_error_handler(Exception, handle_api_exception)
11
+ for plugin in AVAILABLE_PLUGINS.values():
12
+ if plugin.controller:
13
+ bp.register_blueprint(plugin.controller)
14
+
15
+
16
+ @bp.route("/testrun/<string:run_type>/submit", methods=["POST"])
17
+ @api_login_required
18
+ def submit_run(run_type: str):
19
+ payload = get_payload(request)
20
+ result = ClientService().submit_run(run_type=run_type, request_data=payload)
21
+ return {
22
+ "status": "ok",
23
+ "response": result
24
+ }
25
+
26
+
27
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/get", methods=["GET"])
28
+ @api_login_required
29
+ def get_run(run_type: str, run_id: str):
30
+ result = ClientService().get_run(run_type=run_type, run_id=run_id)
31
+ return {
32
+ "status": "ok",
33
+ "response": result
34
+ }
35
+
36
+
37
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/heartbeat", methods=["POST"])
38
+ @api_login_required
39
+ def run_heartbeat(run_type: str, run_id: str):
40
+ result = ClientService().heartbeat(run_type=run_type, run_id=run_id)
41
+ return {
42
+ "status": "ok",
43
+ "response": result
44
+ }
45
+
46
+
47
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/get_status")
48
+ @api_login_required
49
+ def run_get_status(run_type: str, run_id: str):
50
+ result = ClientService().get_run_status(run_type=run_type, run_id=run_id)
51
+ return {
52
+ "status": "ok",
53
+ "response": result
54
+ }
55
+
56
+
57
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/set_status", methods=["POST"])
58
+ @api_login_required
59
+ def run_set_status(run_type: str, run_id: str):
60
+ payload = get_payload(request)
61
+ result = ClientService().update_run_status(run_type=run_type, run_id=run_id, new_status=payload["new_status"])
62
+ return {
63
+ "status": "ok",
64
+ "response": result
65
+ }
66
+
67
+
68
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/update_product_version", methods=["POST"])
69
+ @api_login_required
70
+ def run_update_product_version(run_type: str, run_id: str):
71
+ payload = get_payload(request)
72
+ result = ClientService().submit_product_version(
73
+ run_type=run_type, run_id=run_id, version=payload["product_version"])
74
+ return {
75
+ "status": "ok",
76
+ "response": result
77
+ }
78
+
79
+
80
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/logs/submit", methods=["POST"])
81
+ @api_login_required
82
+ def run_submit_logs(run_type: str, run_id: str):
83
+ payload = get_payload(request)
84
+ result = ClientService().submit_logs(run_type=run_type, run_id=run_id, logs=payload["logs"])
85
+ return {
86
+ "status": "ok",
87
+ "response": result
88
+ }
89
+
90
+
91
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/finalize", methods=["POST"])
92
+ @api_login_required
93
+ def run_finalize(run_type: str, run_id: str):
94
+ try:
95
+ payload = get_payload(request)
96
+ except Exception:
97
+ payload = None
98
+ result = ClientService().finish_run(run_type=run_type, run_id=run_id, payload=payload)
99
+ return {
100
+ "status": "ok",
101
+ "response": result
102
+ }
103
+
104
+
105
+ @bp.route("/testrun/<string:run_type>/<string:run_id>/submit_results", methods=["POST"])
106
+ @api_login_required
107
+ def submit_results(run_type: str, run_id: str):
108
+ payload = get_payload(request)
109
+ return ClientService().submit_results(run_type=run_type, run_id=run_id, results=payload)
@@ -0,0 +1,316 @@
1
+ import datetime
2
+ import json
3
+ import logging
4
+ from uuid import UUID
5
+ from flask import (
6
+ Blueprint, flash, g, redirect, render_template, request, session, url_for, make_response
7
+ )
8
+ from argus.backend.controller.notifications import bp as notifications_bp
9
+ from argus.backend.controller.team_ui import bp as teams_bp
10
+ from argus.backend.service.argus_service import ArgusService
11
+ from argus.backend.models.web import ArgusRelease, WebFileStorage
12
+ from argus.backend.service.testrun import TestRunService
13
+ from argus.backend.service.planner_service import PlanningService
14
+ from argus.backend.service.user import UserService, login_required
15
+ from argus.backend.service.views import UserViewService
16
+
17
+ LOGGER = logging.getLogger(__name__)
18
+
19
+ bp = Blueprint('main', __name__)
20
+ bp.register_blueprint(notifications_bp)
21
+ bp.register_blueprint(teams_bp)
22
+
23
+
24
+ @bp.route("/test_runs")
25
+ @login_required
26
+ def test_runs():
27
+ return render_template("test_runs.html.j2")
28
+
29
+
30
+ @bp.route("/test_run/<string:run_id>")
31
+ @login_required
32
+ def test_run(run_id: UUID):
33
+ return render_template("test_run.html.j2", id=run_id)
34
+
35
+
36
+ @bp.route("/test/<string:test_id>/runs")
37
+ @login_required
38
+ def runs(test_id: UUID):
39
+ additional_runs = request.args.getlist("additionalRuns[]")
40
+ return render_template("standalone_test_with_runs.html.j2", test_id=test_id, additional_runs=additional_runs)
41
+
42
+
43
+ @bp.route("/tests/<string:plugin_name>/<string:run_id>", defaults={"tab": "details"})
44
+ @bp.route("/tests/<string:plugin_name>/<string:run_id>/<string:tab>")
45
+ @login_required
46
+ def get_run_by_plugin(plugin_name: str, run_id: UUID | str, tab: str):
47
+ try:
48
+ run_id = UUID(run_id)
49
+ except ValueError:
50
+ flash(message=f"Invalid UUID: {run_id}", category="error")
51
+ return redirect(url_for("main.error", type=404))
52
+ run = TestRunService().get_run(plugin_name, run_id)
53
+ if not run:
54
+ flash(f"Run {plugin_name}/{run_id} not found.", "error")
55
+ return redirect(url_for("main.error", type=404))
56
+ return render_template("run_view_by_plugin.html.j2", run=run, tab=tab)
57
+
58
+
59
+ @bp.route("/")
60
+ def home():
61
+ return redirect(url_for("main.run_dashboard"))
62
+
63
+
64
+ @bp.route("/run_dashboard")
65
+ @bp.route("/workspace")
66
+ @login_required
67
+ def run_dashboard():
68
+ return render_template('dashboard.html.j2')
69
+
70
+
71
+ @bp.route("/releases")
72
+ @login_required
73
+ def releases():
74
+ service = ArgusService()
75
+ all_releases = service.get_releases()
76
+ return render_template("releases.html.j2", releases=all_releases)
77
+
78
+
79
+ @bp.route("/views")
80
+ @login_required
81
+ def views():
82
+ service = UserViewService()
83
+ all_views = service.get_all_views()
84
+ return render_template("views.html.j2", views=sorted(all_views, key=lambda view: view.created or datetime.datetime.fromtimestamp(0), reverse=True))
85
+
86
+
87
+ @bp.route("/view/<string:view_name>")
88
+ @login_required
89
+ def view_dashboard(view_name: str):
90
+ service = UserViewService()
91
+ view = service.get_view_by_name(view_name=view_name)
92
+ data_json = view
93
+ view["widget_settings"] = json.loads(view["widget_settings"])
94
+ return render_template("view_dashboard.html.j2", data=data_json)
95
+
96
+
97
+ @bp.route("/plan/<string:plan_id>")
98
+ @login_required
99
+ def plan_dashboard(plan_id: str):
100
+ service = PlanningService()
101
+ plan = service.get_plan(plan_id=plan_id)
102
+ data_json = plan
103
+ return render_template("plan_dashboard.html.j2", data=data_json)
104
+
105
+
106
+ @bp.route("/alert_debug")
107
+ @login_required
108
+ def alert_debug():
109
+ alert_type = request.args.get("type", "success")
110
+ message = request.args.get("message", "No message provided")
111
+ flash(message=message, category=alert_type)
112
+ return render_template("flash_debug.html.j2")
113
+
114
+
115
+ @bp.route("/dashboard/<string:release_name>")
116
+ @login_required
117
+ def release_dashboard(release_name: str):
118
+ service = ArgusService()
119
+ release, release_groups, release_tests = service.get_data_for_release_dashboard(
120
+ release_name=release_name)
121
+ data_json = {
122
+ "release": dict(release.items()),
123
+ "groups": [dict(group.items()) for group in release_groups],
124
+ "tests": [dict(test.items()) for test in release_tests],
125
+ }
126
+ return render_template("release_dashboard.html.j2", release_name=release_name, data=data_json)
127
+
128
+
129
+ @bp.route("/release/<string:name>/scheduler")
130
+ @login_required
131
+ def release_scheduler(name: str):
132
+ service = ArgusService()
133
+ release, release_groups, release_tests = service.get_data_for_release_dashboard(
134
+ release_name=name)
135
+ data_json = {
136
+ "release": dict(release.items()),
137
+ "groups": [dict(group.items()) for group in release_groups],
138
+ "tests": [dict(test.items()) for test in release_tests],
139
+ }
140
+ return render_template("release_schedule.html.j2", release_name=name, data=data_json)
141
+
142
+
143
+ @bp.route("/release/by-id/<string:id>/planner")
144
+ @login_required
145
+ def release_planner_by_id(id: str):
146
+ release = ArgusRelease.get(id=id)
147
+ return redirect(url_for("main.release_planner", name=release.name))
148
+
149
+
150
+ @bp.route("/release/<string:name>/planner")
151
+ @login_required
152
+ def release_planner(name: str):
153
+ service = PlanningService()
154
+ planner_data = service.release_planner(name)
155
+ return render_template("release_planner.html.j2", release_name=planner_data["release"]["name"], planner_data=planner_data)
156
+
157
+
158
+ @bp.route("/release/<string:name>/duty")
159
+ @login_required
160
+ def duty_planner(name: str):
161
+ service = ArgusService()
162
+ release, release_groups, release_tests = service.get_data_for_release_dashboard(
163
+ release_name=name)
164
+ data_json = {
165
+ "release": dict(release.items()),
166
+ "groups": [dict(group.items()) for group in release_groups],
167
+ "tests": [dict(test.items()) for test in release_tests],
168
+ }
169
+ return render_template("duty_planner.html.j2", release_name=name, data=data_json)
170
+
171
+
172
+ @bp.route("/error/")
173
+ def error():
174
+ return render_template("error.html.j2", type=request.args.get("type", 400))
175
+
176
+
177
+ @bp.route("/profile/")
178
+ @login_required
179
+ def profile():
180
+ first_run = session.pop("first_run_info", None)
181
+ token_generated = session.pop("token_generated", None)
182
+
183
+ return render_template("profile.html.j2", first_run=first_run, token_generated=token_generated)
184
+
185
+
186
+ @bp.route("/profile/oauth/github", methods=["GET"])
187
+ def profile_oauth_github_callback():
188
+ req_state = request.args.get('state', '')
189
+ if req_state != session["csrf_token"]:
190
+ return redirect(url_for("main.error", type=403))
191
+
192
+ req_code = request.args.get("code", "WTF")
193
+ service = UserService()
194
+ try:
195
+ first_run_info = service.github_callback(req_code)
196
+ except Exception as exc:
197
+ LOGGER.error("An error occured in callback", exc_info=True)
198
+ flash(message=exc.args[0], category="error")
199
+ return redirect(url_for("main.error", type=403))
200
+ if first_run_info:
201
+ session["first_run_info"] = first_run_info
202
+
203
+ if path := session.pop("redirect_target"):
204
+ return redirect(path)
205
+ return redirect(url_for("main.profile"))
206
+
207
+
208
+ @bp.route("/storage/picture/<string:picture_id>")
209
+ @login_required
210
+ def get_picture(picture_id: str):
211
+ res = make_response()
212
+ try:
213
+ picture = WebFileStorage.get(id=picture_id)
214
+ with open(picture.filepath, "rb") as file:
215
+ res.set_data(file.read())
216
+ res.content_type = "image/*"
217
+ res.status = 200
218
+ except FileNotFoundError:
219
+ res.status = 404
220
+ res.content_type = "text/plain"
221
+ res.set_data("404 NOT FOUND")
222
+
223
+ res.cache_control.max_age = 86400
224
+ res.cache_control.public = True
225
+ return res
226
+
227
+
228
+ @bp.route("/profile/update/picture", methods=["POST"])
229
+ @login_required
230
+ def upload_file():
231
+ req_file = request.files.get("filedata")
232
+ picture_data = req_file.stream.read()
233
+ picture_name = req_file.filename
234
+ if not req_file.content_type.startswith("image/"):
235
+ flash(
236
+ message=f"Expected image/*, got {req_file.content_type}", category="error")
237
+ return redirect(url_for("main.profile"))
238
+ if not picture_data:
239
+ flash(message="No picture provided", category="error")
240
+ return redirect(url_for("main.profile"))
241
+
242
+ service = UserService()
243
+ filename, filepath = service.save_profile_picture_to_disk(
244
+ picture_name, picture_data, g.user.username)
245
+ service.update_profile_picture(filename, filepath)
246
+
247
+ return redirect(url_for("main.profile"))
248
+
249
+
250
+ @bp.route("/profile/update/name", methods=["POST"])
251
+ @login_required
252
+ def update_full_name():
253
+ new_name = request.values.get("new_name")
254
+ if not new_name:
255
+ flash(message="Incorrect new name", category="error")
256
+ else:
257
+ service = UserService()
258
+ service.update_name(g.user, new_name)
259
+ flash("Successfully changed name!", category="success")
260
+ return redirect(url_for("main.profile"))
261
+
262
+
263
+ @bp.route("/profile/update/email", methods=["POST"])
264
+ @login_required
265
+ def update_email():
266
+ new_email = request.values.get("new_email")
267
+ if not new_email:
268
+ flash("Incorrect new email", category="error")
269
+ else:
270
+ service = UserService()
271
+ service.update_email(g.user, new_email)
272
+ flash("Successfully changed email!", category="success")
273
+ return redirect(url_for("main.profile"))
274
+
275
+
276
+ @bp.route("/profile/update/password", methods=["POST"])
277
+ @login_required
278
+ def update_password():
279
+ old_password = request.values.get("old_password")
280
+ new_password = request.values.get("new_password")
281
+ new_password_confirm = request.values.get("new_password_confirm")
282
+ if not old_password:
283
+ flash("Old password wasn't provided", category="error")
284
+ return redirect(url_for("main.profile"))
285
+ if not new_password:
286
+ flash("New password wasn't provided", category="error")
287
+ return redirect(url_for("main.profile"))
288
+
289
+ if not new_password == new_password_confirm:
290
+ flash("New password doesn't match confirmation!", category="error")
291
+ return redirect(url_for("main.profile"))
292
+
293
+ service = UserService()
294
+ try:
295
+ service.update_password(
296
+ g.user, old_password=old_password, new_password=new_password)
297
+ except Exception:
298
+ flash("Old password is incorrect", category="error")
299
+ return redirect(url_for("main.profile"))
300
+
301
+ flash("Successfully changed password!")
302
+ return redirect(url_for("main.profile"))
303
+
304
+
305
+ @bp.route("/profile/jobs", methods=["GET"])
306
+ @login_required
307
+ def profile_jobs():
308
+ return render_template("profile_jobs.html.j2")
309
+
310
+
311
+ @bp.route("/profile/schedules", methods=["GET"])
312
+ @login_required
313
+ def profile_schedules():
314
+ service = ArgusService()
315
+ schedules = service.get_schedules_for_user(g.user)
316
+ return render_template("profile_schedules.html.j2", schedules=schedules)
@@ -0,0 +1,72 @@
1
+ import logging
2
+ from flask import (
3
+ Blueprint,
4
+ g,
5
+ request,
6
+ )
7
+ from argus.backend.error_handlers import handle_api_exception
8
+ from argus.backend.service.notification_manager import NotificationManagerService
9
+ from argus.backend.service.user import api_login_required
10
+ from argus.backend.util.common import get_payload
11
+
12
+ bp = Blueprint('notifications', __name__, url_prefix='/notifications')
13
+ LOGGER = logging.getLogger(__name__)
14
+ bp.register_error_handler(Exception, handle_api_exception)
15
+
16
+
17
+ @bp.route("/get")
18
+ @api_login_required
19
+ def get_notification():
20
+ notification_id = request.args.get("id")
21
+ if not notification_id:
22
+ raise Exception("No notification id provided")
23
+ service = NotificationManagerService()
24
+ notification = service.get_notificaton(
25
+ receiver=g.user.id, notification_id=notification_id)
26
+ return {
27
+ "status": "ok",
28
+ "response": notification.to_dict()
29
+ }
30
+
31
+
32
+ @bp.route("/get_unread")
33
+ @api_login_required
34
+ def get_unread_count():
35
+ service = NotificationManagerService()
36
+ unread_count = service.get_unread_count(receiver=g.user.id)
37
+ return {
38
+ "status": "ok",
39
+ "response": unread_count
40
+ }
41
+
42
+
43
+ @bp.route("/summary")
44
+ @api_login_required
45
+ def get_summary():
46
+ after = request.args.get("afterId")
47
+ limit = request.args.get("limit")
48
+ limit = int(limit) if limit else 20
49
+ service = NotificationManagerService()
50
+ notifications = service.get_notifications(
51
+ receiver=g.user.id,
52
+ limit=limit,
53
+ after=after
54
+ )
55
+ return {
56
+ "status": "ok",
57
+ "response": [n.to_dict_short_summary() for n in notifications]
58
+ }
59
+
60
+
61
+ @bp.route("/read", methods=["POST"])
62
+ @api_login_required
63
+ def read_notification():
64
+ payload = get_payload(request)
65
+ service = NotificationManagerService()
66
+ status = service.read_notification(
67
+ receiver=g.user.id, notification_id=payload["id"])
68
+
69
+ return {
70
+ "status": "ok",
71
+ "response": status
72
+ }
@@ -0,0 +1,13 @@
1
+ from flask import (
2
+ Blueprint,
3
+ render_template,
4
+ )
5
+ from argus.backend.service.user import login_required
6
+
7
+ bp = Blueprint('notifications', __name__, url_prefix='/notifications')
8
+
9
+
10
+ @bp.route("/")
11
+ @login_required
12
+ def index():
13
+ return render_template("profile_notifications.html.j2")
@@ -0,0 +1,194 @@
1
+ import logging
2
+ from uuid import UUID
3
+ from flask import (
4
+ Blueprint,
5
+ request
6
+ )
7
+ from argus.backend.error_handlers import handle_api_exception
8
+ from argus.backend.service.planner_service import CopyPlanPayload, PlanningService, TempPlanPayload
9
+ from argus.backend.service.test_lookup import TestLookup
10
+ from argus.backend.service.user import api_login_required
11
+ from argus.backend.util.common import get_payload
12
+
13
+ bp = Blueprint('planning_api', __name__, url_prefix='/planning')
14
+ LOGGER = logging.getLogger(__name__)
15
+ bp.register_error_handler(Exception, handle_api_exception)
16
+
17
+
18
+ @bp.route("/", methods=["GET"])
19
+ @api_login_required
20
+ def version():
21
+
22
+ result = PlanningService().version()
23
+
24
+ return {
25
+ "status": "ok",
26
+ "response": result
27
+ }
28
+
29
+
30
+ @bp.route("/plan/<string:plan_id>/copy/check", methods=["GET"])
31
+ @api_login_required
32
+ def is_plan_eligible_for_copy(plan_id: str):
33
+ release_id = request.args.get("releaseId")
34
+ if not release_id:
35
+ raise Exception("Missing release id.")
36
+
37
+ result = PlanningService().check_plan_copy_eligibility(plan_id=UUID(plan_id), target_release_id=UUID(release_id))
38
+
39
+ return {
40
+ "status": "ok",
41
+ "response": result
42
+ }
43
+
44
+
45
+ @bp.route("/release/<string:release_id>/gridview", methods=["GET"])
46
+ @api_login_required
47
+ def grid_view_for_release(release_id: str):
48
+
49
+ result = PlanningService().get_gridview_for_release(release_id=UUID(release_id))
50
+
51
+ return {
52
+ "status": "ok",
53
+ "response": result
54
+ }
55
+
56
+
57
+ @bp.route("/search", methods=["GET"])
58
+ @api_login_required
59
+ def search_tests():
60
+ query = request.args.get("query")
61
+ release_id = request.args.get('releaseId')
62
+ service = TestLookup
63
+ if query:
64
+ res = service.test_lookup(query, release_id=release_id)
65
+ else:
66
+ res = []
67
+ return {
68
+ "status": "ok",
69
+ "response": {
70
+ "hits": res,
71
+ "total": len(res)
72
+ }
73
+ }
74
+
75
+
76
+ @bp.route("/group/<string:group_id>/explode", methods=["GET"])
77
+ @api_login_required
78
+ def explode_group(group_id: str):
79
+ service = TestLookup
80
+ res = service.explode_group(group_id=group_id)
81
+ return {
82
+ "status": "ok",
83
+ "response": res
84
+ }
85
+
86
+
87
+ @bp.route("/plan/<string:plan_id>/get", methods=["GET"])
88
+ @api_login_required
89
+ def get_plan(plan_id: str):
90
+ result = PlanningService().get_plan(plan_id)
91
+
92
+ return {
93
+ "status": "ok",
94
+ "response": result
95
+ }
96
+
97
+
98
+ @bp.route("/release/<string:release_id>/all", methods=["GET"])
99
+ @api_login_required
100
+ def get_plans_for_release(release_id: str):
101
+ result = PlanningService().get_plans_for_release(release_id)
102
+
103
+ return {
104
+ "status": "ok",
105
+ "response": result
106
+ }
107
+
108
+
109
+ @bp.route("/plan/create", methods=["POST"])
110
+ @api_login_required
111
+ def create_plan():
112
+ payload = get_payload(request)
113
+ result = PlanningService().create_plan(payload)
114
+
115
+ return {
116
+ "status": "ok",
117
+ "response": result
118
+ }
119
+
120
+
121
+ @bp.route("/plan/update", methods=["POST"])
122
+ @api_login_required
123
+ def update_plan():
124
+ payload = get_payload(request)
125
+ result = PlanningService().update_plan(payload)
126
+
127
+ return {
128
+ "status": "ok",
129
+ "response": result
130
+ }
131
+
132
+
133
+ @bp.route("/plan/copy", methods=["POST"])
134
+ @api_login_required
135
+ def copy_plan():
136
+ payload = get_payload(request)
137
+ payload["plan"] = TempPlanPayload(**payload["plan"])
138
+ result = PlanningService().copy_plan(CopyPlanPayload(**payload))
139
+
140
+ return {
141
+ "status": "ok",
142
+ "response": result
143
+ }
144
+
145
+
146
+ @bp.route("/plan/<string:plan_id>/delete", methods=["DELETE"])
147
+ @api_login_required
148
+ def delete_plan(plan_id: str):
149
+ delete_view = bool(int(request.args.get("deleteView", "0")))
150
+ result = PlanningService().delete_plan(plan_id, delete_view=delete_view)
151
+
152
+ return {
153
+ "status": "ok",
154
+ "response": result
155
+ }
156
+
157
+
158
+ @bp.route("/plan/<string:plan_id>/owner/set", methods=["POST"])
159
+ @api_login_required
160
+ def change_plan_owner(plan_id: str):
161
+ payload = get_payload(request)
162
+ result = PlanningService().change_plan_owner(plan_id=plan_id, new_owner=payload["newOwner"])
163
+
164
+ return {
165
+ "status": "ok",
166
+ "response": result
167
+ }
168
+
169
+
170
+ @bp.route("/plan/<string:plan_id>/resolve_entities", methods=["GET"])
171
+ @api_login_required
172
+ def resolve_plan_entities(plan_id: str):
173
+
174
+ service = PlanningService()
175
+ result = service.resolve_plan(plan_id)
176
+
177
+ return {
178
+ "status": "ok",
179
+ "response": result,
180
+ }
181
+
182
+
183
+ @bp.route("/plan/trigger", methods=["POST"])
184
+ @api_login_required
185
+ def trigger_jobs_for_plans():
186
+
187
+ payload = get_payload(request)
188
+ service = PlanningService()
189
+ result = service.trigger_jobs(payload)
190
+
191
+ return {
192
+ "status": "ok",
193
+ "response": result,
194
+ }