Skip to content
Snippets Groups Projects
Select Git revision
  • fd7cda488fb4573a057e1f6408904d26202b519b
  • master default protected
2 results

tree_successor_test.py

Blame
  • table.py 5.56 KiB
    import csv
    from dataclasses import dataclass
    from flask import Response, url_for
    from html import escape
    import io
    from markupsafe import Markup
    from typing import Any, Dict, Sequence, Optional, Iterable, Union
    import urllib.parse
    import werkzeug.exceptions
    
    from mo.csv import FileFormat
    import mo.db as db
    from mo.web import app
    
    
    @dataclass
    class Column:
        key: str                # Jméno klíče ve slovnících
        name: str               # Název v hlavičce CSV
        title: str              # Titulek v HTML tabulkách
    
        def __init__(self, key: str, name: Optional[str] = None, title: Optional[str] = None):
            self.key = key
            self.name = name or key
            self.title = title or self.name
    
    
    class Cell:
        """Buňka tabulky může mít dvě verze: textovou a HTML."""
        text: str
    
        def __init__(self, text: str):
            self.text = text
    
        def __str__(self) -> str:
            return self.text
    
        def to_html(self) -> str:
            return '<td>' + escape(self.text)
    
    
    class Row:
        """Řádek tabulky, definuje klíče. Může definovat HTML atributy řádku."""
        keys: Dict[str, Any]
        html_attr: Dict[str, str]
    
        def __init__(self, keys: Dict[str, Any], html_attr: Dict[str, str] = {}):
            self.keys = keys
            self.html_attr = html_attr
    
        def get(self, key: str) -> Any:
            return self.keys.get(key)
    
    
    class CellLink(Cell):
        url: str
        hint: Optional[str]
    
        def __init__(self, text: str, url: str, hint: Optional[str] = None):
            Cell.__init__(self, text)
            self.url = url
            self.hint = hint
    
        def to_html(self) -> str:
            a = '<td><a href="' + escape(self.url) + '"'
            if self.hint:
                a += ' title="' + escape(self.hint) + '"'
            return a + '>' + escape(self.text) + '</a>'
    
    
    class CellCheckbox(Cell):
        name: str
        value: str
        checked: bool
    
        def __init__(self, name: str, value: str, checked: bool = False):
            Cell.__init__(self, "")
            self.name = name
            self.value = value
            self.checked = checked
    
        def to_html(self) -> str:
            ch = f'<td><input type="checkbox" name="{self.name}" value="{self.value}"'
            if self.checked:
                ch += ' checked'
            return ch + '>'
    
    
    class Table:
        columns: Sequence[Column]
        rows: Iterable[Row]
        filename: str
        show_downlink: bool
        table_class: str
    
        def __init__(
            self, columns: Sequence[Column], rows: Iterable[Row],
            filename: str, show_downlink: bool = True,
            table_class: str = "data"
        ):
            self.columns = columns
            self.rows = rows
            self.filename = filename
            self.show_downlink = show_downlink
            self.table_class = table_class
    
        def to_html(self) -> str:
            tab = [f'<table class="{self.table_class}">', '<thead>', '<tr>']
    
            for c in self.columns:
                tab.append(f'\t<th>{c.title}')
    
            tab.append('<tbody>')
    
            for r in self.rows:
                r_attr = [f' {key}="{val}"' for (key, val) in r.html_attr.items()]
                tab.append(f'<tr{"".join(r_attr)}>')
                for c in self.columns:
                    val = r.get(c.key)
                    if isinstance(val, Cell):
                        tab.append(val.to_html())
                    else:
                        tab.append(f'\t<td>{escape(str(val))}')
    
            tab.append('</table>')
            if self.show_downlink:
                tab.append("<p>Stáhnout jako <a href='?format=en_csv'>CSV s čárkami</a>, <a href='?format=cs_csv'>CSV se středníky</a> nebo <a href='?format=tsv'>TSV</a>.")
            return Markup("\n".join(tab))
    
        def to_csv(self, fmt: FileFormat) -> bytes:
            out = io.StringIO()
            writer = csv.writer(out, dialect=fmt.get_dialect())
    
            header = [c.name for c in self.columns]
            writer.writerow(header)
    
            for row in self.rows:
                r = [row.get(c.key) for c in self.columns]
                writer.writerow(r)
    
            return out.getvalue().encode(fmt.get_charset())
    
        def to_csv_stream(self, fmt: FileFormat) -> Iterable[bytes]:
            out = io.StringIO()
            writer = csv.writer(out, dialect=fmt.get_dialect())
    
            header = [c.name for c in self.columns]
            writer.writerow(header)
    
            nrows = 0
            for row in self.rows:
                r = [row.get(c.key) for c in self.columns]
                writer.writerow(r)
    
                nrows += 1
                if nrows >= 100:
                    yield out.getvalue().encode(fmt.get_charset())
                    out.seek(0)
                    out.truncate()
                    nrows = 0
    
            yield out.getvalue().encode(fmt.get_charset())
    
        def send_as(self, format: Union[FileFormat, str], streaming: bool = False) -> Response:
            try:
                fmt = FileFormat.coerce(format)
            except ValueError:
                raise werkzeug.exceptions.NotFound()
    
            if streaming:
                resp = Response(self.to_csv_stream(fmt))
            else:
                out = self.to_csv(fmt)
                resp = app.make_response(out)
    
            resp.content_type = fmt.get_content_type()
            filename = self.filename + '.' + fmt.get_extension()
            resp.headers.add('Content-Disposition', f'attachment; filename={filename}')
            return resp
    
    
    # Pomocné funkce na generování různých typů odkazů
    
    def cell_email_link(user: db.User) -> CellLink:
        return CellLink(user.email, 'mailto:' + urllib.parse.quote(user.email, safe='@'))
    
    
    def cell_user_link(user: db.User, text: str) -> CellLink:
        return CellLink(text, url_for('org_user', id=user.user_id))
    
    
    def cell_place_link(place: db.Place, text: str) -> CellLink:
        return CellLink(text, url_for('org_place', id=place.place_id), hint=place.name)