nulib: génération d'une table

This commit is contained in:
Jephté Clain 2018-05-08 15:20:45 +04:00
parent 51f4e92c3d
commit 0bbcef690b
5 changed files with 285 additions and 43 deletions

View File

@ -36,7 +36,36 @@ mydir, myname = path.split(myself)
# Fonctions diverses
_undef = object()
class Undef(object):
def sa(self, value, kw, name, default=_undef):
"""si value est Undef, récupérer la valeur avec le nom court name dans kw
"""
if default is _undef: default = self
if value is self and name is not None: value = kw.pop(name, self)
if value is self: value = default
return value
def __nonzero__(self):
return False
def __len__(self):
return 0
def __lt__(self, other):
if other: return True
else: return False
def __le__(self, other):
return True
def __eq__(self, other):
if other: return False
else: return True
def __ne__(self, other):
if other: return True
else: return False
def __gt__(self, other):
if other: return False
else: return True
def __ge__(self, other):
return True
def __repr__(self):
return 'Undef'
def __call__(self):

View File

@ -1,7 +1,11 @@
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
__all__ = ('_weburl', '_ensure_session')
__all__ = (
'_weburl', '_ensure_session',
'u', 'uval',
)
from types import UnicodeType, StringType
from ..ext import web
def _weburl(path, __BASEURL, **kw):
@ -14,3 +18,21 @@ def _ensure_session(page):
if session is None: raise ValueError("no session configured")
page.session = session
return session
def u(text):
"""Si text n'est pas de l'unicode, le convertir en unicode avec l'encoding utf-8
"""
t = type(text)
if t is not UnicodeType:
if t is not StringType: text = str(text)
text = unicode(text, "utf-8")
return text
def uval(value):
"""Transformer value en unicode, en particulier s'il est callable ou s'il
a une méthode render()
"""
if value is None: return None
if hasattr(value, 'render'): value = value.render()
if callable(value): value = value()
return u(value)

View File

