def human_time(datetime): # TODO - add localization # http://flask.pocoo.org/snippets/33/ now = datetime.utcnow() diff = now - datetime periods = ( (diff.days / 365, "year", "years"), (diff.days / 30, "month", "months"), (diff.days / 7, "week", "weeks"), (diff.days, "day", "days"), (diff.seconds / 3600, "hour", "hours"), (diff.seconds / 60, "minute", "minutes"), (diff.seconds, "second", "seconds"), ) for i, (period, singular, plural) in enumerate(periods): if period and i > 3: #i.e., less than a day ago return "%d %s ago" % (period, singular if period == 1 else plural) elif period: # http://babel.edgewall.org/wiki/Documentation/0.9/dates.html #return format_datetime(datetime, "MMMM d 'at' h:mma") if diff.days == 1: return "yesterday" elif datetime.year - now.year: return format_datetime(datetime, "MMMM d, YYYY") else: return format_datetime(datetime, "MMMM d") return "now"
def to_dict(self, include_owner=False, include_last_edited_user=False, include_comments=False): owner = None if include_owner: owner = self.owner.to_dict() last_edited_user = None if include_last_edited_user: if self.last_edited_user is not None: last_edited_user = self.last_edited_user.to_dict() comments = None if include_comments: comments = self.comment_topic.to_dict()['comments'] if self.addition_information == '': self.addition_information = '{}' return {'id': self.id, 'name': self.name, 'owner': owner, 'created_time': format_datetime(self.created_time), 'last_edited_user': last_edited_user, 'last_edited_time': format_datetime(self.last_edited_time), 'data_file': self.data_file, 'addition_information': self.addition_information, 'description': self.description, 'is_public': self.is_public, 'is_base_scenario': self.is_base_scenario, 'has_preview': self.has_preview, 'comments': comments}
def date_age(dt, now=None): # Fail silently for now XXX if not dt: return "" formatted_date = babel.format_datetime(dt, format='yyyy-MM-dd HH:mm') return u"{} ({})".format(formatted_date, age(dt, now))
def friendly_time(time, fallback_format=None): now = datetime.utcnow() if type(time) is datetime: diff = now - time elif not time: diff = now - now second_diff = diff.seconds day_diff = diff.days if day_diff < 0: return '' elif day_diff == 0: if second_diff < 10: return _(u'just now') elif second_diff < 60: return _('%s second(s) ago' % str(second_diff)) elif second_diff < 120: return _('1 minute ago') elif second_diff < 3600: return _('%s mintues ago' % str(second_diff / 60)) elif second_diff < 7200: return _('1 hour ago') elif second_diff < 86400: return _('%s hours ago' % str(second_diff / 3600)) elif day_diff == 1: return _('yesterday') elif day_diff < 7: return _('%s days ago' % str(day_diff)) return format_datetime(time, fallback_format)
def macro(self, content, arguments, page_url, alternative): if arguments is None: tm = time.time() # always UTC else: stamp = arguments[0] tm = self.parse_time(stamp) return format_datetime(datetime.utcfromtimestamp(tm))
def timesince(dt, format='EEEE, d MMMM yyyy HH:mm UTC', default=None): now = datetime.utcnow() diff = now - dt periods = ( ( diff.days/3, format_datetime(dt, format) ), ( diff.days, ngettext(u"%(num)s day ago", u"%(num)s days ago", diff.days) ), ( diff.seconds/3600, ngettext(u"%(num)s hour ago", u"%(num)s hours ago", diff.seconds/3600) ), ( diff.seconds/60, ngettext(u"%(num)s minute ago", u"%(num)s minutes ago", diff.seconds/60) ), ( diff.seconds, ngettext(u"%(num)s second ago", u"%(num)s seconds ago", diff.seconds) ), ) for period, result in periods: if period: return result return default or _(u"just now")
def get_latest_commits(repo_dir, commit_count): """ :param repo_dir: path of repo :type repo_dir: str :param commit_count: number of commits to return :type commit_count: int :return: commit information (hash, message, author, date) about commit_count last commits :rtype: list of dicts """ try: sipa_repo = git.Repo(repo_dir) commits = sipa_repo.iter_commits(max_count=commit_count) return [{ 'hexsha': commit.hexsha, 'message': commit.summary, 'author': commit.author, 'date': format_datetime(datetime.fromtimestamp(commit.committed_date)), } for commit in commits] except (InvalidGitRepositoryError, CacheError, GitCommandError): logger.exception("Could not get latest commits", extra={'data': { 'repo_dir': repo_dir }}) return []
def filter_datetime_from_unix(value, format='medium'): value = datetime.datetime.fromtimestamp(value) if format == 'full': format="EEEE, d. MMMM y 'at' HH:mm" elif format == 'medium': format="EE dd.MM.y HH:mm" return format_datetime(value, format)
def utc_to_local_datetime(dt): """Converts a naive UTC datetime object to a naive local datetime object.""" from datetime import datetime from flask.ext.babel import format_datetime return datetime.strptime(format_datetime(dt, 'YYYY-MM-dd HH:mm:ss'), '%Y-%m-%d %H:%M:%S')
def format_babel_datetime(value, format='medium'): if format == 'full': format="EEEE, d. MMMM y 'at' HH:mm" elif format == 'medium': format="EE dd.MM.y HH:mm" elif format == 'basic': format="dd.MM.y HH:mm" return format_datetime(value, format)
def format_datetime_filter(value, date_format='medium'): if type(value) in [int, float]: value = datetime.datetime.fromtimestamp(value) if date_format == 'full': date_format = "EEEE, d MMMM y 'at' HH:mm" elif date_format == 'medium': date_format = "EE dd/MM/y HH:mm" return format_datetime(value, date_format)
def local_date_time(datestamp): """ Returns a babel formatted local date and time """ from flask.ext.babel import format_datetime if datestamp: return format_datetime(datestamp)
def getDict(self): data = {} data['id'] = self.id data['uid'] = self.user_id data['aid'] = self.amend_id data['comment'] = self.comment data['timedelta'] = format_timedelta(datetime.utcnow() - self.time) data['datetime'] = format_datetime(self.time) return data
def date_filter(date, format = None) : """ Date filter to use in Jinja2 templates :param date : datetime object to convert :param format: format to convert date object :type date: datetime :type format: str .. warnings:: Format is in flask.ext.babel format """ if date : if format : return format_datetime(date, format) else : return format_datetime(date, 'dd MM yyyy') else : return ''
def getDict(self): data = {} data['uid'] = self.user_id data['id'] = self.id data['content'] = self.content data['language'] = self.language data['datetime'] = format_datetime(self.time) data['timedelta'] = format_timedelta(datetime.utcnow() - self.time) return data
def getDict(self): data = {} data['id'] = self.id data['uid'] = self.user_id data['twitter_id'] = self.twitter_id data['content'] = self.content data['explain'] = self.explain data['datetime'] = format_datetime(self.time) data['timedelta'] = format_timedelta(datetime.utcnow() - self.time) return data
def format_date_local(date): "Human friendly datetime display" adate = arrow.get(date) locale = get_locale() diff = arrow.utcnow() - adate if diff.days <= 2: return adate.humanize(locale=locale) else: return format_datetime(date)
def format_date_local(date): "Human friendly datetime display" adate = arrow.get(date) locale = get_locale() diff = arrow.utcnow() - adate if diff.days <= 2: return adate.humanize(locale = locale) else: return format_datetime(date)
def format_datetime_with_lang(value, format='%H:%M %d-%m-%Y'): """Returns formatted timestamp. Args: value (date): the date to be converted to a string. format (optional str): the format of the new string. Defaults to '%H:%M %d-%m-%Y'. Returns: The formatted timestamp string. """ return format_datetime(value, format)
def _value(self): if self.raw_data: return ' '.join(self.raw_data) else: locale = get_locale() date_fmt = locale.date_formats['short'].pattern date_fmt = date_fmt.replace('MMMM', 'MM')\ .replace('MMM', 'MM') # force numerical months time_fmt = locale.time_formats['short'] dt_fmt = locale.datetime_formats['short'].format(time_fmt, date_fmt) return format_datetime(self.data, dt_fmt) if self.data else ''
def test_parse_time(self): MacroDateTimeBase_obj = MacroDateTimeBase() test_time_args = '2011-08-07T11:11:11+0533' result = MacroDateTimeBase_obj.parse_time(test_time_args) expected = 1312695491.0 assert result == expected result = format_datetime(datetime.utcfromtimestamp(result)) expected = u'Aug 7, 2011, 5:38:11 AM' # comma after year was added in recent CLDR assert result == expected with pytest.raises(ValueError): # things after next 10,000 years can't be predicted MacroDateTimeBase_obj.parse_time('12011-08-07T11:11:11')
def age(dt, now=None, add_direction=True, date_threshold=None): """ :param dt: :class:`datetime<datetime.datetime>` instance to format :param now: :class:`datetime<datetime.datetime>` instance to compare to `dt` :param add_direction: if `True`, will add "in" or "ago" (example for `en` locale) to time difference `dt - now`, i.e "in 9 min." or " 9min. ago" :param date_threshold: above threshold, will use a formated date instead of elapsed time indication. Supported values: "day". """ # Fail silently for now XXX if not dt: return "" if not now: now = datetime.datetime.utcnow() locale = babel.get_locale() dt = utc_dt(dt) now = utc_dt(now) delta = dt - now if date_threshold is not None: dy, dw, dd = dt_cal = dt.isocalendar() ny, nw, nd = now_cal =now.isocalendar() if dt_cal != now_cal: # not same day remove_year = (dy == ny) date_fmt = locale.date_formats['long'].pattern time_fmt = locale.time_formats['short'].pattern fmt = locale.datetime_formats['medium'] if remove_year: date_fmt = date_fmt.replace('y', '').strip() # remove leading or trailing spaces, comma, etc... date_fmt = re.sub(u'^[^A-Za-z]*|[^A-Za-z]*$', u'', date_fmt) fmt = fmt.format(time_fmt, date_fmt) return babel.format_datetime(dt, format=fmt) # don't use (flask.ext.)babel.format_timedelta: as of Flask-Babel 0.9 it # doesn't support "threshold" arg. return format_timedelta(delta, locale=locale, granularity='minute', threshold=0.9, add_direction=add_direction)
def to_dict(self, include_owner=False, include_topic=False): owner = None if include_owner: owner = self.owner.to_dict() topic = None if include_topic: topic = self.topic.to_dict() return {'id': self.id, 'owner': owner, 'topic': topic, 'created_time': format_datetime(self.created_time), 'description': self.description, 'content': self.content}
def to_dict(self, include_owner=False): owner = None if include_owner: owner = self.owner.to_dict() comment_objects = self.get_latest_comments() comments = [] for comment_object in comment_objects: comments.append(comment_object.to_dict(include_owner=True)) return {'id': self.id, 'title': self.title, 'owner': owner, 'created_time': format_datetime(self.created_time), 'comments': comments}
def show_info(): show = request.args.get('show') if show is None: return jsonify({'success': False, 'error': 'no show set!'}) show = Show.query.get(int(show)) if show is None: return jsonify({'success': False, 'error': 'no show found!'}) ret = {'name': show.name, 'description': show.description, 'begin': format_datetime(show.begin), #'duration': format_timedelta(show.end - show.begin,granularity='minute'), 'users': []} for ushow in show.users: ret['users'].append({'username': ushow.user.username, 'status': ushow.status}) return jsonify({'success': True, 'data': ret})
def sitemap() : """ Site sitemap """ # list all contents sitemap_contents = [c for c in contents if not c.draft] # root root = ET.Element('urlset') root.set('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9') for content in sitemap_contents : url = ET.SubElement(root, 'url') loc = ET.SubElement(url, 'loc') loc.text = urljoin(request.url_root, content.path) lastmod = ET.SubElement(url, 'lastmod') lastmod.text = format_datetime(content.published, 'yyyy-MM-dd') text = ET.tostring(root) response = make_response(text, 200) response.headers['Content-Type'] = 'application/xml' return response
def to_dict(self, include_owner=False, include_comments=False): owner = None if include_owner: owner = self.owner.to_dict() comments = None if include_comments: comments = self.comment_topic.to_dict()['comments'] return {'id': self.id, 'name': self.name, 'owner': owner, 'comments': comments, 'created_time': format_datetime(self.created_time), 'data_file': self.data_file, 'file_type': self.file_type, 'addition_information': json.loads(self.addition_information), 'description': self.description, 'has_preview': self.has_preview, 'is_base_item': self.is_base_item}
def get_latest_commits(repo_dir, commit_count): """ :param repo_dir: path of repo :type repo_dir: str :param commit_count: number of commits to return :type commit_count: int :return: commit information (hash, message, author, date) about commit_count last commits :rtype: list of dicts """ try: sipa_repo = git.Repo(repo_dir) commits = sipa_repo.iter_commits(max_count=commit_count) return [{ 'hexsha': commit.hexsha, 'message': commit.summary, 'author': commit.author, 'date': format_datetime(datetime.fromtimestamp( commit.committed_date)), } for commit in commits] except (InvalidGitRepositoryError, CacheError, GitCommandError): logger.exception("Could not get latest commits", extra={'data': { 'repo_dir': repo_dir}}) return []
def format_datetime2(value, format='medium'): if format == 'full': format = "EEEE, d. MMMM y 'at' HH:mm" elif format == 'medium': format = "MM/dd/y" return format_datetime(value, format)
def format_datetime_filter(value, format = "yyyy-mm-dd HH:mm"): return format_datetime(value, format)
def convert_datetime(value): return format_datetime(value)
def format_datetime2(value, format='medium'): if format == 'full': format="EEEE, d. MMMM y 'at' HH:mm" elif format == 'medium': format="MM/dd/y" return format_datetime(value, format)
def format_date(value, format='y-MM-dd'): return format_datetime(value, format)
def format_datetime(value, format='full'): if type(value) is str or type(value) is unicode: datetimeobj = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S') return babel.format_datetime(datetimeobj, format) else: return babel.format_datetime(value, format)
def jinja_format_datetime(value, dt_format='dd. MMMM yyyy HH:mm'): return format_datetime(value, dt_format)
def posted_at(value): format = "dd/MM/y 'à' HH'h'mm" return format_datetime(value, format)
def send_email_report(report, location=None, end_date=None, start_date=None): """Sends an email via Hermes with the latest report. Args: report (str): The report ID, from the REPORTS_LIST configuration file parameter. """ report_list = current_app.config['REPORTS_CONFIG']['report_list'] country = current_app.config['MESSAGING_CONFIG']['messages']['country'] topic = current_app.config['MESSAGING_CONFIG']['subscribe'][ 'topic_prefix'] + report # TESTING # If report requested begins with "test_" then send to the test topic # E.g. test_communicable_diseases would send cd report to "test-emails" test_id = "" test_sub = "" if report.startswith("test_"): report = report[5:] topic = "test-emails" test_id = "-" + str(datetime.now().time().isoformat()) test_sub = "TEST {} | ".format(current_app.config['DEPLOYMENT']) if validate_report_arguments(current_app.config, report, location, end_date, start_date): app.logger.debug("creating report") ret = create_report(config=current_app.config, report=report, location=location, end_date=end_date, start_date=start_date) relative_url = url_for(report_list[report].get('email_report_format', 'reports.report'), report=report, location=location, end_date=end_date, start_date=start_date) report_url = current_app.config['LIVE_URL'] + relative_url # Use meerkat_auth to authenticate the email's report link # Only do this if an access account is specified for the email email_access = report_list.get(report, {}).get('email_access_account', {}) if email_access: app.logger.warning('Authenticating') token = authenticate(email_access.get('username'), email_access.get('password')) if token: report_url += "?meerkat_jwt=" + str(token) # Use env variable to determine whether to fetch image content from external source or not if int(current_app.config['PDFCROWD_USE_EXTERNAL_STATIC_FILES']) == 1: content_url = current_app.config['PDFCROWD_STATIC_FILE_URL'] else: content_url = current_app.config['LIVE_URL'] + 'static/' html_email_body = render_template(ret['template_email_html'], report=ret['report'], extras=ret['extras'], address=ret['address'], content=g.config['REPORTS_CONFIG'], report_url=report_url, content_url=content_url) plain_email_body = render_template(ret['template_email_plain'], report=ret['report'], extras=ret['extras'], address=ret['address'], content=g.config['REPORTS_CONFIG'], report_url=report_url, content_url=content_url) epi_week = ret['report']['data']['epi_week_num'] start_date = datetime_from_json(ret['report']['data']['start_date']) end_date = datetime_from_json(ret['report']['data']['end_date']) if report_list[report]['default_period'] == 'month': subject = '{test_subject}{country} | {title} ({start_date} - {end_date})'.format( test_subject=test_sub, country=gettext(country), title=gettext(report_list[report]['monthly_email_title']), start_date=format_datetime(start_date, 'dd MMMM YYYY'), end_date=format_datetime(end_date, 'dd MMMM YYYY')) email_id = ''.join([ topic, "-", end_date.strftime('%m'), "-", end_date.strftime('%Y'), "-", report, test_id ]) else: subject = '{test_subject}{country} | {title} {epi_week_text} {epi_week} ({start_date} - {end_date})'.format( test_subject=test_sub, country=gettext(country), title=gettext(report_list[report]['title']), epi_week_text=gettext('Epi Week'), epi_week=epi_week, start_date=format_datetime(start_date, 'dd MMMM YYYY'), end_date=format_datetime(end_date, 'dd MMMM YYYY')) email_id = ''.join([ topic, "-", str(epi_week), "-", end_date.strftime('%Y'), "-", report, test_id ]) # Assemble the message data in a manner hermes will understand. message = { "id": email_id, "topics": topic, "html-message": html_email_body, "message": plain_email_body, "subject": subject, "from": current_app.config['MESSAGING_CONFIG']['messages']['from'] } # Publish the message to hermes r = hermes('/publish', 'PUT', message) print(r) succ = 0 fail = 0 for resp in r: try: resp['ResponseMetadata']['HTTPStatusCode'] except KeyError: current_app.logger.warning("Hermes return value error:" + str(resp['message'])) fail += 1 except TypeError: current_app.logger.warning("Hermes job error:" + str(r['message'])) abort(502) else: if resp['ResponseMetadata']['HTTPStatusCode'] == 200: succ += 1 else: current_app.logger.warning( "Hermes error while sending message:" + str(resp['message'])) fail += 1 return '\nSending {succ} messages succeeded, {fail} messages failed\n\n'.format( succ=succ, fail=fail) else: current_app.logger.warning("Aborting. Report doesn't exist: " + str(report)) abort(501)
def _jinja2_humanize_datetime(date, locale=None): if date is not None: fmt = "d/MM, H':'mm" if datetime.now().year != date.year: fmt = "d/MM/yyyy" return format_datetime(date, fmt)
def current_date(): return '<br>'.join( (format_datetime(datetime.now()), format_datetime(datetime.now(), 'full'), format_datetime(datetime.now(), 'short')))
def format_timestamp(value, format='medium'): if format == 'short': print 'printing: ' + str(value) return format_datetime(value, format="y.MM.dd HH") elif format == 'medium': return format_datetime(value, format="EE y.MM.dd HH:mm")
def format_datetime(value, format='full'): return babel.format_datetime(value, format)
def view_email_report(report, location=None, end_date=None, start_date=None, email_format='html'): """ Views and email as it would be sent to Hermes API Args: report (str): The report ID, from the REPORTS_LIST configuration file parameter. """ print("viewing email") report_list = current_app.config['REPORTS_CONFIG']['report_list'] country = current_app.config['MESSAGING_CONFIG']['messages']['country'] if validate_report_arguments(current_app.config, report, location, end_date, start_date): ret = create_report(config=current_app.config, report=report, location=location, end_date=end_date, start_date=start_date) relative_url = url_for(report_list[report].get('email_report_format', 'reports.report'), report=report, location=None, end_date=None, start_date=None) report_url = c.add_domain(relative_url) # Use meerkat_auth to authenticate the email's report link # Only do this if an access account is specified for the email email_access = report_list.get(report, {}).get('email_access_account', {}) app.logger.debug('Email access {}'.format(email_access)) if email_access: app.logger.warning('Authenticating') token = authenticate(email_access.get('username'), email_access.get('password')) if token: report_url += "?meerkat_jwt=" + str(token) # Use env variable to determine whether to fetch image content from external source or not if int(current_app.config['PDFCROWD_USE_EXTERNAL_STATIC_FILES']) == 1: content_url = current_app.config['PDFCROWD_STATIC_FILE_URL'] else: content_url = c.add_domain('/static/') if email_format == 'html': email_body = render_template(ret['template_email_html'], report=ret['report'], extras=ret['extras'], address=ret['address'], content=g.config['REPORTS_CONFIG'], report_url=report_url, content_url=content_url) elif email_format == 'txt': email_body = '<plaintext>' + render_template( ret['template_email_plain'], report=ret['report'], extras=ret['extras'], address=ret['address'], content=g.config['REPORTS_CONFIG'], report_url=report_url, content_url=content_url) else: abort(501) if report_list[report]['default_period'] == 'month': topic = current_app.config['MESSAGING_CONFIG']['subscribe'][ 'topic_prefix'] + report start_date = datetime_from_json( ret['report']['data']['start_date']) end_date = datetime_from_json(ret['report']['data']['end_date']) subject = '{country} | {monthly_email_title} ({start_date} - {end_date})'.format( country=gettext(country), monthly_email_title=gettext( report_list[report]['monthly_email_title']), start_date=format_datetime(start_date, 'dd MMMM YYYY'), end_date=format_datetime(end_date, 'dd MMMM YYYY')) email_id = ("<topic>" + "-" + end_date.strftime('%b') + "-" + end_date.strftime('%Y') + "-" + report) else: start_date = datetime_from_json( ret['report']['data']['start_date']) end_date = datetime_from_json(ret['report']['data']['end_date']) epi_week = ret['report']['data']['epi_week_num'] subject = '{country} | {title} {epi_week_text} {epi_week} ({start_date} - {end_date})'.format( country=gettext(country), title=gettext(report_list[report]['title']), epi_week_text=gettext('Epi Week'), epi_week=epi_week, start_date=format_datetime(start_date, 'dd MMMM YYYY'), end_date=format_datetime(end_date, 'dd MMMM YYYY')) email_id = ("<topic>" + "-" + str(epi_week) + "-" + end_date.strftime('%Y') + "-" + report) current_app.logger.debug('Viewing email with id: ' + email_id) current_app.logger.debug('Email subject: ' + subject) return email_body else: abort(501)
def task_greeting(course_id, task_id, lang): student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') db = g.db # generate the parameters as soon as the student visits params, meta = get_params(course_id, task_id, student_id, db) instr_ok = True try: instructions = db.task_instructions.find_one({'course_id': course_id, 'task_id': task_id}) instructions = instructions.get(lang, instructions[app.config['DEFAULT_LANG']]) except Exception: try: instructions = list(instructions.values())[0] except Exception as e: instructions = str(e) instr_ok = False if instr_ok: try: public_params = [] for k, v in meta.items(): if v.get('public', False): public_params += [{ 'name': k, 'value': params.get(k), 'description': v.get('descriptions', {}).get(lang) }] except Exception as e: instructions = str(e) computer_list = list(db.student_computers.find({'course_id': course_id, 'task_id': task_id, 'student_id': student_id})) backing_files = collections.defaultdict(set) for computer in computer_list: if 'disk_urls' not in computer: continue for name, disk in computer['disk_urls'].items(): for fmt in disk['formats']: backing_files[fmt] |= set(disk[fmt][1:]) if request.args.get('narediStack', 'false') == 'true': #db.student_tasks.update({'task_id': task_id, 'student_id': student_id}, {'$set': {'create_openstack': True}}, upsert = True) openstackCreated = False # Spremeni na True, ko odkomentiras zgornjo vrstico. else: if db.student_tasks.find({'course_id': course_id, 'task_id': task_id, 'student_id': student_id, 'openstack_created': True}).count() > 0: openstackCreated = True elif db.student_tasks.find({'course_id': course_id, 'task_id': task_id, 'student_id': student_id, 'create_openstack': True}).count() > 0: openstackCreated = True else: openstackCreated = False try: result = db.results.find_one( {'$query': {'course_id': course_id, 'task_id': task_id, 'student_id': student_id}, '$orderby': collections.OrderedDict([('result', -1), ('time', 1)])}, {'result': 1, 'status': 1, 'hints': 1, 'time': True, '_id': 0}) result['time'] = format_datetime(result['time']) print(result) except Exception: result = None return render_template('task_greeting.html', disk_base_url='/'.join([app.config['STUDENT_DISK_URL'], student_id, course_id, task_id, '']), course_id=course_id, task_id=task_id, computers=sorted((c for c in computer_list if 'disk_urls' in c), key=lambda c: c['name']), backing_files={fmt: sorted(images) for fmt, images in backing_files.items()}, lang='sl' if lang == 'si' else lang, # TODO s/si/sl in all tasks (and maybe elsewhere) openstack=openstackCreated, instructions=jinja2.Template(instructions), params=public_params, result=result, **{p['name']: p['value'] for p in public_params})