Exemplo n.º 1
0
def get_button(lang_n="", isExtended=False, full=True):
    """Provides a download form.
    ARGUMENTS:
    - 'lang_n'      :   the glottocode
    - 'ext'         :   if for the extended corpus
    - 'full'        :   if all fields (or just the button)"""
    xml = "<span class='span2' style='margin-left:0px'>no xml</span>"
    wav_size = 0
    eaf_size = 0
    tg_size = 0
    xml_size = 0
    tsv_size = 0
    txt = "<span class='span2' style='margin-left:0px'>Dataset files :</span>"
    wav = "<span class='span2' style='margin-left:0px'>" \
              "<button class='btn btn-warning' value='wav' name='format' type='input'>WAV</button> " + str(wav_size) + " MB</span>"
    eaf = "<span class='span2' style='margin-left:0px'>" \
          "<button class='btn btn-primary' value='eaf' name='format' type='input'>eaf</button> " + str(eaf_size) + " MB</span>"
    textgrid = "<span class='span2' style='margin-left:0px'>" \
               "<button class='btn btn-danger' value='textgrid' name='format' type='input'>TextGrid</button> " + str(tg_size) + " MB</span>"
    if xml_size>0:
        xml = "<span class='span2' style='margin-left:0px'>" \
          "<button class='btn btn-info' value='xml' name='format' type='input'>eaf</button> " + str(xml_size) + " MB</span>"
    tsv = "<span class='span2' style='margin-left:0px'>" \
          "<button class='btn btn-info' value='tsv' name='format' type='input'>eaf</button> " + str(tsv_size) + " MB</span>"

    form = "<form method=\"post\" action=\"/doreLoad\" style='margin:0px;line-height:3em;'>" + \
           "<input type=\"hidden\" id=\"f_id\" " "name=\"id\" value=\"" + lang_n + "\">" + \
           "<input type=\"hidden\" id=\"f_ext\" name=\"extended\" value=\"" + str(isExtended) + "\">"
    if isExtended:
        return literal(form + txt + eaf + "</form>")
    else:
        return literal(form + txt + wav + eaf + textgrid + xml + tsv + "</form>")
Exemplo n.º 2
0
def text2html(text, mode='br', sep='\n\n'):
    """
    >>> assert 'div' in unicode(text2html('chunk', mode='p'))
    """
    if mode == 'p':
        return HTML.div(*[HTML.p(literal(newline2br(line))) for line in text.split(sep)])
    return HTML.p(literal(newline2br(text)))
Exemplo n.º 3
0
    def gloss_with_tooltip(gloss):
        person_map = {
            '1': 'first person',
            '2': 'second person',
            '3': 'third person',
        }

        res = []
        end = 0
        for match in GLOSS_ABBR_PATTERN.finditer(gloss):
            if match.start() > end:
                res.append(literal(gloss[end:match.start()]))

            abbr = match.group('abbr')
            if abbr in abbrs:
                explanation = abbrs[abbr]
                if match.group('personprefix'):
                    explanation = '%s %s' % (
                        person_map[match.group('personprefix')], explanation)

                if match.group('personsuffix'):
                    explanation = '%s %s' % (
                        explanation, person_map[match.group('personsuffix')])

                res.append(HTML.span(
                    HTML.span(gloss[match.start():match.end()].lower(), class_='sc'),
                    **{'data-hint': explanation, 'class': 'hint--bottom'}))
            else:
                res.append(abbr)

            end = match.end()

        res.append(literal(gloss[end:]))
        return filter(None, res)
Exemplo n.º 4
0
def text2html(text, mode="br", sep="\n\n"):
    """Turn plain text into simple HTML.

    >>> assert 'div' in text_type(text2html('chunk', mode='p'))
    """
    if mode == "p":
        return HTML.div(*[HTML.p(literal(newline2br(line))) for line in text.split(sep)])
    return HTML.p(literal(newline2br(text)))
Exemplo n.º 5
0
def text2html(text, mode='br', sep='\n\n'):
    """
    >>> assert 'div' in unicode(text2html('chunk', mode='p'))
    """
    if mode == 'p':
        return HTML.div(
            *[HTML.p(literal(newline2br(line))) for line in text.split(sep)])
    return HTML.p(literal(newline2br(text)))
Exemplo n.º 6
0
 def col_defs(self):
     return list(
         filter(lambda col: not self.semanticfield or col.name != 'sf', [
             LWTCodeCol(self, 'lwt_code'),
             LinkCol(
                 self,
                 'name',
                 sTitle='Meaning',
                 sDescription=
                 "This column shows the labels of the Loanword Typology "
                 "meanings. By clicking on a meaning label, you get more information "
                 "about the meaning, as well as a list of all words that are counterparts "
                 "of that meaning."),
             CoreListCol(self, 'core_list'),
             Col(self,
                 'cat',
                 sTitle='Semantic category',
                 sDescription=
                 "Meanings were assigned to semantic categories with "
                 "word-class-like labels: nouns, verbs, adjectives, adverbs, function "
                 "words. No claim is made about the grammatical behavior of words "
                 "corresponding to these meanings. The categories are intended to be "
                 "purely semantic.",
                 model_col=Meaning.semantic_category,
                 choices=get_distinct_values(Meaning.semantic_category)),
             SemanticFieldCol(
                 self,
                 'sf',
                 sTitle='Semantic field',
                 sDescription=
                 "The first 22 fields are the fields of the Intercontinental "
                 "Dictionary Series meaning list, proposed by Mary Ritchie Key, and "
                 "ultimately based on Carl Darling Buck's (1949) Dictionary of selected "
                 "synonyms in the principal Indo-European languages. The other two fields "
                 "were added for the Loanword Typology project."),
             MeaningScoreCol(self,
                             'borrowed_score',
                             sDescription=literal(
                                 '%s' % hb_borrowed_score())),
             MeaningScoreCol(self,
                             'age_score',
                             sDescription=literal('%s' % hb_age_score())),
             MeaningScoreCol(self,
                             'simplicity_score',
                             sDescription=literal(
                                 '%s' % hb_simplicity_score())),
             Col(self,
                 'representation',
                 model_col=Meaning.representation,
                 sDescription=
                 "This column shows how many counterparts for this meaning "
                 "there are in the 41 languages. The number can be higher than 41 because "
                 "a language may have several counterparts for one meaning (\"synonyms\"),"
                 " and it may be lower than 41, because not all languages may have a "
                 "counterpart for a meaning. "),
         ]))
