Commit | Line | Data |
---|---|---|
8fca1ec2 | 1 | import datetime |
31329c3f TP |
2 | import os |
3 | ||
4 | import requests | |
9421395c | 5 | from flask import Flask, render_template, request, abort, jsonify |
8fca1ec2 | 6 | from flask_bootstrap import Bootstrap |
31329c3f TP |
7 | from flask_caching import Cache |
8 | from flask_migrate import Migrate | |
8fca1ec2 TP |
9 | from flask_nav import Nav |
10 | from flask_nav.elements import Navbar, Text, View | |
31329c3f TP |
11 | from flask_sqlalchemy import SQLAlchemy |
12 | ||
9421395c TP |
13 | from sqlalchemy import orm, func |
14 | ||
31329c3f TP |
15 | from ui import gitlab, config, models |
16 | ||
17 | app = Flask(__name__) | |
8fca1ec2 | 18 | Bootstrap(app) |
31329c3f TP |
19 | app.config.from_object(config) |
20 | cache = Cache(app) | |
21 | models.db.init_app(app) | |
22 | migrate = Migrate(app, models.db) | |
8fca1ec2 | 23 | nav = Nav(app) |
31329c3f | 24 | |
8fca1ec2 TP |
25 | nav.register_element('top', Navbar( |
26 | "LineageOS Builds", | |
9421395c TP |
27 | View('Builds', '.web_index'), |
28 | View('Runners', '.web_runners'), | |
29 | View('Stats', '.web_stats') | |
8fca1ec2 | 30 | )) |
31329c3f TP |
31 | |
32 | headers = {'Private-Token': os.environ.get('GITLAB_TOKEN', '')} | |
33 | ||
60410d60 | 34 | def parse_args(): |
8fca1ec2 TP |
35 | args = {} |
36 | if request.args: | |
37 | if 'status' in request.args: | |
38 | args['build_status'] = request.args.get('status') | |
39 | if 'device' in request.args: | |
40 | args['build_device'] = request.args.get('device') | |
41 | if 'version' in request.args: | |
42 | args['build_version'] = request.args.get('version') | |
43 | if 'type' in request.args: | |
44 | args['build_type'] = request.args.get('type') | |
45 | if 'date' in request.args: | |
60410d60 TP |
46 | date = datetime.datetime.strptime(request.args.get('date'), '%Y-%m-%d').date() |
47 | args['build_date'] = datetime.datetime.strptime(request.args.get('date'), '%Y-%m-%d').date() | |
48 | return args | |
49 | ||
9421395c TP |
50 | def stats(): |
51 | ||
52 | runner_build_times = models.Build.query.join(models.Build.build_runner).with_entities( | |
53 | models.Runner.runner_name, | |
54 | models.Build.build_version, | |
55 | func.avg(models.Build.build_duration), | |
56 | func.max(models.Build.build_duration), | |
57 | func.min(models.Build.build_duration), | |
58 | func.sum(models.Build.build_duration) | |
59 | ).group_by(models.Build.build_version, models.Runner.runner_name).all() | |
60 | ||
61 | all_build_times = models.Build.query.with_entities( | |
62 | models.Build.build_version, | |
63 | func.avg(models.Build.build_duration), | |
64 | func.max(models.Build.build_duration), | |
65 | func.min(models.Build.build_duration), | |
66 | func.sum(models.Build.build_duration) | |
67 | ).group_by(models.Build.build_version).all() | |
68 | ||
69 | runner_build_status = models.Build.query.join(models.Build.build_runner).with_entities( | |
70 | models.Runner.runner_name, | |
71 | models.Build.build_status, | |
72 | func.count(models.Build.build_status) | |
73 | ).group_by(models.Runner.runner_name, models.Build.build_status).all() | |
74 | ||
75 | ||
76 | stats = { | |
77 | 'builds': { | |
78 | 'all': {} | |
79 | }, | |
80 | 'times': { | |
81 | 'all': {} | |
82 | } | |
83 | } | |
84 | ||
85 | for build_time in all_build_times: | |
86 | stats['times']['all'][build_time[0]] = { | |
87 | 'avg': build_time[1] if build_time[1] else 0, | |
88 | 'max': build_time[2] if build_time[2] else 0, | |
89 | 'min': build_time[3] if build_time[3] else 0, | |
90 | 'sum': build_time[4] if build_time[4] else 0, | |
91 | } | |
92 | ||
93 | for build_time in runner_build_times: | |
94 | stats['times'].setdefault(build_time[0], {})[build_time[1]] = { | |
95 | 'avg': build_time[2] if build_time[2] else 0, | |
96 | 'max': build_time[3] if build_time[3] else 0, | |
97 | 'min': build_time[4] if build_time[4] else 0, | |
98 | 'sum': build_time[5] if build_time[5] else 0, | |
99 | } | |
100 | ||
101 | for build_status in runner_build_status: | |
102 | stats['builds']['all'].setdefault(build_status[1], 0) | |
103 | stats['builds']['all'][build_status[1]] += build_status[2] | |
104 | ||
105 | stats['builds'].setdefault(build_status[0], {})[build_status[1]] = build_status[2] | |
106 | return stats | |
107 | ||
60410d60 | 108 | @app.route('/') |
9421395c | 109 | def web_index(): |
60410d60 TP |
110 | try: |
111 | args = parse_args() | |
112 | except ValueError: | |
113 | return "Invalid Date", 400 | |
9421395c | 114 | builds = models.Build.paginate(args) |
8fca1ec2 TP |
115 | return render_template('builds.html', builds=builds) |
116 | ||
117 | @app.route('/runners/<string:runner>') | |
9421395c | 118 | def web_runner(runner): |
60410d60 TP |
119 | try: |
120 | args = parse_args() | |
121 | except ValueError: | |
122 | return "Invalid Date", 400 | |
9421395c | 123 | runner = models.Runner.get({'runner_name': runner}).first() |
60410d60 | 124 | args['build_runner'] = runner |
9421395c | 125 | builds = models.Build.paginate(args) |
8fca1ec2 TP |
126 | return render_template('runner.html', runner=runner, builds=builds) |
127 | ||
9421395c TP |
128 | @app.route('/stats') |
129 | def web_stats(): | |
028304dd TP |
130 | stats_ = stats() |
131 | runners = ['all'] + [x for x in sorted(stats_['builds'].keys()) if x != 'all'] | |
132 | return render_template('stats.html', stats=stats_, runners=runners) | |
9421395c | 133 | |
8fca1ec2 | 134 | @app.route("/runners/") |
9421395c TP |
135 | def web_runners(): |
136 | runners = models.Runner.get().all() | |
8fca1ec2 | 137 | return render_template('runners.html', runners=runners) |
31329c3f | 138 | |
9421395c TP |
139 | @app.route('/api/v1/builds') |
140 | def api_builds(): | |
141 | try: | |
142 | args = parse_args() | |
143 | except ValueError: | |
144 | return jsonify({'error': 'Invalid Date'}), 400 | |
145 | builds = models.Build.paginate(args).items | |
146 | if not builds: | |
147 | abort(404) | |
148 | return jsonify([x.as_dict() for x in builds]) | |
149 | ||
150 | @app.route('/api/v1/runners') | |
151 | def api_runners(): | |
152 | runners = models.Runner.get().all() | |
153 | if not runners: | |
154 | abort(404) | |
155 | return jsonify([x.as_dict() for x in runners]) | |
156 | ||
157 | @app.route('/api/v1/runners/<string:runner>') | |
158 | def api_runner(runner): | |
159 | try: | |
160 | args = parse_args() | |
161 | except ValueError: | |
162 | return jsonify({"Invalid Date"}), 400 | |
163 | runner = models.Runner.get({"runner_name": runner}).first() | |
164 | if not runner: | |
165 | abort(404) | |
166 | return jsonify(runner.as_dict()) | |
167 | ||
168 | @app.route('/api/v1/stats') | |
169 | def api_stats(): | |
170 | return jsonify(stats()) | |
171 | ||
172 | @app.route('/stats') | |
31329c3f TP |
173 | @app.route("/webhook", methods=('POST',)) |
174 | def process_webhook(): | |
175 | gitlab.webhooks.process(request) | |
176 | return "OK", 200 | |
177 | ||
178 | if __name__ == '__main__': | |
179 | app.run(host='0.0.0.0', port=5000) |