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 humanize
19 import os
20 import re
21
22 from flask import Markup, url_for
23
24 from copr_common.enums import ModuleStatusEnum, StatusEnum
25 from coprs import app
26 from coprs import helpers
30 info_words = node.info.split() if node.info else []
31 attrs = self.attrs(node)
32 lexer = None
33
34 if len(info_words) > 0 and len(info_words[0]) > 0:
35 attrs.append(['class', 'language-' +
36 commonmark.common.escape_xml(info_words[0], True)])
37 try:
38 lexer = get_lexer_by_name(info_words[0])
39 except ClassNotFound:
40 pass
41
42 if lexer is None:
43 try:
44 lexer = guess_lexer(node.literal)
45 except ClassNotFound:
46 lexer = TextLexer
47
48 self.cr()
49 self.tag('pre')
50 self.tag('code', attrs)
51 code = highlight(node.literal, lexer, HtmlFormatter())
52 code = re.sub('<pre>', '', code)
53 code = re.sub('</pre>', '', code)
54 self.lit(code)
55 self.tag('/code')
56 self.tag('/pre')
57 self.cr()
58
59
60 @app.template_filter("remove_anchor")
61 -def remove_anchor(data):
67
70 if secs:
71 return time.strftime("%Y-%m-%d %H:%M:%S %Z", time.gmtime(secs))
72
73 return None
74
79
83 if num is None:
84 return "unknown"
85 return StatusEnum(num)
86
90 if num is None:
91 return "unknown"
92 return ModuleStatusEnum(num)
93
94
95 @app.template_filter("os_name_short")
96 -def os_name_short(os_name, os_version):
106
107
108 @app.template_filter('localized_time')
109 -def localized_time(time_in, timezone):
110 """ return time shifted into timezone (and printed in ISO format)
111
112 Input is in EPOCH (seconds since epoch).
113 """
114 if not time_in:
115 return "Not yet"
116 format_tz = "%Y-%m-%d %H:%M %Z"
117 utc_tz = pytz.timezone('UTC')
118 if timezone:
119 user_tz = pytz.timezone(timezone)
120 else:
121 user_tz = utc_tz
122 dt_aware = datetime.datetime.fromtimestamp(time_in).replace(tzinfo=utc_tz)
123 dt_my_tz = dt_aware.astimezone(user_tz)
124 return dt_my_tz.strftime(format_tz)
125
126
127 @app.template_filter('timestamp_diff')
128 -def timestamp_diff(time_in, until=None):
129 """ returns string with difference between two timestamps
130
131 Input is in EPOCH (seconds since epoch).
132 """
133 if time_in is None:
134 return " - "
135 if until is not None:
136 now = datetime.datetime.fromtimestamp(until)
137 else:
138 now = datetime.datetime.now()
139 diff = now - datetime.datetime.fromtimestamp(time_in)
140 return str(int(diff.total_seconds()))
141
142
143 @app.template_filter('time_ago')
144 -def time_ago(time_in, until=None):
145 """ returns string saying how long ago the time on input was
146
147 Input is in EPOCH (seconds since epoch).
148 """
149 if time_in is None:
150 return " - "
151 if until is not None:
152 now = datetime.datetime.fromtimestamp(until)
153 else:
154 now = datetime.datetime.now()
155 diff = now - datetime.datetime.fromtimestamp(time_in)
156 return humanize.naturaldelta(diff)
157
168
175
179 if pkg is not None:
180 return os.path.basename(pkg)
181 return pkg
182
186
187 description_map = {
188 "failed": "Build failed. See logs for more details.",
189 "succeeded": "Successfully built.",
190 "canceled": "The build has been cancelled manually.",
191 "running": "Build in progress.",
192 "pending": "Your build is waiting for a builder.",
193 "skipped": "This package has already been built previously.",
194 "starting": "Trying to acquire and configure builder for task.",
195 "importing": "Package content is being imported into DistGit.",
196 "waiting": "Task is waiting for something else to finish.",
197 "imported": "Package was successfully imported into DistGit.",
198 "forked": "Build has been forked from another build.",
199 }
200
201 return description_map.get(state, "")
202
206 description_map = {
207 "unset": "No default source",
208 "link": "External link to .spec or SRPM",
209 "upload": "SRPM or .spec file upload",
210 "scm": "Build from an SCM repository",
211 "pypi": "Build from PyPI",
212 "rubygems": "Build from RubyGems",
213 "custom": "Custom build method",
214 }
215
216 return description_map.get(state, "")
217
224
229
230 @app.template_filter("repo_url")
231 -def repo_url(url):
232 """
233 render copr://<user>/<prj> or copr://g/<group>/<prj>
234 to be rendered as copr projects pages
235 """
236 parsed = urlparse(url)
237 if parsed.scheme == "copr":
238 owner = parsed.netloc
239 prj = parsed.path.split("/")[1]
240 if owner[0] == '@':
241 url = url_for("coprs_ns.copr_detail", group_name=owner[1:], coprname=prj)
242 else:
243 url = url_for("coprs_ns.copr_detail", username=owner, coprname=prj)
244
245 return helpers.fix_protocol_for_frontend(url)
246
247 @app.template_filter("mailto")
248 -def mailto(url):
249 return url if urlparse(url).scheme else "mailto:{}".format(url)
250