Exemplo n.º 7
0
 def col_defs(self):
     return [
         # ID, Vocabulary, Authors, Number of words, Percentage of loanwords, cite
         IntegerIdCol(
             self,
             'id',
             sTitle="ID",
             sDescription=literal(
                 "The vocabulary ID number corresponds to the ordering to the"
                 " chapters on the book <em>Loanwords in the World's Languages</em>. "
                 "Languages are listed in rough geographical order from west to east, "
                 "from Africa via Europe to Asia and the Americas, so that "
                 "geographically adjacent languages are next to each other."
             )),
         VocabularyCol(
             self,
             'vocabulary',
             sDescription=literal(
                 "<p>Each vocabulary of WOLD is a separate electronic "
                 "publication with a separate author or team of authors. Each vocabulary "
                 "has a characteristic colour in WOLD.</p><p>Click on a vocabulary to "
                 "see the words (loanwords and nonloanwords) and their properties.</p>"
             )),
         ContributorsCol(
             self,
             'contributor',
             sDescription=
             "The authors are experts of the language and its history. "
             "They also contributed a prose chapter on the borrowing situation in "
             "their language that was published in the book "
             "Loanwords in the World's Languages."),
         Col(self,
             'n',
             sTitle='Number of words',
             model_col=Vocabulary.count_words,
             sDescription="There would be 1814 words in each vocabulary, "
             "corresponding to the 1814 Loanword Typology meanings, if each meaning "
             "had exactly one counterpart, and if all the counterparts were "
             "different words. But many (\"polysomous\") words are counterparts of "
             "several meanings, many meanings have several word counterparts "
             "(\"synonyms\", or \"subcounterparts\"), and many meanings have no "
             "counterparts at all, so the number of words in each database varies "
             "considerably."),
         PercentCol(
             self,
             'p',
             sTitle='Percentage of loanwords',
             model_col=Vocabulary.borrowed_score,
             sDescription=
             "This gives the percentage of words in each language that "
             "are \"clearly borrowed\" or \"probably borrowed\"."),
         CitationCol(self, 'cite'),
     ]
Exemplo n.º 8
0
    def get_legends(self):
        items = []

        for biome in DBSession.query(Biome)\
                .filter(Biome.description != 'ffffff')\
                .order_by(as_int(Biome.id)):
            items.append(
                HTML.label(
                    HTML.span(
                        literal('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'),
                        style='background-color: #%s;' % biome.description,
                        class_='biome-color'),
                    literal(biome.name),
                    style='margin-left: 1em; margin-right: 1em;'))
        yield Legend(self, 'categories', items)
Exemplo n.º 9
0
    def get_legends(self):
        items = []

        for biome in DBSession.query(Biome)\
                .filter(Biome.description != 'ffffff')\
                .order_by(as_int(Biome.id)):
            items.append(
                HTML.label(
                    HTML.span(
                        literal('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'),
                        style='background-color: #%s;' % biome.description,
                        class_='biome-color'),
                    literal(biome.name),
                    style='margin-left: 1em; margin-right: 1em;'))
        yield Legend(self, 'categories', items)
Exemplo n.º 10
0
 def value_li(l):
     return (
         HTML.img(height="20", width="20", src=self.icon_map[l.pk]),
         literal('&nbsp;'),
         languoid_link(self.req, l),
         literal('&nbsp;'),
         HTML.label(HTML.input(
             type="checkbox",
             onclick="GLOTTOLOG3.filterMarkers(this);",
             class_="checkbox inline",
             checked="checked",
             value=str(l.pk)),
                    class_="checkbox inline",
                    title="click to toggle markers"),
     )
Exemplo n.º 11
0
 def value_li(l):
     return (
         HTML.img(height="20", width="20", src=self.icon_map[l.pk]),
         literal('&nbsp;'),
         languoid_link(self.req, l),
         literal('&nbsp;'),
         HTML.label(
             HTML.input(
                 type="checkbox",
                 onclick="GLOTTOLOG3.filterMarkers(this);",
                 class_="checkbox inline",
                 checked="checked",
                 value=str(l.pk)),
             class_="checkbox inline",
             title="click to toggle markers"),
     )
Exemplo n.º 12
0
 def get_attrs(self, item):
     label = item.__unicode__()
     title = label
     if self.dt.parameter:
         label = HTML.span(map_marker_img(self.dt.req, item),
                           literal('&nbsp;'), label)
     return {'label': label, 'title': title}
Exemplo n.º 13
0
def newline2br(text):
    chunks = []
    for i, line in enumerate(text.split('\n')):
        if i > 0:
            chunks.append(HTML.br())
        chunks.append(literal(line))
    return '\n'.join(chunks)
Exemplo n.º 14
0
def get_audio(ctx):
    html_result = "list of audio in nakala"
    languages = DBSession.query(doreLanguage).join(Language, Language.pk == doreLanguage.pk).all()
    for lang in languages:
        if lang.AUDIO is not None and lang.AUDIO != 'na':
            html_result += '<li> ' + lang.name + ' <a href="https://test.nakala.fr/' + lang.AUDIO + '" target=_BLANK>view audio</a><br>' + '<a href="./static/' + lang.id + '.zip">add a direct link to a zip archive of the audio ?</a>' + '</li>'
    return literal(html_result)
Exemplo n.º 15
0
def get_nak(ctx, sour):
    html_result = ""
    lang = DBSession.query(doreLanguage). \
        filter(doreLanguage.id == ctx.id).first()
    if lang.NAK is not None and lang.NAK != 'na':
        html_result += '<a href="https://test.nakala.fr/' + lang.NAK + '" target=_BLANK>view language information on nakala</a></li>'
    return literal(html_result)
Exemplo n.º 16
0
def newline2br(text):
    chunks = []
    for i, line in enumerate(text.split('\n')):
        if i > 0:
            chunks.append(HTML.br())
        chunks.append(literal(line))
    return '\n'.join(chunks)
Exemplo n.º 17
0
 def get_layers(self):
     for canton, dialects in itertools.groupby(
             DBSession.query(models.Variety).order_by(
                 models.Variety.canton, as_int(common.Language.id)),
             lambda l: l.canton):
         dialects = list(dialects)
         json = DialectsGeoJson(None).render(dialects, self.req, dump=False)
         yield Layer(slug(canton),
                     canton,
                     data=json,
                     marker=HTML.span(
                         helpers.map_marker_img(self.req,
                                                dialects[0],
                                                marker=self.map_marker),
                         literal('&nbsp;'),
                         dialects[0].canton_img(self.req),
                         literal('&nbsp;')))
