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