@ -2,19 +2,22 @@
__all__ = (
'load',
'table', 'tr', 'td',
'Menu', 'Action',
'About',
'Alert', 'set_alert',
'Menu', 'Action',
)
import sys, traceback
from types import StringType
from ..base import seqof, Undef
from ..base import isseq, seqof, Undef
from ..ext import web
from . import ui
from .ui import Action, u, p
from .api import _ensure_session
from .api import _ensure_session, u, uval
from .model import Row, TableSchema
from .ui import Action, p
Undef = Undef() # spécifique à ce module
@ -43,12 +46,98 @@ def load(prefix=None, icons=True, charset="utf-8"):
])
return u"\n".join(lines)
def sa(value, kw, name, default=Undef):
"""si value est indéfini, récupérer la valeur avec le nom court dans kw
"""
if value is Undef and name is not None: value = kw.pop(name, Undef)
if value is Undef: value = default
return value
################################################################################
# Table
TRD_CLASS_MAP = dict(
active="active",
success="success", done="success",
info="info",
warning="warning", warn="warning", notice="warning",
danger="danger", error="danger",
)
def trd_joinc(css):
if css is Undef: css = None
if type(css) is StringType:
css = css.split(",")
classes = []
for css in seqof(css):
classes.append(TRD_CLASS_MAP.get(css, css))
css = ' '.join(classes)
return css
def td(value, schema=None, css=Undef, tag=Undef, **kw):
css = Undef.sa(css, kw, 'c')
value = uval(value)
if schema:
value = schema.html(value)
if css is Undef: css = schema.css(value)
if tag is Undef: tag = 'td'
css = trd_joinc(css)
return u'<%s%s>%s</%s>' % (tag, ui.classif(css), value, tag)
def tr(values, schema=None, css=Undef, celltag=Undef, **kw):
css = Undef.sa(css, kw, 'c')
tdschema = lambda value, index: None
if schema is not None:
if css is Undef: css = schema.css(values)
tdschema = lambda value, index: schema.cell_schema(value, index)
css = trd_joinc(css)
lines = []
lines.append(u'<tr%s>' % ui.classif(css))
index = 0
if isinstance(values, dict) or isinstance(values, Row):
if schema is not None:
for col in schema.cols:
value = values[col.key]
lines.append(td(value, tdschema(value, index), tag=celltag))
index += 1
else:
for value in values.values():
lines.append(td(value, tdschema(value, index), tag=celltag))
index += 1
else:
for value in values:
lines.append(td(value, tdschema(value, index), tag=celltag))
index += 1
lines.append(u'</tr>')
return u'\n'.join(lines)
TABLE_CLASS_MAP = dict(
auto="table-auto", a="table-auto",
striped="table-striped", strip="table-striped", s="table-striped",
bordered="table-bordered", border="table-bordered", b="table-bordered",
hover="table-hover", h="table-hover",
condensed="table-condensed", c="table-condensed",
)
def table_joinc(css):
if css is Undef: css = None
if type(css) is StringType:
css = css.split(",")
classes = ['table']
for css in seqof(css):
classes.append(TABLE_CLASS_MAP.get(css, css))
css = ' '.join(classes)
return css
def table(rows, schema=None, css=Undef, **kw):
css = Undef.sa(css, kw, 'c')
if schema is None: schema = TableSchema.parse(rows)
elif isseq(schema): schema = TableSchema(schema)
if css is Undef: css = schema.css(rows)
css = table_joinc(css)
lines = []
lines.append(u'<table%s>' % ui.classif(css))
lines.append(u'<thead>')
lines.append(tr(schema.headers(), schema.header_row_schema(), celltag='th'))
lines.append(u'</thead><tbody>')
index = 0
for row in rows:
lines.append(tr(row, schema.body_row_schema(index)))
index += 1
lines.append(u'</tbody>')
lines.append(u'</table>')
return u'\n'.join(lines)
################################################################################
# Menu
@ -265,12 +354,12 @@ class Alert(object):
showtb = None
def __init__(self, msg=None, exc_info=Undef, type=Undef, closeable=Undef, escape=Undef, action=None, showtb=True, **kw):
exc_info = sa(exc_info, kw, 'e')
exc_info = Undef.sa(exc_info, kw, 'e')
if exc_info is Undef: exc_info = sys.exc_info()
if exc_info == (None, None, None): exc_info = None
type = sa(type, kw, 't', "error")
closeable = sa(closeable, kw, 'c', False)
escape = sa(escape, kw, 'x', None)
type = Undef.sa(type, kw, 't', "error")
closeable = Undef.sa(closeable, kw, 'c', False)
escape = Undef.sa(escape, kw, 'x', None)
self.msg = msg
self.exc_info = exc_info
@ -281,7 +370,7 @@ class Alert(object):
self.showtb = showtb
def __call__(self, msg=Undef, exc_info=Undef, type=Undef, closeable=Undef, escape=Undef, action=Undef, showtb=Undef, **kw):
exc_info = sa(exc_info, kw, 'e')
exc_info = Undef.sa(exc_info, kw, 'e')
if msg is Undef:
# si on ne spécifie pas de message, alors prendre la valeur actuelle
msg = self.msg
@ -290,11 +379,11 @@ class Alert(object):
# si on spécifie un message, alors prendre aussi l'exception courante
if exc_info is Undef: exc_info = sys.exc_info()
if exc_info == (None, None, None): exc_info = None
type = sa(type, kw, 't', self.type)
closeable = sa(closeable, kw, 'c', self.closeable)
escape = sa(escape, kw, 'x', self.escape)
action = sa(action, kw, None, self.action)
showtb = sa(showtb, kw, None, self.showtb)
type = Undef.sa(type, kw, 't', self.type)
closeable = Undef.sa(closeable, kw, 'c', self.closeable)
escape = Undef.sa(escape, kw, 'x', self.escape)
action = Undef.sa(action, kw, None, self.action)
showtb = Undef.sa(showtb, kw, None, self.showtb)
self.msg = msg
self.exc_info = exc_info
@ -306,7 +395,7 @@ class Alert(object):
return self
def render(self, msg=Undef, exc_info=Undef, type=Undef, closeable=Undef, escape=Undef, action=Undef, showtb=Undef, **kw):
exc_info = sa(exc_info, kw, 'e')
exc_info = Undef.sa(exc_info, kw, 'e')
if msg is Undef:
# si on ne spécifie pas de message, alors prendre la valeur initiale
msg = self.msg
@ -314,11 +403,11 @@ class Alert(object):
else:
# si on spécifie un message, alors prendre aussi l'exception courante
if exc_info is Undef: exc_info = sys.exc_info()
type = sa(type, kw, 't', self.type)
closeable = sa(closeable, kw, 'c', self.closeable)
escape = sa(escape, kw, 'x', self.escape)
action = sa(action, kw, None, self.action)
showtb = sa(showtb, kw, None, self.showtb)
type = Undef.sa(type, kw, 't', self.type)
closeable = Undef.sa(closeable, kw, 'c', self.closeable)
escape = Undef.sa(escape, kw, 'x', self.escape)
action = Undef.sa(action, kw, None, self.action)
showtb = Undef.sa(showtb, kw, None, self.showtb)
if callable(msg):
# si msg est callable, par défaut ne pas mettre le résultat en

View File