Exemplo n.º 18
0
def legend_items(req):
    for type, label in [('recipient', 'recipient language'),
                        ('donor', 'donor languoid')]:
        yield HTML.label(HTML.img(src=icon_url(req, type),
                                  height='20',
                                  width='20'),
                         literal(' ' + label),
                         style='margin-left: 1em; margin-right: 1em;')
Exemplo n.º 19
0
def feature_description(req, ctx):
    desc = ctx.markup_description or ctx.description
    desc = re.sub(
        "\*\*(?P<id>[0-9]+\-[0-9]+)\*\*",
        lambda m: HTML.a('[%s]' % m.group('id'),
                         href=req.route_url('sentence', id=m.group('id'))),
        desc)

    desc = re.sub(
        "\*\*\<\/span\>(?P<id>[0-9]+\-[0-9]+)\*\*",
        lambda m: literal('</span>') + HTML.a(
            '[%s]' % m.group('id'),
            href=req.route_url('sentence', id=m.group('id'))), desc)

    return re.sub(
        '\<span style\=\"font-style\: italic;\"\>WALS\<\/span\>\s+feature\s+[0-9]+',
        lambda m: HTML.a(literal(desc[m.start():m.end()]),
                         href=req.route_url('wals', id=ctx.id)), desc)
Exemplo n.º 20
0
def linkify_iso_codes(request, text, class_=None, route_name='glottolog.iso'):
    def chunks():
        start = 0
        for match in ISO_PATTERN.finditer(text):
            yield text[start:match.start(0)]
            url = request.route_url(route_name, id=match.group('iso'))
            yield HTML.a(match.group(0), href=url, class_=class_)
            start = match.end(0)
        yield text[start:]
    return literal('').join(chunks())
Exemplo n.º 21
0
Arquivo: helpers.py Projeto: clld/clld
def newline2br(text):
    """Replace newlines in text with HTML br tags."""
    if not text:
        return ''
    chunks = []
    for i, line in enumerate(text.split('\n')):
        if i > 0:
            chunks.append(HTML.br())
        chunks.append(literal(line))
    return '\n'.join(chunks)
Exemplo n.º 22
0
 def js(self):
     return Markup(HTML.script(literal("""\
 $(document).ready(function() {
     $('.%s').clickover({
         html: true,
         title: 'Download information',
         placement: '%s',
         trigger: 'click'
     });
 });""" % (self._opener_class, self.options['doc_position']))))
Exemplo n.º 23
0
 def js(self):
     return Markup(HTML.script(literal("""\
 $(document).ready(function() {
     $('.%s').clickover({
         html: true,
         title: 'Download information',
         placement: '%s',
         trigger: 'click'
     });
 });""" % (self._opener_class, self.options['doc_position']))))
Exemplo n.º 24
0
Arquivo: util.py Projeto: clld/apics
def feature_description(req, ctx):
    desc = ctx.markup_description or ctx.description
    desc = re.sub(
        "\*\*(?P<id>[0-9]+\-[0-9]+)\*\*",
        lambda m: HTML.a(
            '[%s]' % m.group('id'), href=req.route_url('sentence', id=m.group('id'))),
        desc)

    desc = re.sub(
        "\*\*\<\/span\>(?P<id>[0-9]+\-[0-9]+)\*\*",
        lambda m: literal('</span>') + HTML.a(
            '[%s]' % m.group('id'), href=req.route_url('sentence', id=m.group('id'))),
        desc)

    return re.sub(
        '\<span style\=\"font-style\: italic;\"\>WALS\<\/span\>\s+feature\s+[0-9]+',
        lambda m: HTML.a(
            literal(desc[m.start():m.end()]), href=req.route_url('wals', id=ctx.id)),
        desc)
Exemplo n.º 25
0
def newline2br(text):
    """Replace newlines in text with HTML br tags."""
    if not text:
        return ''
    chunks = []
    for i, line in enumerate(text.split('\n')):
        if i > 0:
            chunks.append(HTML.br())
        chunks.append(literal(line))
    return '\n'.join(chunks)
Exemplo n.º 26
0
def alt_representations(req, rsc, doc_position='right', exclude=None):
    exclude = exclude or []
    exclude.extend(['html', 'snippet.html'])
    adapters = [
        a
        for n, a in req.registry.getAdapters([rsc], interfaces.IRepresentation)
        if a.extension not in set(exclude)
    ]
    doc = []
    for adapter in adapters:
        if adapter.__doc__:
            doc.append(HTML.dt(adapter.name or adapter.extension))
            doc.append(HTML.dd(adapter.__doc__))
    doc = HTML.div(
        HTML.p(
            """You may download alternative representations of the data on
"%s" by clicking the button """ % rsc.name, icon('download-alt')),
        HTML.dl(*doc))
    return HTML.div(
        HTML.div(
            button(
                icon('info-sign', inverted=True), **{
                    'class': ['btn-info'],
                    'id': 'rsc-dl',
                    'data-content': unicode(doc)
                }),
            HTML.a(
                icon('download-alt'), HTML.span(class_="caret"), **{
                    'class_': "btn dropdown-toggle",
                    'data-toggle': "dropdown",
                    'href': "#",
                    'id': "dt-dl-opener",
                }),
            HTML.ul(
                *[
                    HTML.li(
                        HTML.
                        a(a.name or a.extension,
                          href="#",
                          onclick="document.location.href = '%s'; return false;"
                          % req.resource_url(rsc, ext=a.extension),
                          id='dt-dl-%s' % a.extension)) for a in adapters
                ], **dict(class_="dropdown-menu")),
            class_='btn-group'),
        HTML.script(
            literal("""\
    $(document).ready(function() {
        $('#rsc-dl').clickover({
            html: true,
            title: 'Alternative representations',
            placement: '%s',
            trigger: 'click'
        });
    });""" % doc_position)))
Exemplo n.º 27
0
def newline2br(text):
    """
    >>> assert newline2br(None) == ''
    """
    if not text:
        return ''
    chunks = []
    for i, line in enumerate(text.split('\n')):
        if i > 0:
            chunks.append(HTML.br())
        chunks.append(literal(line))
    return '\n'.join(chunks)
