Fix stats template, don't error on missing runner stats
[GitLab/stricted-build/lineage_builder.git] / ui / app.py
CommitLineData
8fca1ec2 1import datetime
31329c3f
TP
2import os
3
4import requests
9421395c 5from flask import Flask, render_template, request, abort, jsonify
8fca1ec2 6from flask_bootstrap import Bootstrap
31329c3f
TP
7from flask_caching import Cache
8from flask_migrate import Migrate
8fca1ec2
TP
9from flask_nav import Nav
10from flask_nav.elements import Navbar, Text, View
31329c3f
TP
11from flask_sqlalchemy import SQLAlchemy
12
9421395c
TP
13from sqlalchemy import orm, func
14
31329c3f
TP
15from ui import gitlab, config, models
16
17app = Flask(__name__)
8fca1ec2 18Bootstrap(app)
31329c3f
TP
19app.config.from_object(config)
20cache = Cache(app)
21models.db.init_app(app)
22migrate = Migrate(app, models.db)
8fca1ec2 23nav = Nav(app)
31329c3f 24
8fca1ec2
TP
25nav.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
32headers = {'Private-Token': os.environ.get('GITLAB_TOKEN', '')}
33
60410d60 34def 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
50def 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 109def 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 118def 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')
129def 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
135def 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')
140def 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')
151def 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>')
158def 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')
169def api_stats():
170 return jsonify(stats())
171
172@app.route('/stats')
31329c3f
TP
173@app.route("/webhook", methods=('POST',))
174def process_webhook():
175 gitlab.webhooks.process(request)
176 return "OK", 200
177
178if __name__ == '__main__':
179 app.run(host='0.0.0.0', port=5000)