]> git.ipfire.org Git - people/jschlag/pbs.git/blame - src/web/ui_modules.py
Remove builders load progress bar
[people/jschlag/pbs.git] / src / web / ui_modules.py
CommitLineData
f6e6ff79 1#!/usr/bin/python
9137135a 2
5669a87f
MT
3from __future__ import division
4
eedc6432 5import datetime
b9d096e0 6import itertools
5669a87f 7import math
cd870d0a 8import pytz
f6e6ff79
MT
9import re
10import string
11import tornado.escape
9137135a
MT
12import tornado.web
13
2c909128
MT
14from .. import users
15from ..constants import *
9137135a
MT
16
17class UIModule(tornado.web.UIModule):
18 @property
1aae140a 19 def backend(self):
4b9167ef 20 return self.handler.application.backend
9137135a 21
f6e6ff79
MT
22
23class TextModule(UIModule):
1aae140a
MT
24 BUGZILLA_PATTERN = re.compile(r"(?:bug\s?|#)(\d+)")
25 CVE_PATTERN = re.compile(r"(?:CVE)[\s\-](\d{4}\-\d{4})")
c764ce43 26
1aae140a 27 LINK = """<a href="%s" target="_blank" rel="noopener">%s</a>"""
c21cb4ec 28
b9d096e0 29 def split_paragraphs(self, s):
6ea360cc 30 for group_seperator, line_iteration in itertools.groupby(s.splitlines(True), key=str.isspace):
b9d096e0
MT
31 if group_seperator:
32 continue
33
34 paragraph = "".join(line_iteration)
35 yield paragraph.replace("\n", " ")
36
c764ce43 37 def render(self, text, pre=False, remove_linebreaks=True):
c764ce43
MT
38 if remove_linebreaks:
39 text = text.replace("\n", " ")
f6e6ff79 40
c764ce43
MT
41 # Escape the text and create make urls clickable.
42 text = tornado.escape.xhtml_escape(text)
43 text = tornado.escape.linkify(text, shorten=True,
1aae140a 44 extra_params="target=\"_blank\" rel=\"noopener\"")
f6e6ff79 45
1aae140a
MT
46 # Search for bug ids that need to be linked to bugzilla
47 text = re.sub(self.BUGZILLA_PATTERN, self._bugzilla_repl, text, re.I|re.U)
f6e6ff79 48
c764ce43 49 # Search for CVE numbers and create hyperlinks.
1aae140a 50 text = re.sub(self.CVE_PATTERN, self._cve_repl, text, re.I|re.U)
f6e6ff79
MT
51
52 if pre:
c764ce43 53 return "<pre>%s</pre>" % text
f6e6ff79 54
9d0c3e75 55 return text
f6e6ff79 56
1aae140a
MT
57 def _bugzilla_repl(self, m):
58 bug_id = m.group(1)
59
60 # Get the URL
61 bug_url = self.backend.bugzilla.bug_url(bug_id)
62
63 return self.LINK % (bug_url, m.group(0))
64
65 def _cve_repl(self, m):
66 return self.LINK % ("http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s" % m.group(1), m.group(0))
67
f6e6ff79 68
b9d096e0
MT
69class CommitMessageModule(TextModule):
70 def render(self, commit):
71 s = "h5. %s\n\n" % commit.subject
72
73 paragraphs = self.split_paragraphs(commit.message)
74 s += "\n\n".join(paragraphs)
75
76 return TextModule.render(self, s, remove_linebreaks=False)
77
78
f6e6ff79
MT
79class ModalModule(UIModule):
80 def render(self, what, **kwargs):
81 what = "modules/modal-%s.html" % what
9137135a 82
f6e6ff79
MT
83 return self.render_string(what, **kwargs)
84
85
86class BuildHeadlineModule(UIModule):
d3a9ee4e 87 def render(self, build, short=False, shorter=False):
f6e6ff79
MT
88 if shorter:
89 short = True
90
91 return self.render_string("modules/build-headline.html",
d3a9ee4e 92 build=build, pkg=build.pkg, short=short, shorter=shorter)
f6e6ff79
MT
93
94
eedc6432
MT
95class JobsStatusModule(UIModule):
96 def render(self, build):
97 return self.render_string("modules/jobs/status.html",
98 build=build, jobs=build.jobs)
99
100
f6e6ff79
MT
101class BugsTableModule(UIModule):
102 def render(self, pkg, bugs):
103 return self.render_string("modules/bugs-table.html",
104 pkg=pkg, bugs=bugs)
105
106
4b1e87c4
MT
107class ChangelogModule(UIModule):
108 def render(self, name=None, builds=None, *args, **kwargs):
109 if not builds:
0c699ed0 110 builds = self.backend.builds.get_changelog(name, *args, **kwargs)
4b1e87c4
MT
111
112 return self.render_string("modules/changelog/index.html", builds=builds)
113
114
115class ChangelogEntryModule(UIModule):
116 def render(self, build):
117 return self.render_string("modules/changelog/entry.html", build=build)
118
119
f6e6ff79
MT
120class CommitsTableModule(UIModule):
121 def render(self, distro, source, commits, full_format=True):
122 return self.render_string("modules/commits-table.html",
123 distro=distro, source=source, commits=commits,
124 full_format=full_format)
125
126
127class FooterModule(UIModule):
128 def render(self):
129 return self.render_string("modules/footer.html")
130
131
132class PackagesTableModule(UIModule):
133 def render(self, job, packages):
134 return self.render_string("modules/packages-table.html", job=job,
135 packages=packages)
9137135a
MT
136
137
5669a87f
MT
138class PackagesDependencyTableModule(UIModule):
139 def render(self, pkg):
140 if pkg.type == "source":
141 all_deps = [
142 (None, pkg.requires),
143 ]
144 else:
145 all_deps = [
146 ("provides", pkg.provides),
147 ("requires", pkg.requires),
148 ("prerequires", pkg.prerequires),
149 ("conflicts", pkg.conflicts),
150 ("obsoletes", pkg.obsoletes),
151 ("recommends", pkg.recommends),
152 ("suggests", pkg.suggests),
153 ]
154
155 has_deps = []
156 for name, deps in all_deps:
157 if deps:
158 has_deps.append((name, deps))
159
160 if len(has_deps):
161 span = math.floor(12 / len(has_deps))
162
163 if span > 3:
164 span = 3
165 else:
166 span = 12
167
168 return self.render_string("modules/packages/dependency-table.html",
169 pkg=pkg, dependencies=has_deps, span=span)
170
171
9137135a
MT
172class PackageTable2Module(UIModule):
173 def render(self, packages):
174 return self.render_string("modules/package-table-detail.html",
175 packages=packages)
176
177
178class FilesTableModule(UIModule):
179 def render(self, files):
180 return self.render_string("modules/files-table.html", files=files)
181
182
f6e6ff79
MT
183class LogFilesTableModule(UIModule):
184 def render(self, job, files):
185 return self.render_string("modules/log-files-table.html", job=job,
186 files=files)
187
188
189class PackageHeaderModule(UIModule):
190 def render(self, pkg):
191 return self.render_string("modules/package-header.html", pkg=pkg)
192
193
9137135a 194class PackageFilesTableModule(UIModule):
f6e6ff79
MT
195 def render(self, pkg, filelist):
196 return self.render_string("modules/packages-files-table.html",
197 pkg=pkg, filelist=filelist)
9137135a
MT
198
199
200class BuildTableModule(UIModule):
f6e6ff79
MT
201 def render(self, builds, **kwargs):
202 settings = dict(
203 show_user = False,
204 show_repo = False,
205 show_repo_time = False,
206 show_can_move_forward = False,
207 show_when = True,
208 )
209 settings.update(kwargs)
210
eedc6432
MT
211 dates = {}
212
213 for b in builds:
214 try:
215 dates[b.date].append(b)
216 except KeyError:
217 dates[b.date] = [b,]
218
219 dates = sorted(dates.items(), reverse=True)
220
221 return self.render_string("modules/build-table.html", dates=dates, **settings)
f6e6ff79
MT
222
223
224class BuildStateWarningsModule(UIModule):
225 def render(self, build):
226 return self.render_string("modules/build-state-warnings.html", build=build)
227
228
3c7e0537
MT
229class JobsBoxesModule(UIModule):
230 def render(self, build, jobs=None):
231 if jobs is None:
232 jobs = build.jobs
233
234 return self.render_string("modules/jobs/boxes.html",
235 build=build, jobs=jobs)
236
237
238class JobStateModule(UIModule):
eedc6432 239 def render(self, job, cls=None, show_arch=False, show_icon=False, plain=False):
3c7e0537
MT
240 state = job.state
241
242 _ = self.locale.translate
243 classes = []
244
c62cd4db 245 icon = None
3c7e0537
MT
246 if state == "aborted":
247 text = _("Aborted")
248 classes.append("muted")
20e70907 249 icon = "icon-warning-sign"
3c7e0537
MT
250
251 elif state == "dependency_error":
252 text = _("Dependency problem")
253 classes.append("text-warning")
20e70907 254 icon = "icon-random"
3c7e0537
MT
255
256 elif state == "dispatching":
257 text = _("Dispatching")
258 classes.append("text-info")
20e70907 259 icon = "icon-download-alt"
3c7e0537
MT
260
261 elif state == "failed":
262 text = _("Failed")
263 classes.append("text-error")
20e70907 264 icon = "icon-remove"
3c7e0537
MT
265
266 elif state == "finished":
267 text = _("Finished")
268 classes.append("text-success")
20e70907 269 icon = "icon-ok"
3c7e0537
MT
270
271 elif state == "new":
272 text = _("New")
273 classes.append("muted")
20e70907 274 icon = "icon-asterisk"
3c7e0537
MT
275
276 elif state == "pending":
277 text = _("Pending")
278 classes.append("muted")
20e70907 279 icon = "icon-time"
3c7e0537
MT
280
281 elif state == "running":
282 text = _("Running")
c62cd4db 283 classes.append("text-info")
20e70907
MT
284 icon = "icon-cogs"
285
286 elif state == "uploading":
287 text = _("Uploading")
3c7e0537 288 classes.append("text-info")
20e70907 289 icon = "icon-upload-alt"
3c7e0537
MT
290
291 # Return just the string, is state is unknown.
292 else:
293 text = _("Unknown: %s") % state
294 classes.append("muted")
295
eedc6432
MT
296 if plain:
297 return text
298
3c7e0537
MT
299 if cls:
300 classes.append(cls)
301
eedc6432 302 if show_arch:
e459cbba 303 text = job.arch
eedc6432 304
20e70907
MT
305 if show_icon and icon:
306 text = """<i class="%s"></i> %s""" % (icon, text)
307
308 return """<span class="%s">%s</span>""" % (" ".join(classes), text)
3c7e0537
MT
309
310
f6e6ff79
MT
311class JobsTableModule(UIModule):
312 def render(self, build, jobs=None, type="release"):
313 if jobs is None:
314 jobs = build.jobs
315
316 return self.render_string("modules/jobs-table.html", build=build,
317 jobs=jobs, type=type)
318
319
320class JobsListModule(UIModule):
6e63ed49
MT
321 def render(self, jobs):
322 return self.render_string("modules/jobs/list.html", jobs=jobs)
9137135a
MT
323
324
325class RepositoryTableModule(UIModule):
326 def render(self, distro, repos):
327 return self.render_string("modules/repository-table.html",
328 distro=distro, repos=repos)
329
330
331class SourceTableModule(UIModule):
332 def render(self, distro, sources):
333 return self.render_string("modules/source-table.html",
334 distro=distro, sources=sources)
335
336
337class CommentsTableModule(UIModule):
338 def render(self, comments, show_package=False, show_user=True):
339 pkgs, users = {}, {}
340 for comment in comments:
341 if show_package:
342 try:
343 pkg = pkgs[comment.pkg_id]
344 except KeyError:
345 pkg = pkgs[comment.pkg_id] = \
0c699ed0 346 self.backend.packages.get_by_id(comment.pkg_id)
9137135a
MT
347
348 comment["pkg"] = pkg
349
350 if show_user:
351 try:
352 user = users[comment.user_id]
353 except KeyError:
354 user = users[comment.user_id] = \
0c699ed0 355 self.backend.users.get_by_id(comment.user_id)
9137135a
MT
356
357 comment["user"] = user
358
359 return self.render_string("modules/comments-table.html",
360 comments=comments, show_package=show_package, show_user=show_user)
361
362
f6e6ff79
MT
363class LogModule(UIModule):
364 def render(self, entries, **args):
365 return self.render_string("modules/log.html",
366 entries=entries, args=args)
367
368
369class LogEntryModule(UIModule):
e434b018 370 def render(self, entry, small=None, **args):
7d0635e2 371 if small or not entry.user:
e434b018
MT
372 template = "modules/log-entry-small.html"
373 else:
374 template = "modules/log-entry.html"
375
62c7e7cd
MT
376 return self.render_string(template, entry=entry, u=entry.user,
377 show_build=False, **args)
f6e6ff79
MT
378
379
380class LogEntryCommentModule(LogEntryModule):
62c7e7cd 381 def render(self, entry, show_build=False, **args):
f6e6ff79 382 return self.render_string("modules/log-entry-comment.html",
62c7e7cd 383 entry=entry, u=entry.user, show_build=show_build, **args)
f6e6ff79
MT
384
385
386class MaintainerModule(UIModule):
387 def render(self, maintainer):
2c909128 388 if isinstance(maintainer, users.User):
f6e6ff79
MT
389 type = "user"
390 else:
391 type = "string"
392
393 return self.render_string("modules/maintainer.html",
394 type=type, maintainer=maintainer)
395
396
9137135a
MT
397class BuildLogModule(UIModule):
398 # XXX deprecated
399 def render(self, messages):
400 _ = self.locale.translate
401
402 for message in messages:
403 try:
404 msg = LOG2MSG[message.message]
405 message["message"] = _(msg)
406 except KeyError:
407 pass
408
409 return self.render_string("modules/build-log.html", messages=messages)
410
411
412class LogTableModule(UIModule):
413 def render(self, messages, links=["pkg",]):
414 for message in messages:
415 try:
416 message["message"] = LOG2MSG[message.message]
417 except KeyError:
418 pass
419
420 if message.build_id:
0c699ed0 421 message["build"] = self.backend.builds.get_by_id(message.build_id)
9137135a
MT
422
423 elif message.pkg_id:
0c699ed0 424 message["pkg"] = self.backend.packages.get_by_id(message.pkg_id)
9137135a
MT
425
426 return self.render_string("modules/log-table.html",
427 messages=messages, links=links)
428
429
430class UsersTableModule(UIModule):
431 def render(self, users):
432 return self.render_string("modules/user-table.html", users=users)
433
434
435class BuildOffsetModule(UIModule):
436 def render(self):
437 return self.render_string("modules/build-offset.html")
438
439
440class RepoActionsTableModule(UIModule):
441 def render(self, repo):
442 actions = repo.get_actions()
443
444 return self.render_string("modules/repo-actions-table.html",
445 repo=repo, actions=actions)
f6e6ff79
MT
446
447
448class UpdatesTableModule(UIModule):
449 def render(self, updates):
450 return self.render_string("modules/updates-table.html", updates=updates)
451
452
453class WatchersSidebarTableModule(UIModule):
454 def css_files(self):
455 return "css/watchers-sidebar-table.css"
456
457 def render(self, build, watchers, limit=5):
458 # Sort the watchers by their realname.
459 watchers.sort(key=lambda watcher: watcher.realname)
460
461 return self.render_string("modules/watchers-sidebar-table.html",
462 build=build, watchers=watchers, limit=limit)
cd870d0a
MT
463
464
465class SelectLocaleModule(UIModule):
b1eb6312 466 LOCALE_NAMES = [
cd870d0a
MT
467 # local code, English name, name
468 ("ca_ES", u"Catalan", "Catal\xc3\xa0"),
469 ("da_DK", u"Danish", u"Dansk"),
470 ("de_DE", u"German", u"Deutsch"),
471 ("en_GB", u"English (UK)", u"English (UK)"),
472 ("en_US", u"English (US)", u"English (US)"),
473 ("es_ES", u"Spanish (Spain)", u"Espa\xf1ol (Espa\xf1a)"),
474 ("es_LA", u"Spanish", u"Espa\xf1ol"),
475 ("fr_CA", u"French (Canada)", u"Fran\xe7ais (Canada)"),
476 ("fr_FR", u"French", u"Fran\xe7ais"),
477 ("it_IT", u"Italian", u"Italiano"),
478 ("km_KH", u"Khmer", u"\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a"),
d3bda196 479 ("nl_NL", u"Dutch", u"Nederlands"),
cd870d0a
MT
480 ("pt_BR", u"Portuguese (Brazil)", u"Portugu\xeas (Brasil)"),
481 ("pt_PT", u"Portuguese (Portugal)", u"Portugu\xeas (Portugal)"),
b5321ec9 482 ("ro_RO", u"Romanian", u"Rom\xe2n\u0103"),
cd870d0a 483 ("ru_RU", u"Russian", u"\u0440\u0443\u0441\u0441\u043a\u0438\u0439"),
b1eb6312
MT
484 ("uk_UA", u"Ukrainian", u"\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"),
485 ]
486
487 # Sort the list of locales by their English name.
488 LOCALE_NAMES.sort(key=lambda x: x[1])
cd870d0a
MT
489
490 def render(self, name=None, id=None, preselect=None):
491 return self.render_string("modules/select/locale.html",
492 name=name, id=id, preselect=preselect, supported_locales=self.LOCALE_NAMES)
493
494
495class SelectTimezoneModule(UIModule):
496 def render(self, name=None, id=None, preselect=None):
497 return self.render_string("modules/select/timezone.html",
498 name=name, id=id, preselect=preselect,
499 supported_timezones=pytz.common_timezones)