Coerce runner id to a string.
[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
616663f4 50@cache.memoize()
9421395c
TP
51def stats():
52
53 runner_build_times = models.Build.query.join(models.Build.build_runner).with_entities(
54 models.Runner.runner_name,
55 models.Build.build_version,
56 func.avg(models.Build.build_duration),
57 func.max(models.Build.build_duration),
58 func.min(models.Build.build_duration),
59 func.sum(models.Build.build_duration)
60 ).group_by(models.Build.build_version, models.Runner.runner_name).all()
61
62 all_build_times = models.Build.query.with_entities(
63 models.Build.build_version,
64 func.avg(models.Build.build_duration),
65 func.max(models.Build.build_duration),
66 func.min(models.Build.build_duration),
67 func.sum(models.Build.build_duration)
68 ).group_by(models.Build.build_version).all()
69
70 runner_build_status = models.Build.query.join(models.Build.build_runner).with_entities(
71 models.Runner.runner_name,
72 models.Build.build_status,
73 func.count(models.Build.build_status)
74 ).group_by(models.Runner.runner_name, models.Build.build_status).all()
75
76
77 stats = {
78 'builds': {
79 'all': {}
80 },
81 'times': {
82 'all': {}
83 }
84 }
85
86 for build_time in all_build_times:
87 stats['times']['all'][build_time[0]] = {
88 'avg': build_time[1] if build_time[1] else 0,
89 'max': build_time[2] if build_time[2] else 0,
90 'min': build_time[3] if build_time[3] else 0,
91 'sum': build_time[4] if build_time[4] else 0,
92 }
93
94 for build_time in runner_build_times:
95 stats['times'].setdefault(build_time[0], {})[build_time[1]] = {
96 'avg': build_time[2] if build_time[2] else 0,
97 'max': build_time[3] if build_time[3] else 0,
98 'min': build_time[4] if build_time[4] else 0,
99 'sum': build_time[5] if build_time[5] else 0,
100 }
101
102 for build_status in runner_build_status:
103 stats['builds']['all'].setdefault(build_status[1], 0)
104 stats['builds']['all'][build_status[1]] += build_status[2]
105
106 stats['builds'].setdefault(build_status[0], {})[build_status[1]] = build_status[2]
107 return stats
108
60410d60 109@app.route('/')
9421395c 110def web_index():
60410d60
TP
111 try:
112 args = parse_args()
113 except ValueError:
114 return "Invalid Date", 400
9421395c 115 builds = models.Build.paginate(args)
8fca1ec2
TP
116 return render_template('builds.html', builds=builds)
117
118@app.route('/runners/<string:runner>')
9421395c 119def web_runner(runner):
60410d60
TP
120 try:
121 args = parse_args()
122 except ValueError:
123 return "Invalid Date", 400
9421395c 124 runner = models.Runner.get({'runner_name': runner}).first()
60410d60 125 args['build_runner'] = runner
9421395c 126 builds = models.Build.paginate(args)
8fca1ec2
TP
127 return render_template('runner.html', runner=runner, builds=builds)
128
9421395c
TP
129@app.route('/stats')
130def web_stats():
028304dd
TP
131 stats_ = stats()
132 runners = ['all'] + [x for x in sorted(stats_['builds'].keys()) if x != 'all']
133 return render_template('stats.html', stats=stats_, runners=runners)
9421395c 134
8fca1ec2 135@app.route("/runners/")
9421395c
TP
136def web_runners():
137 runners = models.Runner.get().all()
8fca1ec2 138 return render_template('runners.html', runners=runners)
31329c3f 139
9421395c
TP
140@app.route('/api/v1/builds')
141def api_builds():
142 try:
143 args = parse_args()
144 except ValueError:
145 return jsonify({'error': 'Invalid Date'}), 400
146 builds = models.Build.paginate(args).items
147 if not builds:
148 abort(404)
149 return jsonify([x.as_dict() for x in builds])
150
151@app.route('/api/v1/runners')
152def api_runners():
153 runners = models.Runner.get().all()
154 if not runners:
155 abort(404)
156 return jsonify([x.as_dict() for x in runners])
157
158@app.route('/api/v1/runners/<string:runner>')
159def api_runner(runner):
160 try:
161 args = parse_args()
162 except ValueError:
163 return jsonify({"Invalid Date"}), 400
164 runner = models.Runner.get({"runner_name": runner}).first()
165 if not runner:
166 abort(404)
167 return jsonify(runner.as_dict())
168
169@app.route('/api/v1/stats')
170def api_stats():
171 return jsonify(stats())
172
173@app.route('/stats')
31329c3f
TP
174@app.route("/webhook", methods=('POST',))
175def process_webhook():
176 gitlab.webhooks.process(request)
177 return "OK", 200
178
179if __name__ == '__main__':
180 app.run(host='0.0.0.0', port=5000)