@ -3,14 +3,19 @@
__all__ = (
'LazyObject',
'dbconfig', 'fixsqlite', 'lazydb', 'oradbinfos',
'incd', 'excd', 'Row', 'RowCtl',
'incd', 'excd', 'ColSchema', 'Col', 'Row', 'RowCtl',
'Migration',
)
from os import path
from types import StringType
import csv
from ..base import Undef, odict, seqof
from ..ext import web
from .api import uval
Undef = Undef() # spécifique à ce module
class LazyObject(object):
"""Un objet proxy vers un autre objet qui est construit avec retard
@ -84,6 +89,69 @@ class oradbinfos(object):
def __unicode__(self):
return self()
class ColSchema(odict):
def __init__(self, formatter=Undef, link=Undef, **kw):
formatter = Undef.sa(formatter, kw, 'f', None)
link = Undef.sa(link, kw, 'u', None)
super(ColSchema, self).__init__(**kw)
self.formatter = formatter
self.link = link
def css(self, value=Undef):
return None
def format(self, value, index=Undef):
formatter = self.formatter
if type(formatter) is StringType:
return formatter % value
elif formatter is not None:
return formatter(value)
else:
return value
def text(self, value, index=Undef):
return self.format(value, index)
def html(self, value, index=Undef):
link = uval(self.link)
value = self.format(value, index)
if link is None: return value
else: return u'<a href="%s">%s</a>' % (link, value)
class Col(odict):
def __init__(self, key, title=Undef, schema=Undef, formatter=Undef, link=Undef, **kw):
title = Undef.sa(title, kw, 't', None)
schema = Undef.sa(schema, kw, 's', None)
formatter = Undef.sa(formatter, kw, 'f')
link = Undef.sa(link, kw, 'u')
super(Col, self).__init__(**kw)
if title is None: title = key
if schema is None and (formatter is not Undef or link is not Undef):
schema = ColSchema(formatter, link)
self.key = key
self.title = title
self.schema = schema
def geti(values, index):
return values[index] if index >= 0 and index < len(values) else None
class RowSchema(odict):
def __init__(self, cols=Undef, **kw):
cols = seqof(Undef.sa(cols, kw, None, None))
super(RowSchema, self).__init__(**kw)
self.cols = cols
def col(self, index=Undef):
if index is not Undef:
return geti(self.cols, index)
return None
def cell_schema(self, value=Undef, index=Undef):
if index is not Undef:
col = geti(self.cols, index)
if col is not None: return col.schema
return None
def css(self, value=Undef):
return None
class Row(object):
"""un proxy vers une ligne de web.database
@ -94,8 +162,24 @@ class Row(object):
def __init__(self, row):
self.__dict__['_row'] = row
def __contains__(self, name):
return name in self._row
def keys(self): return self._row.keys()
def values(self): return self._row.values()
def items(self): return self._row.items()
def has_key(self): return self._row.has_key()
def get(self): return self._row.get()
def clear(self): return self._row.clear()
def setdefault(self): return self._row.setdefault()
def iterkeys(self): return self._row.iterkeys()
def itervalues(self): return self._row.itervalues()
def iteritems(self): return self._row.iteritems()
def pop(self): return self._row.pop()
def popitem(self): return self._row.popitem()
def update(self): return self._row.update()
def __iter__(self): return self._row.__iter__()
def __contains__(self, key): return self._row.__contains__(key)
def copy(self): return self.__class__(self._row)
def __getattr__(self, name):
row = self._row
try:
@ -128,9 +212,36 @@ class Row(object):
def __delitem__(self, key):
del self._row[key]
def __repr__(self): return repr(self._row)
def __repr__(self): return '<%s:%r>' % (self.__class__.__name__, self._row)
def __str__(self): return str(self._row)
def _firstof(values):
for value in values:
return value
return None
class TableSchema(odict):
@staticmethod
def parse(rows):
# on assume que rows est une liste de dictionnaires
cols = [Col(key) for key in _firstof(rows)] if rows else Undef
return TableSchema(cols)
def __init__(self, cols=Undef, **kw):
cols = seqof(Undef.sa(cols, kw, None, None))
super(TableSchema, self).__init__(**kw)
self.cols = cols
self.row_schemas = {}
self.row_schemas['header'] = RowSchema(cols)
self.row_schemas['body'] = RowSchema(cols)
def headers(self):
return [col.title for col in self.cols]
def header_row_schema(self):
return self.row_schemas["header"]
def body_row_schema(self, index):
return self.row_schemas["body"]
def incd(fd, *names):
td = dict()
if not names:

View File

@ -5,15 +5,15 @@ __all__ = (
'accesskeyif',
'classif', 'addclassif',
'favicon', 'css', 'js', 'jscss',
'u', 'p',
'p',
'Action', 'Menu',
)
from types import StringType, UnicodeType
import urlparse, urllib
from nulib.base import odict, isseq, seqof
from nulib.web import web
from .api import u
def checked(b):
return u' checked="checked"' if b else u''
@ -45,7 +45,7 @@ def css(href, media=None):
return ur'<link rel="stylesheet" href="%s" type="text/css"%s />' % (href, media or u'')
def js(href):
return ur'<script type="text/javascript" src="%s"></script>' % href
def jscss(href, min=False):
def jscss(href):
if href.endswith(".min.js"):
jshref = href
csshref = "%s.css" % href[:-len(".min.js")]
@ -57,15 +57,6 @@ def jscss(href, min=False):
csshref = href
return u"%s\n%s" % (js(jshref), css(csshref))
def u(text):
"""Si text n'est pas de l'unicode, le convertir en unicode avec l'encoding utf-8
"""
t = type(text)
if t is not UnicodeType:
if t is not StringType: text = str(text)
text = unicode(text, "utf-8")
return text
def p(text):
"""Si text commence par un tag, le laisser tel quel, sinon le mettre entre <p> et </p>
"""