Exemplo n.º 28
0
def newline2br(text):
    """
    >>> assert newline2br(None) == ''
    """
    if not text:
        return ''
    chunks = []
    for i, line in enumerate(text.split('\n')):
        if i > 0:
            chunks.append(HTML.br())
        chunks.append(literal(line))
    return '\n'.join(chunks)
Exemplo n.º 29
0
def get_desc(ctx, sour, dflt=True, us=False, type='html'):
    """Provides a language descriptions."""
    data_year = '2022'
    isSource = "[click cite button]"
    human_creator = get_author(ctx.creator, 'txt')
    print(human_creator)
    arc_link = literal(ctx.arc_link())
    if hasattr(sour, 'year'):
        data_year = sour.year
        isSource = get_cite(ctx.id, sour, 'bibtex')
    style = ("<style>p.cite {padding-left: 25px; text-indent: -15px; "
             "font-size: 90%;}</style></style>")
    list_marker = ['<p>', '</p>', '<pre class=\"cite\">', '</pre>']
    if type == 'markdown':
        style = ''
        list_marker = ['', '\n\r', '', '\n\r']
        arc_link = '[' + ctx.arclink + '](' + ctx.arclink + ' "click to open")'

    text = ((list_marker[0] + "The {0} DoReCo data set was compiled by {1} in {2} "
                              "and further processed by the DoReCo team in {3}.")
            .format(ctx.name, human_creator, data_year, "2020-2022"))
    arch = ""
    if not ctx.arclink == "na":
        arch = (" A larger collection of {}'s {} data is archived "
                "at {}.".format(human_creator, ctx.name, arc_link))
    return literal(text + arch + ((list_marker[1] + list_marker[0] + "A set of files with further information "
                                                                     "on the {0} DoReCo data set, including metadata and PIDs is "
                                                                     "automatically included in each download of {0} DoReCo files." +
                                   list_marker[1] +
                                   list_marker[0] + "The {0} DoReCo data set should be cited as follows "
                                                    "(a BibTex version of this citation is provided below):" +
                                   list_marker[1] +
                                   list_marker[2] + "{1}" + list_marker[3] + list_marker[
                                       0] + "Please note that when citing "
                                            "this data set, or any number of DoReCo data sets, it is NOT "
                                            "sufficient to refer to DoReCo (and its editors) as a whole, "
                                            "but the full citation for each individual data set must be "
                                            "provided, including the names of the creators of each data "
                                            "set." + list_marker[1]).format(ctx.name, isSource)))  # " )))#
Exemplo n.º 30
0
def newline2br(text):
    """Replace newlines in text with HTML br tags.

    >>> assert newline2br(None) == ''
    """
    if not text:
        return ""
    chunks = []
    for i, line in enumerate(text.split("\n")):
        if i > 0:
            chunks.append(HTML.br())
        chunks.append(literal(line))
    return "\n".join(chunks)
Exemplo n.º 31
0
def alt_representations(req, rsc, doc_position='right', exclude=None):
    exclude = exclude or []
    exclude.extend(['html', 'snippet.html'])
    adapters = [a for n, a in req.registry.getAdapters([rsc], interfaces.IRepresentation)
                if a.extension not in set(exclude)]
    doc = []
    for adapter in adapters:
        if adapter.__doc__:
            doc.append(HTML.dt(adapter.name or adapter.extension))
            doc.append(HTML.dd(adapter.__doc__))
    doc = HTML.div(
        HTML.p(
            """You may download alternative representations of the data on
"%s" by clicking the button """ % rsc.name,
            icon('download-alt')),
        HTML.dl(*doc))
    return HTML.div(HTML.div(
        button(
            icon('info-sign', inverted=True),
            **{'class': ['btn-info'],
               'id': 'rsc-dl',
               'data-content': unicode(doc)}),
        HTML.a(
            icon('download-alt'),
            HTML.span(class_="caret"),
            **{
                'class_': "btn dropdown-toggle",
                'data-toggle': "dropdown",
                'href': "#",
                'id': "dt-dl-opener",
            }
        ),
        HTML.ul(
            *[HTML.li(HTML.a(
                a.name or a.extension,
                href="#",
                onclick="document.location.href = '%s'; return false;"
                        % req.resource_url(rsc, ext=a.extension),
                id='dt-dl-%s' % a.extension))
              for a in adapters],
            **dict(class_="dropdown-menu")),
        class_='btn-group'),
        HTML.script(literal("""\
    $(document).ready(function() {
        $('#rsc-dl').clickover({
            html: true,
            title: 'Alternative representations',
            placement: '%s',
            trigger: 'click'
        });
    });""" % doc_position)))
Exemplo n.º 32
0
    def gloss_with_tooltip(gloss):
        person_map = {
            '1': 'first person',
            '2': 'second person',
            '3': 'third person',
        }

        res = []
        end = 0
        for match in GLOSS_ABBR_PATTERN.finditer(gloss):
            if match.start() > end:
                res.append(literal(gloss[end:match.start()]))

            abbr = match.group('abbr')
            if abbr in abbrs:
                explanation = abbrs[abbr]
                if match.group('personprefix'):
                    explanation = '%s %s' % (
                        person_map[match.group('personprefix')], explanation)

                if match.group('personsuffix'):
                    explanation = '%s %s' % (
                        explanation, person_map[match.group('personsuffix')])

                res.append(
                    HTML.span(
                        HTML.span(gloss[match.start():match.end()].lower(),
                                  class_='sc'), **{
                                      'data-hint': explanation,
                                      'class': 'hint--bottom'
                                  }))
            else:
                res.append(abbr)

            end = match.end()

        res.append(literal(gloss[end:]))
        return filter(None, res)
Exemplo n.º 33
0
def ipa_custom(req, segments):
    rows = []
    for i, data in segments.items():
        title, symbol, class_, param, exists, vs = data
        if exists and param and (not param.jsondata['core_list'] or i in [15, 74, 77, 84]):
            rows.append(HTML.tr(
                HTML.td(
                    parameter_link(req, literal(symbol), vs or p),
                    title=title, class_=class_),
                HTML.th(
                    title.split('-')[1].strip(),
                    style="padding-left: 10px; text-align: left;"),
            ))
    return HTML.table(HTML.tbody(*rows)) if rows else ''
