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.
- argus/_version.py +21 -0
- argus/backend/.gitkeep +0 -0
- argus/backend/__init__.py +0 -0
- argus/backend/cli.py +57 -0
- argus/backend/controller/__init__.py +0 -0
- argus/backend/controller/admin.py +20 -0
- argus/backend/controller/admin_api.py +355 -0
- argus/backend/controller/api.py +589 -0
- argus/backend/controller/auth.py +67 -0
- argus/backend/controller/client_api.py +109 -0
- argus/backend/controller/main.py +316 -0
- argus/backend/controller/notification_api.py +72 -0
- argus/backend/controller/notifications.py +13 -0
- argus/backend/controller/planner_api.py +194 -0
- argus/backend/controller/team.py +129 -0
- argus/backend/controller/team_ui.py +19 -0
- argus/backend/controller/testrun_api.py +513 -0
- argus/backend/controller/view_api.py +188 -0
- argus/backend/controller/views_widgets/__init__.py +0 -0
- argus/backend/controller/views_widgets/graphed_stats.py +54 -0
- argus/backend/controller/views_widgets/graphs.py +68 -0
- argus/backend/controller/views_widgets/highlights.py +135 -0
- argus/backend/controller/views_widgets/nemesis_stats.py +26 -0
- argus/backend/controller/views_widgets/summary.py +43 -0
- argus/backend/db.py +98 -0
- argus/backend/error_handlers.py +41 -0
- argus/backend/events/event_processors.py +34 -0
- argus/backend/models/__init__.py +0 -0
- argus/backend/models/argus_ai.py +24 -0
- argus/backend/models/github_issue.py +60 -0
- argus/backend/models/plan.py +24 -0
- argus/backend/models/result.py +187 -0
- argus/backend/models/runtime_store.py +58 -0
- argus/backend/models/view_widgets.py +25 -0
- argus/backend/models/web.py +403 -0
- argus/backend/plugins/__init__.py +0 -0
- argus/backend/plugins/core.py +248 -0
- argus/backend/plugins/driver_matrix_tests/controller.py +66 -0
- argus/backend/plugins/driver_matrix_tests/model.py +429 -0
- argus/backend/plugins/driver_matrix_tests/plugin.py +21 -0
- argus/backend/plugins/driver_matrix_tests/raw_types.py +62 -0
- argus/backend/plugins/driver_matrix_tests/service.py +61 -0
- argus/backend/plugins/driver_matrix_tests/udt.py +42 -0
- argus/backend/plugins/generic/model.py +86 -0
- argus/backend/plugins/generic/plugin.py +15 -0
- argus/backend/plugins/generic/types.py +14 -0
- argus/backend/plugins/loader.py +39 -0
- argus/backend/plugins/sct/controller.py +224 -0
- argus/backend/plugins/sct/plugin.py +37 -0
- argus/backend/plugins/sct/resource_setup.py +177 -0
- argus/backend/plugins/sct/service.py +682 -0
- argus/backend/plugins/sct/testrun.py +288 -0
- argus/backend/plugins/sct/udt.py +100 -0
- argus/backend/plugins/sirenada/model.py +118 -0
- argus/backend/plugins/sirenada/plugin.py +16 -0
- argus/backend/service/admin.py +26 -0
- argus/backend/service/argus_service.py +696 -0
- argus/backend/service/build_system_monitor.py +185 -0
- argus/backend/service/client_service.py +127 -0
- argus/backend/service/event_service.py +18 -0
- argus/backend/service/github_service.py +233 -0
- argus/backend/service/jenkins_service.py +269 -0
- argus/backend/service/notification_manager.py +159 -0
- argus/backend/service/planner_service.py +608 -0
- argus/backend/service/release_manager.py +229 -0
- argus/backend/service/results_service.py +690 -0
- argus/backend/service/stats.py +610 -0
- argus/backend/service/team_manager_service.py +82 -0
- argus/backend/service/test_lookup.py +172 -0
- argus/backend/service/testrun.py +489 -0
- argus/backend/service/user.py +308 -0
- argus/backend/service/views.py +219 -0
- argus/backend/service/views_widgets/__init__.py +0 -0
- argus/backend/service/views_widgets/graphed_stats.py +180 -0
- argus/backend/service/views_widgets/highlights.py +374 -0
- argus/backend/service/views_widgets/nemesis_stats.py +34 -0
- argus/backend/template_filters.py +27 -0
- argus/backend/tests/__init__.py +0 -0
- argus/backend/tests/client_service/__init__.py +0 -0
- argus/backend/tests/client_service/test_submit_results.py +79 -0
- argus/backend/tests/conftest.py +180 -0
- argus/backend/tests/results_service/__init__.py +0 -0
- argus/backend/tests/results_service/test_best_results.py +178 -0
- argus/backend/tests/results_service/test_cell.py +65 -0
- argus/backend/tests/results_service/test_chartjs_additional_functions.py +259 -0
- argus/backend/tests/results_service/test_create_chartjs.py +220 -0
- argus/backend/tests/results_service/test_result_metadata.py +100 -0
- argus/backend/tests/results_service/test_results_service.py +203 -0
- argus/backend/tests/results_service/test_validation_rules.py +213 -0
- argus/backend/tests/view_widgets/__init__.py +0 -0
- argus/backend/tests/view_widgets/test_highlights_api.py +532 -0
- argus/backend/util/common.py +65 -0
- argus/backend/util/config.py +38 -0
- argus/backend/util/encoders.py +56 -0
- argus/backend/util/logsetup.py +80 -0
- argus/backend/util/module_loaders.py +30 -0
- argus/backend/util/send_email.py +91 -0
- argus/client/base.py +1 -3
- argus/client/driver_matrix_tests/cli.py +17 -8
- argus/client/generic/cli.py +4 -2
- argus/client/generic/client.py +1 -0
- argus/client/generic_result.py +48 -9
- argus/client/sct/client.py +1 -3
- argus/client/sirenada/client.py +4 -1
- argus/client/tests/__init__.py +0 -0
- argus/client/tests/conftest.py +19 -0
- argus/client/tests/test_package.py +45 -0
- argus/client/tests/test_results.py +224 -0
- argus/common/sct_types.py +3 -0
- argus/common/sirenada_types.py +1 -1
- {argus_alm-0.14.2.dist-info → argus_alm-0.15.1.dist-info}/METADATA +43 -19
- argus_alm-0.15.1.dist-info/RECORD +122 -0
- {argus_alm-0.14.2.dist-info → argus_alm-0.15.1.dist-info}/WHEEL +2 -1
- argus_alm-0.15.1.dist-info/entry_points.txt +3 -0
- argus_alm-0.15.1.dist-info/top_level.txt +1 -0
- argus_alm-0.14.2.dist-info/RECORD +0 -20
- argus_alm-0.14.2.dist-info/entry_points.txt +0 -4
- {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
|
+
}
|