Select Git revision
table.py 4.35 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 Sequence, Optional, Iterable
import urllib.parse
import werkzeug.exceptions
import mo.csv
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 escape(self.text)
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 = '<a href="' + escape(self.url) + '"'
if self.hint:
a += ' title="' + escape(self.hint) + '"'
return a + '>' + escape(self.text) + '</a>'
class Table:
columns: Sequence[Column]
rows: Iterable[dict]
filename: str
def __init__(self, columns: Sequence[Column], rows: Iterable[dict], filename: str):
self.columns = columns
self.rows = rows
self.filename = filename
def to_html(self) -> str:
tab = ['<table class=data>', '<tr>']
for c in self.columns:
tab.append(f'\t<th>{c.title}')
for r in self.rows:
tab.append('<tr>')
for c in self.columns:
val = r.get(c.key)
if isinstance(val, Cell):
vals = val.to_html()
else:
vals = escape(str(val))
tab.append(f'\t<td>{vals}')
tab.append('</table>')
tab.append("<p>Stáhnout jako <a href='?format=csv'>CSV</a> nebo <a href='?format=tsv'>TSV</a>.")
return Markup("\n".join(tab))
def to_csv(self, dialect: str) -> str:
out = io.StringIO()
writer = csv.writer(out, dialect=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()
def to_csv_stream(self, dialect: str) -> Iterable[str]:
out = io.StringIO()
writer = csv.writer(out, dialect=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()
out.seek(0)
out.truncate()
nrows = 0
yield out.getvalue()
def send_as(self, format: str, streaming: bool = False) -> Response:
if format == 'csv':
dialect = 'excel'
ctype = 'text/csv; charset=utf=8'
filename = self.filename + '.csv'
elif format == 'tsv':
dialect = 'tsv'
ctype = 'text/tab-separated-values; charset=utf=8'
filename = self.filename + '.tsv'
else:
raise werkzeug.exceptions.NotFound()
if streaming:
resp = Response(self.to_csv_stream(dialect=dialect))
else:
out = self.to_csv(dialect=dialect)
resp = app.make_response(out)
resp.content_type = ctype
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)