Exemplo n.º 34
0
def get_meaning_properties(req, ctx):
    for attr, info, converter in [
        ('description', None, lambda s: s),
        ('typical_context', None, lambda s: s),
        ('semantic_field', None, lambda sf: link(req, sf)),
        ('semantic_category', None, lambda s: s),
        ('borrowed_score', hb_borrowed_score(), lambda f: '{0:.2f}'.format(f)),
        ('age_score', hb_age_score(), lambda f: '{0:.2f}'.format(f) if f else ''),
        ('simplicity_score', hb_simplicity_score(), lambda f: '{0:.2f}'.format(f)),
    ]:
        label = attr.capitalize().replace('_', ' ')
        if info:
            label = HTML.span(
                label, literal('&nbsp;'), infobutton(info, content_type='html'))
        yield (label, converter(getattr(ctx, attr)))
Exemplo n.º 35
0
 def col_defs(self):
     return [
         IntegerIdCol(
             self,
             'id',
             sDescription=
             "The number in this column is the semantic field number. It "
             "is the first part of the Loanword Typology Code of the words in the "
             "corresponding field."),
         LinkCol(
             self,
             'name',
             sDescription=literal(
                 "The first 22 fields are the fields of the Intercontinental "
                 "Dictionary Series meaning list, proposed by Mary Ritchie Key, and "
                 "ultimately based on Carl Darling Buck's (1949) <i>Dictionary of selected"
                 " synonyms in the principal Indo-European languages</i>. The other two "
                 "fields were added for the Loanword Typology project.")),
         NumberOfMeanings(
             self,
             'number_of_meanings',
             sDescription=
             "This gives the number of different meanings in each "
             "semantic field."),
         SemanticFieldScoreCol(self,
                               'borrowed_score',
                               sDescription=literal('%s' %
                                                    hb_borrowed_score())),
         SemanticFieldScoreCol(self,
                               'age_score',
                               sDescription=literal('%s' % hb_age_score())),
         SemanticFieldScoreCol(self,
                               'simplicity_score',
                               sDescription=literal('%s' %
                                                    hb_simplicity_score())),
     ]
Exemplo n.º 36
0
Arquivo: util.py Projeto: clld/wold2
def get_meaning_properties(req, ctx):
    for attr, info, converter in [
        ('description', None, lambda s: s),
        ('typical_context', None, lambda s: s),
        ('semantic_field', None, lambda sf: link(req, sf)),
        ('semantic_category', None, lambda s: s),
        ('borrowed_score', hb_borrowed_score(), lambda f: '{0:.2f}'.format(f)),
        ('age_score', hb_age_score(), lambda f: '{0:.2f}'.format(f) if f else ''),
        ('simplicity_score', hb_simplicity_score(), lambda f: '{0:.2f}'.format(f)),
    ]:
        label = attr.capitalize().replace('_', ' ')
        if info:
            label = HTML.span(
                label, literal('&nbsp;'), infobutton(info, content_type='html'))
        yield (label, converter(getattr(ctx, attr)))
Exemplo n.º 37
0
def ipa_custom(req, segments):
    rows = []
    for i, data in segments.items():
        title, symbol, class_, param, exists, vs = data
        if exists and param \
                and (not param.jsondata['core_list'] or i in [15, 74, 77, 84]):
            rows.append(
                HTML.tr(
                    HTML.td(parameter_link(req, literal(symbol), vs or param),
                            title=title,
                            class_=class_),
                    HTML.th(title.split('-', 1)[1].strip(),
                            style="padding-left: 10px; text-align: left;"),
                ))
    return HTML.table(HTML.tbody(*rows)) if rows else ''
Exemplo n.º 38
0
def _media(maintype_, obj, **kw):
    label = kw.pop('label', None)
    assert maintype_ in ['audio', 'video']
    if maintype(obj) != maintype_:
        raise ValueError('type mismatch: {0} and {1}'.format(maintype(obj), maintype_))
    kw.setdefault('controls', 'controls')
    media_element = getattr(HTML, maintype_)(
        literal('Your browser does not support the <code>{0}</code> element.'.format(
            maintype_)),
        HTML.source(src=bitstream_url(obj, type_='web'), type=mimetype(obj)), **kw)
    return HTML.div(
        media_element,
        HTML.br(),
        link(obj, label=label),
        class_='cdstar_{0}_link'.format(maintype_),
        style='margin-top: 10px')
Exemplo n.º 39
0
def _media(maintype_, obj, **kw):
    label = kw.pop('label', None)
    assert maintype_ in ['audio', 'video']
    if maintype(obj) != maintype_:
        raise ValueError('type mismatch: {0} and {1}'.format(maintype(obj), maintype_))
    kw.setdefault('controls', 'controls')
    media_element = getattr(HTML, maintype_)(
        literal('Your browser does not support the <code>{0}</code> element.'.format(
            maintype_)),
        HTML.source(src=bitstream_url(obj, type_='web'), type=mimetype(obj)), **kw)
    return HTML.div(
        media_element,
        HTML.br(),
        link(obj, label=label),
        class_='cdstar_{0}_link'.format(maintype_),
        style='margin-top: 10px')
Exemplo n.º 40
0
def value_table(ctx, req):
    rows = []
    langs = {}

    for i, de in enumerate(ctx.domain):
        exclusive = 0
        shared = 0

        for v in [
                _v for _v in de.values if not _v.valueset.language.language_pk
        ]:
            if len(v.valueset.values) > 1:
                shared += 1
            else:
                exclusive += 1
            langs[v.valueset.language_pk] = 1

        cells = [
            HTML.td(map_marker_img(req, de)),
            HTML.td(literal(de.name)),
            HTML.td(str(exclusive), class_='right'),
        ]
        if ctx.multivalued:
            cells.append(HTML.td(str(shared), class_='right'))
            cells.append(HTML.td(str(exclusive + shared), class_='right'))

        if exclusive or shared:
            rows.append(HTML.tr(*cells))
    rows.append(
        HTML.tr(
            HTML.td('Representation:',
                    colspan=str(len(cells) - 1),
                    class_='right'), HTML.td('%s' % len(langs),
                                             class_='right')))

    parts = []
    if ctx.multivalued:
        parts.append(
            HTML.thead(
                HTML.tr(*[
                    HTML.th(s, class_='right')
                    for s in [' ', ' ', 'excl', 'shrd', 'all']
                ])))
    parts.append(HTML.tbody(*rows))

    return HTML.table(*parts, class_='table table-condensed')
