def transform(self, result, encoding): css = u'' javascript = u'' for parent in parents(self.published.context, iface=IFlowFolder): css = parent._v_css = getattr( # noqa: P001 parent, '_v_css', Scss().compile(parent.css or u''), ) javascript =\ (parent.javascript or u'').strip().replace(u'\r\n', u'\n') break root = result.tree.getroot() if css: head = root.find('head') # trick LXML to render styles with //<!CDATA[[ style = builder.STYLE(u'\n/*', type='text/css') style.append(builder.S(CDATA(u'*/\n' + css + u'\n/*'))) style.getchildren()[0].tail = u'*/\n' head.append(style) # head.append(builder.STYLE( # css, # type='text/css', # )) if javascript: body = root.find('body') # trick LXML to render script with //<!CDATA[[ script = builder.SCRIPT(u'\n//', type='application/javascript') script.append(builder.S(CDATA(u'\n' + javascript + u'\n//'))) script.getchildren()[0].tail = u'\n' body.append(script) return result
def to_element(self) -> ElementT: """Returns item element for xml.""" item = create_element('item', children=[ create_element('title', CDATA(self.title)), create_element('description', CDATA(self.description)), ]) if self.url: item.append(create_element('link', self.url)) item.append(create_element( 'guid', attrib={ 'isPermaLink': str(bool(not self.guid and self.url)).lower() }, text=(self.guid or self.url or CDATA(self.title)) )) if self.author: item.append(create_element( '{http://purl.org/dc/elements/1.1/}creator', CDATA(self.author) )) for category in self.categories: item.append(create_element('category', CDATA(category))) if self.enclosure: item.append(self.enclosure.to_element()) if self.pub_date: item.append(create_element('pubDate', self.pub_date)) return item
def process_availability(self, root, tag, child, script_type, identifier): if tag: if child.text is None: child.text = CDATA(self.add_availability(script_type, identifier, False)) else: if 'SchemeUtil_PMP' not in child.text: new_var = '' current_code_array = child.text.splitlines() current_code = '' for line_code in current_code_array: if line_code.startswith('//'): pass else: current_code = current_code + line_code + '\n' if child.text.count('return ') > 1: new_var = new_var + 'var actual : boolean' new_var = new_var + str(current_code).replace('return', 'actual =').replace('"', '\"') else: new_var = str(current_code).replace('return', 'var actual =', 1).replace('"', '\"') child.text = CDATA(new_var + '\n' + self.add_availability(script_type, identifier, True)) else: new_element = etree.Element('AvailabilityScript') new_element.text = CDATA(self.add_availability(script_type, identifier, False)) root.insert(0, new_element)
def process_initialize(self, root, tag, child): if tag: if child.text is None: child.text = CDATA(self.add_clause_initialize()) else: new_element = etree.Element('InitializeScript') new_element.text = CDATA(self.add_clause_initialize()) root.insert(0, new_element)
def process_existence(self, root, tag, child): if self.existence != '': del root.attrib['existence'] if tag: if child.text is None: child.text = CDATA(self.add_clause_existence()) else: new_element = etree.Element('ExistenceScript') new_element.text = CDATA(self.add_clause_existence()) root.insert(0, new_element)
def element_artwork(self, e, p): if e.text and e.text.strip() and not ']]>' in e.text: e.text = CDATA( e.text ) # prevent text from being mucked up by other processors stripattr(e, [ 'height', '{http://www.w3.org/XML/1998/namespace}space', 'width', ]) if e.text and re.search(r'^\s*<CODE BEGINS>', e.text): # We have source code. Permitted attributes: anchor, name, # source, type. e = self.replace(e, 'sourcecode') e.set('markers', 'true') match = re.search( r'(?s)^\s*<CODE BEGINS>(\s+file\s+"([^"]*)")?(.*?)(<CODE ENDS>(.*))?$', e.text) file = match.group(1) name = match.group(2) body = match.group(3) ends = match.group(4) tail = match.group(5) if file and name: e.set('name', name) e.text = CDATA(body) if not ends: self.warn(e, "Found <CODE BEGINS> without matching <CODE ENDS>") if ends and tail.strip() != "": self.warn(e, "Found non-whitespace content after <CODE ENDS>") e.tail = tail stripattr(e, [ 'align', 'alt', 'height', 'suppress-title', 'width', ]) src = e.get('src') if e.text and e.text.strip() and src: # We have both text content and a src attribute -- convert to # <artset> with 2 <artwork) ext = os.path.splitext(src)[1][1:] artset = self.element('artset', line=e.sourceline) extart = self.copy(e, 'artwork') extart.text = None extart.tail = None extart.set('type', ext) artset.append(extart) stripattr(e, ['src']) e.set('type', 'ascii-art') artset.append(e) p.append(artset)
def generate_rss_feed(repo, filename, me): generator = FeedGenerator() generator.id(repo.html_url) generator.title(f"RSS feed of {repo.owner.login}'s {repo.name}") generator.author( {"name": os.getenv("GITHUB_NAME"), "email": os.getenv("GITHUB_EMAIL")} ) generator.link(href=repo.html_url) generator.link( href=f"https://raw.githubusercontent.com/{repo.full_name}/master/{filename}", rel="self", ) for issue in repo.get_issues(): if not issue.body or not is_me(issue, me) or issue.pull_request: continue item = generator.add_entry(order="append") item.id(issue.html_url) item.link(href=issue.html_url) item.title(issue.title) item.published(issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")) for label in issue.labels: item.category({"term": label.name}) body = "".join(c for c in issue.body if _valid_xml_char_ordinal(c)) item.content(CDATA(marko.convert(body)), type="html") generator.atom_file(filename)
def _cdata(self, text): if isinstance(text, str): el = Element('a') el.text = CDATA(text) return dc(tostring(el)[3:-4]) return smart_text(text)
def generateNoteElement(html, enex): soup, noteProps = generateCData(html) note = etree.SubElement(enex, "note") title = etree.SubElement(note, 'title').text = noteProps['note-title'] content = etree.SubElement(note, "content").text = CDATA(str(soup)) etree.SubElement( note, "created" ).text = f"{dt.utcnow().strftime('%Y%m%dT%H%M%SZ')}" #todo: replace dummy datetimes etree.SubElement( note, "updated").text = f"{dt.utcnow().strftime('%Y%m%dT%H%M%SZ')}" nattrs = etree.SubElement(note, "note-attributes") etree.SubElement(nattrs, 'author').text = "Jon Crump" for x in noteProps: d = noteProps[x] if x != 'note-title': resource = etree.SubElement(note, "resource") etree.SubElement(resource, "data", encoding="base64").text = d['data'] etree.SubElement(resource, "mime").text = d['type'] etree.SubElement(resource, "width").text = d['width'] etree.SubElement(resource, "height").text = d['height'] rattrs = etree.SubElement(resource, "resource-attributes") etree.SubElement(rattrs, 'file-name').text = d['filename']
def _add_text_elm(entry, data, name): """Add a text subelement to an entry""" if not data: return elm = xml_elem(name, entry) type_ = data.get('type') if data.get('src'): if name != 'content': raise ValueError("Only the 'content' element of an entry can " "contain a 'src' attribute") elm.attrib['src'] = data['src'] elif data.get(name): # Surround xhtml with a div tag, parse it and embed it if type_ == 'xhtml': xhtml = '<div xmlns="http://www.w3.org/1999/xhtml">' \ + data.get(name) + '</div>' elm.append(xml_fromstring(xhtml)) elif type_ == 'CDATA': elm.text = CDATA(data.get(name)) # Parse XML and embed it elif type_ and (type_.endswith('/xml') or type_.endswith('+xml')): elm.append(xml_fromstring(data[name])) # Embed the text in escaped form elif not type_ or type_.startswith('text') or type_ == 'html': elm.text = data.get(name) # Everything else should be included base64 encoded else: raise NotImplementedError( 'base64 encoded {} is not supported at the moment. ' 'Pull requests adding support are welcome.'.format(name) ) # Add type description of the content if type_: elm.attrib['type'] = type_
def process_version(version, key, path, author): cpath = path vars = (version.get('vars') or {}) xmlver = Element( 'version', { 'name': key, 'author': version.get('author') or author, 'time': get(version, 'time', prefix=path), }) SubElement(xmlver, 'changelog').text = CDATA(get(version, 'changelog', path)) file_list = get(version, 'files', path) for i in range(len(file_list)): filedef = file_list[i] cpath = path + ['files', str(i + 1)] if isinstance(filedef, str): # Simple case: just a string SubElement(xmlver, 'source').text = replace_text(filedef, vars, cpath) else: # Complex case, dictionary with fields # TODO process variables attribs = filedef.copy() for k, v in attribs.items(): attribs[k] = replace_text(str(v), vars, cpath) src = attribs.pop('src') SubElement(xmlver, 'source', attribs).text = src return xmlver
def encode_item(self, request, item, search_context): link_url = request.build_absolute_uri( "%s?%s" % (reverse("ows"), urlencode( dict( service="WCS", version="2.0.1", request="DescribeCoverage", coverageId=item.identifier, )))) rss_item = RSS( "item", RSS("title", item.identifier), RSS("description", CDATA(item.identifier)), RSS("link", link_url), ) if "geo" in search_context.parameters: rss_item.append(RSS("guid", request.build_absolute_uri())) else: rss_item.append(RSS("guid", item.identifier, isPermaLink="false")) rss_item.extend(self.encode_item_links(request, item)) # TODO: remove this for the general dc:date? if item.begin_time and item.end_time: rss_item.append( GML("TimePeriod", GML("beginPosition", isoformat(item.begin_time)), GML("endPosition", isoformat(item.end_time)), **{ns_gml("id"): item.identifier})) rss_item.extend(self.encode_spatio_temporal(item)) return rss_item
def makeLayers(self, root, mapp, mapkey): for l in mapp[mapkey]: layer = etree.SubElement(root, "Layer", name=l["name"].replace("\"", "")) for key in l.keys(): if "include" == key: # import the file? pass elif "name" == key: pass elif "status" == key: layer.set("status", l[key]) elif "type" == key: layer.set("type", l[key]) elif "classes" == key: for s in l[key]: class_ = etree.SubElement(layer, "Class") for k in s.keys(): if k == 'name' or k == 'status': class_.set(k, s[k].replace('"', '') .replace("'", "")) elif k == 'styles': st = s['styles'] for sty in st: stEl = etree.SubElement(class_, "Style") for key in sty.keys(): self.makeSubElement(stEl, sty, key) stEl = self.sortChildren(stEl, stEl) elif "labels" == k: kx = self.makeLabels(s, class_, k) else: self.makeSubElement(class_, s, k) class_ = self.sortChildren(class_, class_) elif "metadata" == key: meta = etree.SubElement(layer, "Metadata") c = l[key] for kx in c: item = etree.SubElement(meta, "item", name=kx.replace("'", "") .replace("\"", "")) item.text = c[kx].replace("'", "").replace("\"", "") elif "validation" == key: validation = etree.SubElement(layer, "Validation") for k in l["validation"]: item = etree.SubElement(validation, "item", name=k[0].replace("'", "") .replace("\"", "")) item.text = k[1].replace("'", "").replace("\"", "") elif "data" == key: data = etree.SubElement(layer, "Data") data.text = CDATA(l["data"][0]) else: self.makeSubElement(layer, l, key) layer = self.sortChildren(layer, layer)
def to_element(self) -> ElementT: """Returns image element for xml.""" image = create_element('image', children=[ create_element('url', self.url), create_element('link', self.link), create_element('title', CDATA(self.title)) ]) if self.description: image.append(create_element('description', CDATA(self.description))) if self.height: image.append(create_element('height', self.height)) if self.width: image.append(create_element('width', self.width)) return image
def to_xml(self) -> Element: """Serialize the taskstore into a lxml element.""" root = Element('tasklist') for task in self.lookup.values(): element = SubElement(root, self.XML_TAG) element.set('id', str(task.id)) element.set('status', task.status.value) title = SubElement(element, 'title') title.text = task.title tags = SubElement(element, 'tags') for t in task.tags: tag_tag = SubElement(tags, 'tag') tag_tag.text = str(t.id) dates = SubElement(element, 'dates') added_date = SubElement(dates, 'added') added_date.text = str(task.date_added) modified_date = SubElement(dates, 'modified') modified_date.text = str(task.date_modified) if task.status == Status.DONE: done_date = SubElement(dates, 'done') done_date.text = str(task.date_closed) if task.date_due: due = SubElement(dates, 'due') due.text = str(task.date_due) if task.date_start: start = SubElement(dates, 'start') start.text = str(task.date_start) subtasks = SubElement(element, 'subtasks') for subtask in task.children: sub = SubElement(subtasks, 'sub') sub.text = str(subtask.id) content = SubElement(element, 'content') text = task.content # Poor man's encoding. # CDATA's only poison is this combination of characters. text = text.replace(']]>', ']]>') content.text = CDATA(text) return root
def process_package_covterms(self, child_element): for option_cov_term in child_element: if option_cov_term.tag == 'Packages': for cov_term_opt in option_cov_term: for cov_term_opt_item in cov_term_opt: if cov_term_opt_item.text is not None: if cov_term_opt_item.tag == 'PackageTerms': pass else: cov_term_opt_item.text = CDATA(cov_term_opt_item.text) return self
def encode_entry(self, request, item): entry = ATOM( "entry", ATOM("title", item.identifier), ATOM("id", item.identifier), ATOM("summary", CDATA(item.identifier)), ) entry.extend(self.encode_item_links(request, item)) entry.extend(self.encode_spatio_temporal(item)) return entry
def _create_xml_content(self, export_data): """ Creates an xml content based on the input data and the EDI report template and puts it into the 'body' field of the 'self'. :param export_data: a list of dictionaries with element names and values This method is called explicitly when the EDI document is created. """ report_obj = self.env['report'] self.body = report_obj.get_html( self, 'eintegration_edi_manager.edi_document_report_template') root = etree.fromstring(self.body.lstrip()) message_body = root.find('.//MESSAGEBODY') element_list = [] for export_data_item in export_data: top = None for exported_field in export_data_item['names']: element_names = exported_field.split('/') field_element = None parent_element = top for element_name in element_names: if top is None: field_element = etree.Element(element_name) top = field_element else: field_element = parent_element.find(element_name) if field_element is None: field_element = parent_element if parent_element.tag == element_name else etree.SubElement( parent_element, element_name) parent_element = field_element data_value = export_data_item['data'].pop(0) if isinstance(data_value, (int, float)): field_element.text = CDATA(str(data_value)) else: field_element.text = CDATA(data_value) element_list.append(top) for element in element_list: message_body.append(element) self.body = "<?xml version='1.0' encoding='ISO-8859-1'?>\n" + etree.tostring( root, pretty_print=True)
def cdata_wrap(text): if text is None: return None try: if re.search("[<>\'\"]", text) is not None: return CDATA(text) pass return text except ValueError as ve: raise except TypeError as te: raise
def element_artwork(self, e, p): if e.text and e.text.strip(): e.text = CDATA( e.text ) # prevent text from being mucked up by other processors stripattr(e, [ 'height', '{http://www.w3.org/XML/1998/namespace}space', 'width', ]) if e.text and re.search(r'^\s*<CODE BEGINS>', e.text): # We have source code. Permitted attributes: anchor, name, # source, type. e = self.replace(e, 'sourcecode') e.set('markers', 'true') match = re.search( r'(?s)^\s*<CODE BEGINS>(\s+file\s+"([^"]*)")?(.*?)(<CODE ENDS>(.*))?$', e.text) file = match.group(1) name = match.group(2) body = match.group(3) ends = match.group(4) tail = match.group(5) if file and name: e.set('name', name) e.text = CDATA(body) if not ends: self.warn(e, "Found <CODE BEGINS> without matching <CODE ENDS>") if ends and tail.strip() != "": self.warn(e, "Found non-whitespace content after <CODE ENDS>") e.tail = tail stripattr(e, [ 'align', 'alt', 'height', 'suppress-title', 'width', ])
def new_dict_to_xml(self, indata): log.debug("new_dict_to_xml=%s", indata) from lxml import etree from lxml.etree import CDATA root = etree.Element("xml") for k, v in indata.items(): field = etree.SubElement(root, str(k)) nv = str(v) if type(v) not in types.StringTypes else v log.debug("new_dict_to_xml=%s", nv) if k == 'detail': field.text = CDATA(nv) else: field.text = nv xmlstr = etree.tostring(root, encoding='utf-8') return xmlstr
def _output(self, texts: Tuple[bytes, ...]) -> bytes: feed_type_desc = self._feed_type_desc items = [ item for text in texts for item in self._hext_rule_extract(Html(text.decode())) ] items = list(unique_everseen(items, json.dumps)) log.info('HTML inputs %s have %s items in all.', feed_type_desc, len(items)) feed = self._init_feed() is_debug_logged = self._is_debug_logged for item in items: if item["code_link"].startswith("https://github.com/"): item["code_author"] = item["code_link"].removeprefix( "https://github.com/").split("/")[0] item[ "title"] = "/" + item["code_author"] + "/ " + item["title"] if 'categories' not in item: item['categories'] = [] elif isinstance(item['categories'], str): item['categories'] = [item['categories']] entry = feed.add_entry(order='append') entry.title(item['title']) entry.link(href=item['link']) entry.guid(item['link'], permalink=True) # description = '\n\n'.join((item['description'], item['code_link'])) description = f'{item["description"]} <p>Code: <a href="{item["code_link"]}">{item["code_link"]}</a></p>' entry.description(CDATA(description)) entry.comments(item["code_link"]) for category in item['categories']: if category.startswith( '+') and category[1:].isdigit(): # Ex: +1, +2 continue category = category.capitalize() if category.isupper( ) else category entry.category(term=category) if is_debug_logged: log.debug('Added: %s', item['title']) text_: bytes = feed.rss_str(pretty=True) log.info('XML output %s has %s items.', feed_type_desc, text_.count(b'<item>')) return text_
def clozeQuestion(problemNumber): question = etree.SubElement(quiz, "question", type="cloze") name = etree.SubElement(question, "name") txt = etree.SubElement(name, "text") txt.text = problemID + str(problemNumber) questionText = etree.SubElement(question, "questiontext", format="html") txt = etree.SubElement(questionText, "text") cdatadiv = etree.Element("div") ############################################################################# ## The following lines determine the values that are used in the question: ## ############################################################################# amount = 1000 * int( 5 * random() + 1) # A random multiple of 1000 between 1000 and 5000. interest = int(13 * random() + 4) # Random integer between 4 and 16. years = 2 + int(4 * random()) # Random integer between 2 and 5 ######################################################################### ## The following lines create the text for the question: ## ######################################################################### par = etree.SubElement(cdatadiv, "p") par.text = "If I invest R" + str(amount) + " in a fund that receives " par.text = par.text + str(interest) + " percent interest per year, " par.text = par.text + "compounded monthly, " par.text = par.text + "calculate the value of the account after " par.text = par.text + str(years) + " years." ################################################################################ ## Lastly, these lines create the Cloze answer boxes with the data they need: ## ################################################################################ par = etree.SubElement(cdatadiv, "p") # Start a new paragraph par.text = clozeAnswerElement(1, amount * (1 + interest / 1200)**(12 * years), 0.02) ######################################################################## contentStr = etree.tostring(cdatadiv, pretty_print=True) txt.text = CDATA(contentStr) quiz.append(etree.Comment("New question.")) return
def to_element(self) -> ElementT: """Returns root element for xml.""" root = Element('rss', nsmap=self.root_nsmap, version=self.root_version) channel = create_element( 'channel', children=[ create_element('title', CDATA(self.title)), create_element('description', CDATA(self.description)), create_element('link', self.site_url), create_element('{http://www.w3.org/2005/Atom}link', href=self.feed_url, rel='self', type='application/rss+xml'), create_element('generator', self.generator), create_element('lastBuildDate', datetime.utcnow()) ]) channel_image = self.image if not channel_image and self.image_url: channel_image = Image(self.image_url, self.site_url, self.title) if isinstance(channel_image, Image): channel.append(channel_image.to_element()) for category in self.categories: channel.append(create_element('category', CDATA(category))) if self.pub_date: channel.append(create_element('pubDate', self.pub_date)) if self.copyright: channel.append(create_element('copyright', CDATA(self.copyright))) if self.language: channel.append(create_element('language', CDATA(self.language))) if self.editor: channel.append(create_element('managingEditor', CDATA(self.editor))) if self.webmaster: channel.append(create_element('webMaster', CDATA(self.webmaster))) if self.docs_url: channel.append(create_element('docs', self.docs_url)) for item in self.items: if isinstance(item, Item): channel.append(item.to_element()) root.append(channel) return root
def export_post_comments(self, nid, base_url): wp = self.nsmap['wp'] content = self.nsmap['content'] dsq = self.nsmap['dsq'] post = self.posts[nid] item_node = Element('item', nsmap=self.nsmap) SubElement(item_node, 'title').text = post['title'] SubElement(item_node, 'link').text = base_url + self.get_post_path(post) + '/' SubElement(item_node, '{%s}encoded' % content).text = CDATA(post['body']) SubElement(item_node, '{%s}thread_identifier' % dsq).text = '/' + self.get_post_path(post) + '/' SubElement(item_node, '{%s}post_date_gmt' % wp).text = str(post['date']) SubElement(item_node, '{%s}comment_status' % wp).text = 'open' if nid in self.comments.keys(): for comment in self.comments[nid]: item_node.append(self.export_comment(comment)) return item_node
def multiply_fracture_conductivity(origin_xml, new_xml, multiplier): """Function that generates a nex XML with some multiple of the conductivity ----------- Parameters: origin_xml: String. Full path to the xml to edit new_xml: String. Full path to the new xml multipler: Float. Conductivity will be multipled by this parameter """ parser = etree.XMLParser(strip_cdata=False) tree = etree.parse(origin_xml, parser) root = tree.getroot() # Read the relevant features data = root.find('Mesh').find('Data') aperture = data.find('.//DataSet[@TagName="APERTURE"]') conductivity = data.find( './/DataSet[@TagName="GMFACE_FRACTURE_CONDUCTIVITY"]') # Parse the text data as a pandas DataFrame cond_data = pn.read_csv(StringIO(conductivity.text), sep='\t\t\t\t', header=None, names=['id_face', 'cond']) # Modify the data cond_data.loc[:, 'cond'] = cond_data.cond * multiplier # Enforce data types cond_data = cond_data.astype({'id_face': str, 'cond': str}) pdb.set_trace() # Return the modified data to the text format b = cond_data.values.tolist() step_1 = ['\t\t\t\t'.join(elm) for elm in b] step_2 = '\n\t\t\t\n\t\t\t' + '\n\t\t\t'.join(step_1) + '\n\t\t\t\n\t\t\t' # Add the CDATA tag to the new string step_3 = CDATA(step_2) conductivity.text = step_3 tree.write(new_xml)
def create_question_xml(self, slides_lst): XHTML_NAMESPACE = xml_schema_url XHTML = "{%s}" % XHTML_NAMESPACE NSMAP = { None: XHTML_NAMESPACE, 'xsi': 'http://www.w3.org/2001/XMLSchema-instance' } envelope = Element("HTMLQuestion", nsmap=NSMAP) index_path = Path.cwd() / 'docs' / 'index.html' index_html = index_path.read_text() index_html = index_html.replace( 'var slides_to_test = [12];', 'var slides_to_test = ' + str(slides_lst) + ';') html_content = SubElement(envelope, 'HTMLContent') html_content.text = CDATA(index_html) frame_height = SubElement(envelope, 'FrameHeight') frame_height.text = '0' return tostring(envelope, encoding='unicode')
def get_kml_for_fields(self, scientific_name=None, province_list=None, provider_id_list=None, latitude=None, longitude=None, start_date=None, end_date=None, page=None, items_per_page=None, start_latitude=None, end_latitude=None, start_longitude=None, end_longitude=None, is_simple_search=False): collection_filter = Collection.query.filter( Collection.full_scientific_name.like('%' + scientific_name + '%'), Collection.province.in_(province_list) if province_list else True, Collection.provider_id.in_(provider_id_list) if provider_id_list else True, Collection.latitude >= start_latitude if start_latitude else True, Collection.latitude <= end_latitude if end_latitude else True, Collection.longitude >= start_longitude if start_longitude else True, Collection.longitude <= end_longitude if end_longitude else True, Collection.last_date >= start_date if start_date else True, Collection.last_date <= end_date if end_date else True) if collection_filter.count() == 0: return None output = io.BytesIO() from lxml import etree as et root_el = et.Element("kml", xmlns="http://www.opengis.net/kml/2.2") document = et.SubElement(root_el, "Document", id="root_doc") folder = et.SubElement(document, "Folder") et.SubElement(folder, "name").text = "BioNoMo data" from unidecode import unidecode for collection in collection_filter: if collection.longitude and collection.latitude: placemark = et.SubElement(folder, "Placemark") _content = """ <span style='font-weight: bold'>{}</span><br><br> {}: <span style='font-style: italic'>{}</span><br> {}: <span style='font-style: italic'>{}</span><br> {}: <span style='font-style: italic'>{}</span><br> <br> <span style='font-weight: bold'>{}</span><br> {}<br> """.format( collection.full_scientific_name, unidecode(collection.get_header('header.province')), collection.province if collection.province else "---", unidecode(collection.get_header('header.provider')), collection.provider.name if collection.provider else "---", unidecode(collection.get_header('header.last_date')), collection.last_date if collection.last_date else "---", unidecode(collection.get_header('header.taxonomy')), self.get_taxonomical_tree_as_str_html(collection)) point = et.SubElement(placemark, "Point") et.SubElement(point, "coordinates").text = "{},{}"\ .format(collection.longitude, collection.latitude) et.SubElement(placemark, "description").text = CDATA(_content) tree = et.ElementTree(root_el) tree.write(output, pretty_print=False, xml_declaration=True, encoding='utf-8', compression=1) return (output, 'zip')
def to_file(self): """ returns: - filename: STR - fsdb: FSDB xml data, to be used in frontend.""" formula = self.comp.formula pilots = self.comp.participants '''create dicts of attributes for each section''' comp_attr = { 'id': '', # still to do 'name': self.comp.comp_name, 'location': self.comp.comp_site, 'from': self.comp.date_from, 'to': self.comp.date_to, 'utc_offset': self.comp.time_offset / 3600, 'discipline': self.comp.comp_class.lower(), 'ftv_factor': round(1 - formula.validity_param, 2), 'fai_sanctioning': (1 if self.comp.sanction == 'FAI 1' else 2 if self.comp.sanction == 'FAI 2' else 0) } formula_attr = { 'id': formula.formula_name, 'min_dist': km(formula.min_dist, 1), 'nom_dist': km(formula.nominal_dist, 1), 'nom_time': formula.nominal_time / 3600, 'nom_launch': formula.nominal_launch, 'nom_goal': formula.nominal_goal, 'day_quality_override': 0, # still to implement 'bonus_gr': formula.glide_bonus, 'jump_the_gun_factor': (0 if formula.max_JTG == 0 else round( 1 / formula.JTG_penalty_per_sec, 1)), 'jump_the_gun_max': formula.max_JTG, 'normalize_1000_before_day_quality': 0, # still to implement 'time_points_if_not_in_goal': round(1 - formula.no_goal_penalty, 1), 'use_1000_points_for_max_day_quality': 0, # still to implement 'use_arrival_position_points': 1 if formula.formula_arrival == 'position' else 0, 'use_arrival_time_points': 1 if formula.formula_arrival == 'time' else 0, 'use_departure_points': 1 if formula.formula_departure == 'departure' else 0, 'use_difficulty_for_distance_points': 1 if formula.formula_distance == 'difficulty' else 0, 'use_distance_points': 1 if formula.formula_distance != 'off' else 0, 'use_distance_squared_for_LC': 1 if formula.comp_class == 'PG' else 0, # still to implement 'use_leading_points': 1 if formula.formula_departure == 'leadout' else 0, 'use_semi_circle_control_zone_for_goal_line': 1, # still to implement 'use_time_points': 1 if formula.formula_time == 'on' else 0, 'scoring_altitude': 'GPS' if formula.scoring_altitude == 'GPS' else 'QNH', 'final_glide_decelerator': 'none' if formula.arr_alt_bonus == 0 else 'aatb', 'no_final_glide_decelerator_reason': '', 'min_time_span_for_valid_task': 60 if self.comp_class == 'PG' else 0, # still to implement 'score_back_time': formula.score_back_time / 60, 'use_proportional_leading_weight_if_nobody_in_goal': '', # still to implement 'leading_weight_factor': (0 if formula.formula_departure != 'leadout' else round( formula.lead_factor, 3)), 'turnpoint_radius_tolerance': formula.tolerance, 'use_arrival_altitude_points': 0 if formula.arr_alt_bonus == 0 else '' # still to implement } if formula.arr_alt_bonus > 0: formula_attr['aatb_factor'] = round(formula.arr_alt_bonus, 3) '''create the file structure''' root = ET.Element('Fs') root.set('version', '3.5') root.set('comment', 'generated by AirScore') '''FsCompetition''' comp = ET.SubElement(root, 'FsCompetition') for k, v in comp_attr.items(): comp.set(k, str(v)) formula = ET.SubElement(comp, 'FsScoreFormula') for k, v in formula_attr.items(): formula.set(k, str(v)) notes = ET.SubElement(comp, 'FsCompetitionNotes') notes.text = CDATA('Generated by AirScore') # notes.text = '<![CDATA[Generated by AirScore]]>' '''FsParticipants''' participants = ET.SubElement(comp, 'FsParticipants') for p in pilots: pil = ET.SubElement(participants, 'FsParticipant') pilot_attr = { 'id': p.ID or p.par_id, 'name': p.name, 'birthday': p.pilot_birthdate_str, 'glider': p.glider, 'glider_main_colors': '', 'fai_licence': 1 if p.fai_id else 0, 'female': p.female, 'nat_code_3166_a3': p.nat, 'sponsor': p.sponsor, 'CIVLID': p.civl_id, } custom_attr = { 'fai_n': p.fai_id, 'class': p.glider_cert, 'team': p.team, 'LIVE': p.live_id } for k, v in pilot_attr.items(): pil.set(k, str(v)) cus = ET.SubElement(pil, 'FsCustomAttributes') for k, v in custom_attr.items(): sub = ET.SubElement(cus, 'FsCustomAttribute') sub.set('name', k) sub.set('value', str(v)) '''FsTasks''' tasks = ET.SubElement(comp, 'FsTasks') for idx, t in enumerate(self.tasks): task = ET.SubElement(tasks, 'FsTask') task.set('id', str(idx + 1)) task.set('name', t.task_name) task.set('tracklog_folder', '') task_f = ET.SubElement(task, 'FsScoreFormula') task_d = ET.SubElement(task, 'FsTaskDefinition') task_s = ET.SubElement(task, 'FsTaskState') task_p = ET.SubElement(task, 'FsParticipants') task_sp = ET.SubElement(task, 'FsTaskScoreParams') # tf = dict(t.formula.to_dict(), **t.stats) '''FsTaskState''' task_s.set('task_state', ('REGULAR' if not t.stopped_time else 'STOPPED')) # ? task_s.set('score_back_time', str(t.formula.score_back_time / 60)) task_s.set('cancel_reason', t.comment) '''FsScoreFormula''' # we permit just few changes in single tasks from comp formula, so we just update those tf_attr = formula_attr tf_attr.update({ 'jump_the_gun_factor': (0 if not t.formula.JTG_penalty_per_sec else round( 1 / t.formula.JTG_penalty_per_sec, 1)), 'time_points_if_not_in_goal': 1 - t.formula.no_goal_penalty, 'use_arrival_position_points': 1 if t.formula.arrival == 'position' else 0, 'use_arrival_time_points': 1 if t.formula.arrival == 'time' else 0, 'use_departure_points': 1 if t.formula.departure == 'departure' else 0, 'use_difficulty_for_distance_points': 1 if t.formula.distance == 'difficulty' else 0, 'use_distance_points': 0 if t.formula.distance == 'off' else 1, 'use_leading_points': 0 if t.formula.departure == 'off' else 1, 'use_time_points': 0 if t.formula.time == 'off' else 1, 'scoring_altitude': 'GPS' if t.formula.scoring_altitude == 'GPS' else 'QNH', 'final_glide_decelerator': 'none' if t.formula.arr_alt_bonus == 0 else 'aatb', 'use_arrival_altitude_points': 0 if t.formula.arr_alt_bonus == 0 else 1, 'turnpoint_radius_tolerance': t.formula.tolerance, }) for k, v in tf_attr.items(): task_f.set(k, str(v)) '''FsTaskDefinition''' tps = t.turnpoints td_attr = { 'ss': [i + 1 for i, tp in enumerate(tps) if tp.type == 'speed'].pop(0), 'es': [i + 1 for i, tp in enumerate(tps) if tp.type == 'endspeed'].pop(0), 'goal': next(tp.shape for tp in tps if tp.type == 'goal').upper(), 'groundstart': 0, # still to implement 'qnh_setting': 1013.25 # still to implement } for k, v in td_attr.items(): task_d.set(k, str(v)) t_open = get_isotime(t.date, t.window_open_time, t.time_offset) t_close = get_isotime(t.date, t.task_deadline, t.time_offset) ss_open = get_isotime(t.date, t.start_time, t.time_offset) if t.start_close_time: ss_close = get_isotime(t.date, t.start_close_time, t.time_offset) else: ss_close = t_close if t.window_close_time: w_close = get_isotime(t.date, t.window_close_time, t.time_offset) else: w_close = ss_close for i, tp in enumerate(tps): task_tp = ET.SubElement(task_d, 'FsTurnpoint') tp_attr = { 'id': tp.name, 'lat': round(tp.lat, 5), 'lon': round(tp.lon, 5), 'altitude': tp.altitude, 'radius': tp.radius, 'open': t_open if i < (td_attr['ss'] - 1) else ss_open, 'close': w_close if i == 0 else ss_close if i == (td_attr['ss'] - 1) else t_close } for k, v in tp_attr.items(): task_tp.set(k, str(v)) '''we add also FsTaskDistToTp during tp iteration''' sp_dist = ET.SubElement(task_sp, 'FsTaskDistToTp') sp_dist.set('tp_no', str(i + 1)) sp_dist.set('distance', str(t.partial_distance[i])) '''add start gates''' gates = 1 if t.SS_interval > 0: gates += t.start_iteration for i in range(gates): task_sg = ET.SubElement(task_d, 'FsStartGate') intv = 0 if not t.SS_interval else t.SS_interval * i i_time = get_isotime(t.date, (t.start_time + intv), t.time_offset) task_sg.set('open', str(i_time)) '''FsTaskScoreParams''' launch_ess = [ t.partial_distance[i] for i, tp in enumerate(t.turnpoints) if tp.type == 'endspeed' ].pop() sp_attr = { 'ss_distance': km(t.SS_distance), 'task_distance': km(t.opt_dist), 'launch_to_ess_distance': km(launch_ess), 'no_of_pilots_present': t.pilots_present, 'no_of_pilots_flying': t.pilots_launched, 'no_of_pilots_lo': t.pilots_launched - t.pilots_goal, 'no_of_pilots_reaching_nom_dist': len([ x for x in t.valid_results if x.distance_flown > t.formula.nominal_dist ]), 'no_of_pilots_reaching_es': t.pilots_ess, 'no_of_pilots_reaching_goal': t.pilots_goal, 'sum_flown_distance': km(t.tot_distance_flown), 'best_dist': km(t.max_distance or 0), 'best_time': round((t.fastest or 0) / 3600, 14), 'worst_time': round( max((x.ESS_time or 0) - (x.SSS_time or 0) for x in t.valid_results) / 3600, 14), 'no_of_pilots_in_competition': len(self.comp.participants), 'no_of_pilots_landed_before_stop': 0 if not t.stopped_time else t.pilots_landed, 'sum_dist_over_min': km(t.tot_dist_over_min), 'sum_real_dist_over_min': km(t.tot_dist_over_min), # not yet implemented 'best_real_dist': km(t.max_distance), # not yet implemented 'last_start_time': get_isotime( t.date, max([ x.SSS_time for x in t.valid_results if x.SSS_time is not None ]), t.time_offset), 'first_start_time': ('' if not t.min_dept_time else get_isotime( t.date, t.min_dept_time, t.time_offset)), 'first_finish_time': ('' if not t.min_ess_time else get_isotime( t.date, t.min_ess_time, t.time_offset)), 'max_time_to_get_time_points': round(0 / 3600, 14), # not yet implemented 'no_of_pilots_with_time_points': len([x for x in t.valid_results if x.time_score > 0]), 'goal_ratio': (0 if t.pilots_launched == 0 else round( t.pilots_goal / t.pilots_launched, 15)), 'arrival_weight': 0 if t.arrival == 0 else round(t.arr_weight, 3), 'departure_weight': 0 if t.departure != 'on' else round(t.dep_weight, 3), 'leading_weight': 0 if t.departure != 'leadout' else round(t.dep_weight, 3), 'time_weight': 0 if t.arrival == 'off' else round(t.time_weight, 3), 'distance_weight': round(t.dist_weight, 3), # not yet implemented 'smallest_leading_coefficient': round(t.min_lead_coeff, 14), 'available_points_distance': round(t.avail_dist_points, 14), 'available_points_time': round(t.avail_time_points, 14), 'available_points_departure': (0 if not t.formula.departure == 'departure' else round( t.avail_dep_points, 14)), 'available_points_leading': (0 if not t.formula.departure == 'leadout' else round( t.avail_dep_points, 14)), 'available_points_arrival': round(t.avail_arr_points, 14), 'time_validity': round(t.time_validity, 3), 'launch_validity': round(t.launch_validity, 3), 'distance_validity': round(t.dist_validity, 3), 'stop_validity': round(t.stop_validity, 3), 'day_quality': round(t.day_quality, 3), 'ftv_day_validity': t.ftv_validity, 'time_points_stop_correction': 0 # not yet implemented } for k, v in sp_attr.items(): task_sp.set(k, str(v)) '''FsParticipants''' for i, pil in enumerate(t.pilots): '''create pilot result for the task''' pil_p = ET.SubElement(task_p, 'FsParticipant') pil_p.set('id', str(pil.ID or pil.par_id)) if not (pil.result_type in ('abs', 'dnf', 'nyp')): '''only if pilot flew''' pil_fd = ET.SubElement(pil_p, 'FsFlightData') pil_r = ET.SubElement(pil_p, 'FsResult') if not (pil.result_type in ['mindist', 'min_dist']): fd_attr = { 'distance': km(pil.distance_flown), 'bonus_distance': km(pil.distance), # ?? seems 0 for PG and more than dist for HG 'started_ss': '' if not pil.real_start_time else get_isotime( t.date, pil.real_start_time, t.time_offset), 'finished_ss': '' if not pil.ESS_time else get_isotime( t.date, pil.ESS_time, t.time_offset), 'altitude_at_ess': pil.ESS_altitude, 'finished_task': '' if not pil.goal_time else get_isotime( t.date, pil.goal_time, t.time_offset), 'tracklog_filename': pil.track_file, 'lc': pil.lead_coeff, 'iv': pil.fixed_LC or '', 'ts': get_isotime(t.date, pil.first_time, t.time_offset), 'alt': pil.last_altitude, # ?? 'bonus_alt': '', # ?? not implemented 'max_alt': pil.max_altitude, 'last_tracklog_point_distance': '', # not implemented yet 'bonus_last_tracklog_point_distance': '', # ?? not implemented 'last_tracklog_point_time': get_isotime(t.date, pil.landing_time, t.time_offset), 'last_tracklog_point_alt': pil.landing_altitude, 'landed_before_deadline': '1' if pil.landing_time < (t.task_deadline if not t.stopped_time else t.stopped_time) else '0', 'reachedGoal': 1 if pil.goal_time else 0 # only deadline? } for k, v in fd_attr.items(): pil_fd.set(k, str(v)) r_attr = { 'rank': i + 1, # not implemented, they should be ordered tho # Rank IS NOT SAFE (I guess) 'points': round(pil.score), 'distance': km(pil.total_distance if pil.total_distance else pil. distance_flown), 'ss_time': '' if not pil.ss_time else sec_to_time( pil.ss_time).strftime('%H:%M:%S'), 'finished_ss_rank': '' if not pil.ESS_time and pil.ESS_rank else pil.ESS_rank, 'distance_points': 0 if not pil.distance_score else round( pil.distance_score, 1), 'time_points': 0 if not pil.time_score else round(pil.time_score, 1), 'arrival_points': 0 if not pil.arrival_score else round( pil.arrival_score, 1), 'departure_points': 0 if not t.formula.departure == 'departure' else round( pil.departure_score, 1), 'leading_points': 0 if not t.formula.departure == 'leadout' else round( pil.departure_score, 1), 'penalty': 0 if not [ n for n in pil.notifications if n.percentage_penalty > 0 ] else max(n.percentage_penalty for n in pil.notifications), 'penalty_points': 0 if not [ n for n in pil.notifications if n.flat_penalty > 0 ] else max(n.flat_penalty for n in pil.notifications), 'penalty_reason': '; '.join([ n.comment for n in pil.notifications if n.flat_penalty + n.percentage_penalty > 0 and not n.notification_type == 'jtg' ]), 'penalty_points_auto': sum(n.flat_penalty for n in pil.notifications if n.notification_type == 'jtg'), 'penalty_reason_auto': '' if not [ n for n in pil.notifications if n.notification_type == 'jtg' ] else next( n for n in pil.notifications if n.notification_type == 'jtg').flat_penalty, 'penalty_min_dist_points': 0, # ?? 'got_time_but_not_goal_penalty': (pil.ESS_time or 0) > 0 and not pil.goal_time, 'started_ss': '' if not pil.real_start_time else get_isotime( t.date, pil.SSS_time, t.time_offset), 'finished_ss': '' if not pil.ESS_time else get_isotime( t.date, pil.ESS_time, t.time_offset), 'ss_time_dec_hours': 0 if not pil.ESS_time else round( pil.ss_time / 3600, 14), 'ts': get_isotime(t.date, pil.first_time, t.time_offset), # flight origin time 'real_distance': km(pil.distance_flown), 'last_distance': '', # ?? last fix distance? 'last_altitude_above_goal': pil.last_altitude, 'altitude_bonus_seconds': 0, # not implemented 'altitude_bonus_time': sec_to_time(0).strftime('%H:%M:%S'), # not implemented 'altitude_at_ess': pil.ESS_altitude, 'scored_ss_time': ('' if not pil.ss_time else sec_to_time( pil.ss_time).strftime('%H:%M:%S')), 'landed_before_stop': t.stopped_time and res.landing_time < t.stopped_time } for k, v in r_attr.items(): pil_r.set(k, str(v)) '''creates the file to store''' fsdb = ET.tostring(root, pretty_print=True, xml_declaration=True, encoding='UTF-8') return self.filename, fsdb
point.set('IsXOnly', 'False') point.set('Index', '329') positionScreen = SubElement(point, 'PositionScreen') positionScreen.set('X', '{}'.format(xScreen)) positionScreen.set('Y', '{}'.format(yScreen)) document = Element('Document') document.set('VersionNumber', '11.0') document.set('AxesPointsRequired', '0') image = SubElement(document, 'Image') image.set('Width', '605') image.set('Height', '340') image.text = CDATA( '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' ) coordSystem = SubElement(document, 'CoordSystem') general = SubElement(coordSystem, 'General') general.set('CursorSize', '3') general.set('ExtraPrecision', '1') coords = SubElement(coordSystem, 'Coords') coords.set('Type', '0') coords.set('TypeString', 'Cartesian') coords.set('Coords', '0') coords.set('ScaleXTheta', '0') coords.set('ScaleXThetaString', 'Linear') coords.set('ScaleYRadius', '0') coords.set('ScaleYRadiusString', 'Linear') coords.set('UnitsX', '0')