1 import datetime
2 from six.moves.urllib.parse import urlparse
3 import pytz
4 import time
5
6 try:
7 import commonmark
8 except:
9
10 import CommonMark as commonmark
11
12 from pygments import highlight
13 from pygments.lexers import get_lexer_by_name, guess_lexer
14 from pygments.lexers.special import TextLexer
15 from pygments.util import ClassNotFound
16 from pygments.formatters import HtmlFormatter
17
18 import os
19 import re
20
21 from flask import Markup, url_for
22
23 from copr_common.enums import ModuleStatusEnum, StatusEnum
24 from coprs import app
25 from coprs import helpers
29 info_words = node.info.split() if node.info else []
30 attrs = self.attrs(node)
31 lexer = None
32
33 if len(info_words) > 0 and len(info_words[0]) > 0:
34 attrs.append(['class', 'language-' +
35 commonmark.common.escape_xml(info_words[0], True)])
36 try:
37 lexer = get_lexer_by_name(info_words[0])
38 except ClassNotFound:
39 pass
40
41 if lexer is None:
42 try:
43 lexer = guess_lexer(node.literal)
44 except ClassNotFound:
45 lexer = TextLexer
46
47 self.cr()
48 self.tag('pre')
49 self.tag('code', attrs)
50 code = highlight(node.literal, lexer, HtmlFormatter())
51 code = re.sub('<pre>', '', code)
52 code = re.sub('</pre>', '', code)
53 self.lit(code)
54 self.tag('/code')
55 self.tag('/pre')
56 self.cr()
57
58
59 @app.template_filter("remove_anchor")
60 -def remove_anchor(data):
66
69 if secs:
70 return time.strftime("%Y-%m-%d %H:%M:%S %Z", time.gmtime(secs))
71
72 return None
73
78
82 if num is None:
83 return "unknown"
84 return StatusEnum(num)
85
89 if num is None:
90 return "unknown"
91 return ModuleStatusEnum(num)
92
93
94 @app.template_filter("os_name_short")
95 -def os_name_short(os_name, os_version):
105
106
107 @app.template_filter('localized_time')
108 -def localized_time(time_in, timezone):
109 """ return time shifted into timezone (and printed in ISO format)
110
111 Input is in EPOCH (seconds since epoch).
112 """
113 if not time_in:
114 return "Not yet"
115 format_tz = "%Y-%m-%d %H:%M %Z"
116 utc_tz = pytz.timezone('UTC')
117 if timezone:
118 user_tz = pytz.timezone(timezone)
119 else:
120 user_tz = utc_tz
121 dt_aware = datetime.datetime.fromtimestamp(time_in).replace(tzinfo=utc_tz)
122 dt_my_tz = dt_aware.astimezone(user_tz)
123 return dt_my_tz.strftime(format_tz)
124
125
126 @app.template_filter('timestamp_diff')
127 -def timestamp_diff(time_in, until=None):
128 """ returns string with difference between two timestamps
129
130 Input is in EPOCH (seconds since epoch).
131 """
132 if time_in is None:
133 return " - "
134 if until is not None:
135 now = datetime.datetime.fromtimestamp(until)
136 else:
137 now = datetime.datetime.now()
138 diff = now - datetime.datetime.fromtimestamp(time_in)
139 return str(int(diff.total_seconds()))
140
141
142 @app.template_filter('time_ago')
143 -def time_ago(time_in, until=None):
144 """ returns string saying how long ago the time on input was
145
146 Input is in EPOCH (seconds since epoch).
147 """
148 if time_in is None:
149 return " - "
150 if until is not None:
151 now = datetime.datetime.fromtimestamp(until)
152 else:
153 now = datetime.datetime.now()
154 diff = now - datetime.datetime.fromtimestamp(time_in)
155 secdiff = int(diff.total_seconds())
156 if secdiff < 120:
157
158 return "1 minute"
159 elif secdiff < 7200:
160
161 return str(secdiff // 60) + " minutes"
162 elif secdiff < 172800:
163
164 return str(secdiff // 3600) + " hours"
165 elif secdiff < 5184000:
166
167 return str(secdiff // 86400) + " days"
168 elif secdiff < 63072000:
169
170 return str(secdiff // 2592000) + " months"
171 else:
172
173 return str(secdiff // 31536000) + " years"
174
185
192
196 if pkg is not None:
197 return os.path.basename(pkg)
198 return pkg
199
203
204 description_map = {
205 "failed": "Build failed. See logs for more details.",
206 "succeeded": "Successfully built.",
207 "canceled": "The build has been cancelled manually.",
208 "running": "Build in progress.",
209 "pending": "Your build is waiting for a builder.",
210 "skipped": "This package has already been built previously.",
211 "starting": "Trying to acquire and configure builder for task.",
212 "importing": "Package content is being imported into DistGit.",
213 "waiting": "Task is waiting for something else to finish.",
214 "imported": "Package was successfully imported into DistGit.",
215 "forked": "Build has been forked from another build.",
216 }
217
218 return description_map.get(state, "")
219
223 description_map = {
224 "unset": "No default source",
225 "link": "External link to .spec or SRPM",
226 "upload": "SRPM or .spec file upload",
227 "scm": "Build from an SCM repository",
228 "pypi": "Build from PyPI",
229 "rubygems": "Build from RubyGems",
230 "custom": "Custom build method",
231 }
232
233 return description_map.get(state, "")
234
241
246
247 @app.template_filter("repo_url")
248 -def repo_url(url):
249 """
250 render copr://<user>/<prj> or copr://g/<group>/<prj>
251 to be rendered as copr projects pages
252 """
253 parsed = urlparse(url)
254 if parsed.scheme == "copr":
255 owner = parsed.netloc
256 prj = parsed.path.split("/")[1]
257 if owner[0] == '@':
258 url = url_for("coprs_ns.copr_detail", group_name=owner[1:], coprname=prj)
259 else:
260 url = url_for("coprs_ns.copr_detail", username=owner, coprname=prj)
261
262 return helpers.fix_protocol_for_frontend(url)
263
264 @app.template_filter("mailto")
265 -def mailto(url):
266 return url if urlparse(url).scheme else "mailto:{}".format(url)
267