Exemplo n.º 41
0
Arquivo: util.py Projeto: clld/apics
def value_table(ctx, req):
    rows = []
    langs = {}

    for i, de in enumerate(ctx.domain):
        exclusive = 0
        shared = 0

        for v in [_v for _v in de.values if not _v.valueset.language.language_pk]:
            if len(v.valueset.values) > 1:
                shared += 1
            else:
                exclusive += 1
            langs[v.valueset.language_pk] = 1

        cells = [
            HTML.td(map_marker_img(req, de)),
            HTML.td(literal(de.name)),
            HTML.td(str(exclusive), class_='right'),
        ]
        if ctx.multivalued:
            cells.append(HTML.td(str(shared), class_='right'))
            cells.append(HTML.td(str(exclusive + shared), class_='right'))

        if exclusive or shared:
            rows.append(HTML.tr(*cells))
    rows.append(HTML.tr(
        HTML.td('Representation:', colspan=str(len(cells) - 1), class_='right'),
        HTML.td('%s' % len(langs), class_='right')))

    parts = []
    if ctx.multivalued:
        parts.append(HTML.thead(
            HTML.tr(*[HTML.th(s, class_='right')
                      for s in [' ', ' ', 'excl', 'shrd', 'all']])))
    parts.append(HTML.tbody(*rows))

    return HTML.table(*parts, class_='table table-condensed')
Exemplo n.º 42
0
def rendered_sentence(sentence, abbrs=None, fmt='long'):
    if sentence.xhtml:
        return HTML.div(
            HTML.div(Markup(sentence.xhtml), class_='body'), class_="sentence")

    if abbrs is None:
        q = DBSession.query(models.GlossAbbreviation).filter(
            or_(models.GlossAbbreviation.language_pk == sentence.language_pk,
                models.GlossAbbreviation.language_pk == None)
        )
        abbrs = dict((g.id, g.name) for g in q)

    def gloss_with_tooltip(gloss):
        person_map = {
            '1': 'first person',
            '2': 'second person',
            '3': 'third person',
        }

        res = []
        end = 0
        for match in GLOSS_ABBR_PATTERN.finditer(gloss):
            if match.start() > end:
                res.append(literal(gloss[end:match.start()]))

            abbr = match.group('abbr')
            if abbr in abbrs:
                explanation = abbrs[abbr]
                if match.group('personprefix'):
                    explanation = '%s %s' % (
                        person_map[match.group('personprefix')], explanation)

                if match.group('personsuffix'):
                    explanation = '%s %s' % (
                        explanation, person_map[match.group('personsuffix')])

                res.append(HTML.span(
                    HTML.span(gloss[match.start():match.end()].lower(), class_='sc'),
                    **{'data-hint': explanation, 'class': 'hint--bottom'}))
            else:
                res.append(abbr)

            end = match.end()

        res.append(literal(gloss[end:]))
        return filter(None, res)

    units = []
    if sentence.analyzed and sentence.gloss:
        analyzed = sentence.analyzed
        glossed = sentence.gloss
        for morpheme, gloss in zip(analyzed.split('\t'), glossed.split('\t')):
            units.append(HTML.div(
                HTML.div(literal(morpheme), class_='morpheme'),
                HTML.div(*gloss_with_tooltip(gloss), **{'class': 'gloss'}),
                class_='gloss-unit'))

    return HTML.p(
        HTML.div(
            HTML.div(
                HTML.div(literal(sentence.markup_text or sentence.name), class_='object-language'),
                HTML.div(*units, **{'class': 'gloss-box'}) if units else '',
                HTML.div(sentence.description, class_='translation')
                if sentence.description else '',
                HTML.div(sentence.original_script, class_='original_script')
                if sentence.original_script else '',
                #HTML.small(literal(sentence.comment)) if sentence.comment and fmt == 'long' else '',
                class_='body',
            ),
            class_="sentence",
        ),
        class_="sentence",
    )
Exemplo n.º 43
0
Arquivo: value.py Projeto: mitcho/clld
 def get_attrs(self, item):
     label = item.__unicode__()
     title = label
     if self.dt.parameter:
         label = HTML.span(map_marker_img(self.dt.req, item), literal('&nbsp;'), label)
     return {'label': label, 'title': title}
Exemplo n.º 44
0
Arquivo: helpers.py Projeto: clld/clld
def text2html(text, mode='br', sep='\n\n'):
    """Turn plain text into simple HTML."""
    if mode == 'p':
        return HTML.div(*[HTML.p(literal(newline2br(line))) for line in text.split(sep)])
    return HTML.p(literal(newline2br(text)))
Exemplo n.º 45
0
Arquivo: maps.py Projeto: clld/wold2
def legend_items(req):
    for type, label in [('recipient', 'recipient language'), ('donor', 'donor languoid')]:
        yield HTML.label(
            HTML.img(src=icon_url(req, type), height='20', width='20'),
            literal(' ' + label),
            style='margin-left: 1em; margin-right: 1em;')
Exemplo n.º 46
0
Arquivo: helpers.py Projeto: clld/clld
def rendered_sentence(sentence, abbrs=None, fmt='long'):
    """Format a sentence as HTML."""
    if sentence.xhtml:
        return HTML.div(
            HTML.div(Markup(sentence.xhtml), class_='body'), class_="sentence")

    if abbrs is None:
        q = DBSession.query(models.GlossAbbreviation).filter(
            or_(models.GlossAbbreviation.language_pk == sentence.language_pk,
                models.GlossAbbreviation.language_pk == None)
        )
        abbrs = dict((g.id, g.name) for g in q)

    def gloss_with_tooltip(gloss):
        person_map = {
            '1': 'first person',
            '2': 'second person',
            '3': 'third person',
        }

        res = []
        end = 0
        for match in GLOSS_ABBR_PATTERN.finditer(gloss):
            if match.start() > end:
                res.append(gloss[end:match.start()])

            abbr = match.group('abbr')
            if abbr in abbrs:
                explanation = abbrs[abbr]
                if match.group('personprefix'):
                    explanation = '%s %s' % (
                        person_map[match.group('personprefix')], explanation)

                if match.group('personsuffix'):
                    explanation = '%s %s' % (
                        explanation, person_map[match.group('personsuffix')])

                res.append(HTML.span(
                    HTML.span(gloss[match.start():match.end()].lower(), class_='sc'),
                    **{'data-hint': explanation, 'class': 'hint--bottom'}))
            else:
                res.append(abbr)

            end = match.end()

        res.append(gloss[end:])
        return filter(None, res)

    def alt_translation(sentence):
        res = ''
        if sentence.jsondata.get('alt_translation'):
            text = sentence.jsondata['alt_translation']
            name = ''
            if ALT_TRANSLATION_LANGUAGE_PATTERN.match(text):
                name, text = [t.strip() for t in text.split(':', 1)]
                name = HTML.span(name + ': ')
            res = HTML.div(name, HTML.span(text, class_='translation'))
        return res

    units = []
    if sentence.analyzed and sentence.gloss:
        analyzed = sentence.analyzed
        glossed = sentence.gloss
        for morpheme, gloss in zip(analyzed.split('\t'), glossed.split('\t')):
            units.append(HTML.div(
                HTML.div(morpheme, class_='morpheme'),
                HTML.div(*gloss_with_tooltip(gloss), **{'class': 'gloss'}),
                class_='gloss-unit'))

    return HTML.div(
        HTML.div(
            HTML.div(
                HTML.div(sentence.original_script, class_='original-script')
                if sentence.original_script else '',
                HTML.div(literal(sentence.markup_text or sentence.name),
                         class_='object-language'),
                HTML.div(*units, **{'class': 'gloss-box'}) if units else '',
                HTML.div(sentence.description, class_='translation')
                if sentence.description else '',
                alt_translation(sentence),
                class_='body',
            ),
            class_="sentence",
        ),
        class_="sentence-wrapper",
    )
Exemplo n.º 47
0
def rendered_sentence(sentence, abbrs=None, fmt="long"):
    """Format a sentence as HTML."""
    if sentence.xhtml:
        return HTML.div(HTML.div(Markup(sentence.xhtml), class_="body"), class_="sentence")

    if abbrs is None:
        q = DBSession.query(models.GlossAbbreviation).filter(
            or_(
                models.GlossAbbreviation.language_pk == sentence.language_pk,
                models.GlossAbbreviation.language_pk == None,
            )
        )
        abbrs = dict((g.id, g.name) for g in q)

    def gloss_with_tooltip(gloss):
        person_map = {"1": "first person", "2": "second person", "3": "third person"}

        res = []
        end = 0
        for match in GLOSS_ABBR_PATTERN.finditer(gloss):
            if match.start() > end:
                res.append(gloss[end : match.start()])

            abbr = match.group("abbr")
            if abbr in abbrs:
                explanation = abbrs[abbr]
                if match.group("personprefix"):
                    explanation = "%s %s" % (person_map[match.group("personprefix")], explanation)

                if match.group("personsuffix"):
                    explanation = "%s %s" % (explanation, person_map[match.group("personsuffix")])

                res.append(
                    HTML.span(
                        HTML.span(gloss[match.start() : match.end()].lower(), class_="sc"),
                        **{"data-hint": explanation, "class": "hint--bottom"}
                    )
                )
            else:
                res.append(abbr)

            end = match.end()

        res.append(gloss[end:])
        return filter(None, res)

    def alt_translation(sentence):
        res = ""
        if sentence.jsondata.get("alt_translation"):
            text = sentence.jsondata["alt_translation"]
            name = ""
            if ALT_TRANSLATION_LANGUAGE_PATTERN.match(text):
                name, text = [t.strip() for t in text.split(":", 1)]
                name = HTML.span(name + ": ")
            res = HTML.div(name, HTML.span(text, class_="translation"))
        return res

    units = []
    if sentence.analyzed and sentence.gloss:
        analyzed = sentence.analyzed
        glossed = sentence.gloss
        for morpheme, gloss in zip(analyzed.split("\t"), glossed.split("\t")):
            units.append(
                HTML.div(
                    HTML.div(morpheme, class_="morpheme"),
                    HTML.div(*gloss_with_tooltip(gloss), **{"class": "gloss"}),
                    class_="gloss-unit",
                )
            )

    return HTML.p(
        HTML.div(
            HTML.div(
                HTML.div(sentence.original_script, class_="original-script") if sentence.original_script else "",
                HTML.div(literal(sentence.markup_text or sentence.name), class_="object-language"),
                HTML.div(*units, **{"class": "gloss-box"}) if units else "",
                HTML.div(sentence.description, class_="translation") if sentence.description else "",
                alt_translation(sentence),
                class_="body",
            ),
            class_="sentence",
        ),
        class_="sentence",
    )
Exemplo n.º 48
0
def rendered_sentence(sentence, abbrs=None, fmt='long'):
    if sentence.xhtml:
        return HTML.div(HTML.div(Markup(sentence.xhtml), class_='body'),
                        class_="sentence")

    if abbrs is None:
        q = DBSession.query(models.GlossAbbreviation).filter(
            or_(models.GlossAbbreviation.language_pk == sentence.language_pk,
                models.GlossAbbreviation.language_pk == None))
        abbrs = dict((g.id, g.name) for g in q)

    def gloss_with_tooltip(gloss):
        person_map = {
            '1': 'first person',
            '2': 'second person',
            '3': 'third person',
        }

        res = []
        end = 0
        for match in GLOSS_ABBR_PATTERN.finditer(gloss):
            if match.start() > end:
                res.append(literal(gloss[end:match.start()]))

            abbr = match.group('abbr')
            if abbr in abbrs:
                explanation = abbrs[abbr]
                if match.group('personprefix'):
                    explanation = '%s %s' % (
                        person_map[match.group('personprefix')], explanation)

                if match.group('personsuffix'):
                    explanation = '%s %s' % (
                        explanation, person_map[match.group('personsuffix')])

                res.append(
                    HTML.span(
                        HTML.span(gloss[match.start():match.end()].lower(),
                                  class_='sc'), **{
                                      'data-hint': explanation,
                                      'class': 'hint--bottom'
                                  }))
            else:
                res.append(abbr)

            end = match.end()

        res.append(literal(gloss[end:]))
        return filter(None, res)

    units = []
    if sentence.analyzed and sentence.gloss:
        analyzed = sentence.analyzed
        glossed = sentence.gloss
        for morpheme, gloss in zip(analyzed.split('\t'), glossed.split('\t')):
            units.append(
                HTML.div(HTML.div(literal(morpheme), class_='morpheme'),
                         HTML.div(*gloss_with_tooltip(gloss),
                                  **{'class': 'gloss'}),
                         class_='gloss-unit'))

    return HTML.p(
        HTML.div(
            HTML.div(
                HTML.div(literal(sentence.markup_text or sentence.name),
                         class_='object-language'),
                HTML.div(*units, **{'class': 'gloss-box'}) if units else '',
                HTML.div(sentence.description, class_='translation')
                if sentence.description else '',
                HTML.div(sentence.original_script, class_='original_script')
                if sentence.original_script else '',
                #HTML.small(literal(sentence.comment)) if sentence.comment and fmt == 'long' else '',
                class_='body',
            ),
            class_="sentence",
        ),
        class_="sentence",
    )
Exemplo n.º 49
0
Arquivo: util.py Projeto: clld/ewave
def value_table(ctx, req):
    rows = [HTML.tr(
        HTML.td(map_marker_img(req, de)),
        HTML.td(literal(de.name + ' - ' + de.description)),
        HTML.td(str(len(de.values)), class_='right')) for de in ctx.domain]
    return HTML.table(HTML.tbody(*rows), class_='table table-condensed')
Exemplo n.º 50
0
Arquivo: util.py Projeto: clld/ewave
def value_table(ctx, req):
    rows = [HTML.tr(
        HTML.td(map_marker_img(req, de)),
        HTML.td(literal(de.name + ' - ' + de.description)),
        HTML.td(str(len(de.values)), class_='right')) for de in ctx.domain]
    return HTML.table(HTML.tbody(*rows), class_='table table-condensed')
Exemplo n.º 51
0
Arquivo: maps.py Projeto: clld/waab
 def html_label(marker_spec, text):
     return HTML.label(
         marker_img(self.req.static_url(marker_spec)),
         literal(text),
         style='margin-left: 1em; margin-right: 1em;')
Exemplo n.º 52
0
 def value_li(de):
     return HTML.label(
         map_marker_img(self.req, de),
         literal(de.abbr),
         style='margin-left: 1em; margin-right: 1em;')
Exemplo n.º 53
0
def get_form(lang_n="", isExtended=False, full=True):
    """Provides a download form.
    ARGUMENTS:
    - 'lang_n'      :   the glottocode
    - 'ext'         :   if for the extended corpus
    - 'full'        :   if all fields (or just the button)"""

    style = ("<style>"
             "form {float: left; margin: 0px; padding: 0px;} "
             "input {float: left; margin-right: 10px;} "
             "select {float: left; margin-right: 10px;} "
             "label {float: left; margin-right: 10px;} "
             "p.form {float: left; margin-right: 10px; padding: 3px;} "
             "p.citeb {float: right;}"
             "</style>")
    lang = ("<input type=\"hidden\" id=\"f_id\" "
            "name=\"id\" value=\"" + lang_n + "\">")
    wav = ""
    form = ""
    text = ""
    ext = ""
    if full:
        if lang_n.lower() == 'languages':
            wav = ("<input type=\"hidden\" id=\"f_wav\" "
                   "name=\"wav\" value=\"False\">")
        else:
            wav = ("<input type=\"checkbox\" id=\"f_wav\" name=\"wav\" "
                   "value=\"True\"><label for=\"f_wav\">WAV</label>")
    else:
        wav = ("<input type=\"hidden\" id=\"f_wav\" "
               "name=\"wav\" value=\"True\">")
        form = ("<input type=\"hidden\" id=\"f_format\" "
                "name=\"format\" value=\"all\">")
    if isExtended and not isExtended == "False":
        ext = ("<input type=\"hidden\" id=\"f_ext\" "
               "name=\"extended\" value=\"True\">")
        text = ("<p class=\"form\"><b>Download all files:</b></p>")
        form = ("<select id=\f_format\" name=\"format\">"
                "<option value=\"elan\">Elan (.eaf)</option></select>")
    else:
        ext = ("<input type=\"hidden\" id=\"f_ext\" "
               "name=\"extended\" value=\"False\">")
        text = ("<p class=\"form\"><b>Download all core files:</b></p>")
        form = ("<select id=\f_format\" name=\"format\">"
                "<option value=\"all\">All types</option>"
                "<option value=\"praat\">Praat (.TextGrid)</option>"
                "<option value=\"elan\">Elan (.eaf)</option></select>")
        # "
        # "<option value=\"tabular\">Tabular (.csv)</option>"
        # "<option value=\"tei\">TEI (.xml)</option>"
        # "<option value=\"MAUS\">MAUS parameters</option>
    submit = "<input type=\"submit\" value=\"Download\">"
    wav = ""
    audio = (
        "<p><a href='mailto:[email protected]?subject=[doreco]Audio files for'>contact us</a> if you are interested in downloading all the audio files as a zip archive.</p>")
    # TODO audio license, do we exclude the audio for TLA archive too ?
    if lang_n == 'languages':
        audio = ('<p>Not all audio are available. Please check languages individually</p>')
    else:
        statement = DBSession.query(doreLanguage) \
            .filter(Language.id == lang_n).first()
        # print('toto', lang_n, statement)
        if statement:
            audioLicense = statement.audio_lic
            if audioLicense == 'audio linked':
                audio = ("<p>See archive for the audio files.</p>")
            if statement.AUDIO is not None and statement.AUDIO != 'na':
                audio = '<a style="display:block" href="https://test.nakala.fr/' + \
                        statement.AUDIO + '" target="_BLANK">direct link to audios</a>'
    if isExtended==True:
        audio = ''

    if full:
        return literal(style + audio + text + "<form method=\"post\" action=\"/doreLoad\">"
                       + ext + lang + wav + form + submit + "</form>")
    else:
        return literal(style + "<form method=\"post\" action=\"/doreLoad\">"
                       + ext + lang + wav + form + submit + "</form>")
Exemplo n.º 54
0
 def __unicode__(self):
     return literal(super(Feature, self).__unicode__())
Exemplo n.º 55
0
def legend(req):
    return HTML.table(*[
        HTML.tr(HTML.td(literal('&nbsp;'), class_=SEGMENT_VALUES[n][2]),
                HTML.td(SEGMENT_VALUES[n][0], style="padding-left: 10px;"))
        for n in sorted(SEGMENT_VALUES.keys())
    ])
Exemplo n.º 56
0
Arquivo: util.py Projeto: clld/apics
def legend(req):
    return HTML.table(*[
        HTML.tr(
            HTML.td(literal('&nbsp;'), class_=SEGMENT_VALUES[n][2]),
            HTML.td(SEGMENT_VALUES[n][0], style="padding-left: 10px;"))
        for n in sorted(SEGMENT_VALUES.keys())])