Version 0.1

Added sidebar, Dashboard, Line Graph
This commit is contained in:
Ayxan
2022-05-23 04:12:37 +04:00
parent d660f2a4ca
commit 9e90b4a150
204 changed files with 29218 additions and 109 deletions

View File

@ -0,0 +1,57 @@
# Copyright (c) 2010-2022 openpyxl
from .base import *
from .sequence import Sequence
class MetaStrict(type):
def __new__(cls, clsname, bases, methods):
for k, v in methods.items():
if isinstance(v, Descriptor):
v.name = k
return type.__new__(cls, clsname, bases, methods)
class MetaSerialisable(type):
def __new__(cls, clsname, bases, methods):
attrs = []
nested = []
elements = []
namespaced = []
for k, v in methods.items():
if isinstance(v, Descriptor):
ns= getattr(v, 'namespace', None)
if ns:
namespaced.append((k, "{%s}%s" % (ns, k)))
if getattr(v, 'nested', False):
nested.append(k)
elements.append(k)
elif isinstance(v, Sequence):
elements.append(k)
elif isinstance(v, Typed):
if hasattr(v.expected_type, 'to_tree'):
elements.append(k)
else:
attrs.append(k)
else:
if not isinstance(v, Alias):
attrs.append(k)
if methods.get('__attrs__') is None:
methods['__attrs__'] = tuple(attrs)
methods['__namespaced__'] = tuple(namespaced)
if methods.get('__nested__') is None:
methods['__nested__'] = tuple(sorted(nested))
if methods.get('__elements__') is None:
methods['__elements__'] = tuple(sorted(elements))
return MetaStrict.__new__(cls, clsname, bases, methods)
Strict = MetaStrict('Strict', (object,), {})
_Serialiasable = MetaSerialisable('_Serialisable', (object,), {})
#del MetaStrict
#del MetaSerialisable

View File

@ -0,0 +1,268 @@
# Copyright (c) 2010-2022 openpyxl
"""
Based on Python Cookbook 3rd Edition, 8.13
http://chimera.labs.oreilly.com/books/1230000000393/ch08.html#_discussiuncion_130
"""
import datetime
import re
from openpyxl.utils.datetime import from_ISO8601
from .namespace import namespaced
class Descriptor(object):
def __init__(self, name=None, **kw):
self.name = name
for k, v in kw.items():
setattr(self, k, v)
def __set__(self, instance, value):
instance.__dict__[self.name] = value
class Typed(Descriptor):
"""Values must of a particular type"""
expected_type = type(None)
allow_none = False
nested = False
def __init__(self, *args, **kw):
super(Typed, self).__init__(*args, **kw)
self.__doc__ = "Values must be of type {0}".format(self.expected_type)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
if (not self.allow_none
or (self.allow_none and value is not None)):
raise TypeError('expected ' + str(self.expected_type))
super(Typed, self).__set__(instance, value)
def __repr__(self):
return self.__doc__
def _convert(expected_type, value):
"""
Check value is of or can be converted to expected type.
"""
if not isinstance(value, expected_type):
try:
value = expected_type(value)
except:
raise TypeError('expected ' + str(expected_type))
return value
class Convertible(Typed):
"""Values must be convertible to a particular type"""
def __set__(self, instance, value):
if ((self.allow_none and value is not None)
or not self.allow_none):
value = _convert(self.expected_type, value)
super(Convertible, self).__set__(instance, value)
class Max(Convertible):
"""Values must be less than a `max` value"""
expected_type = float
allow_none = False
def __init__(self, **kw):
if 'max' not in kw and not hasattr(self, 'max'):
raise TypeError('missing max value')
super(Max, self).__init__(**kw)
def __set__(self, instance, value):
if ((self.allow_none and value is not None)
or not self.allow_none):
value = _convert(self.expected_type, value)
if value > self.max:
raise ValueError('Max value is {0}'.format(self.max))
super(Max, self).__set__(instance, value)
class Min(Convertible):
"""Values must be greater than a `min` value"""
expected_type = float
allow_none = False
def __init__(self, **kw):
if 'min' not in kw and not hasattr(self, 'min'):
raise TypeError('missing min value')
super(Min, self).__init__(**kw)
def __set__(self, instance, value):
if ((self.allow_none and value is not None)
or not self.allow_none):
value = _convert(self.expected_type, value)
if value < self.min:
raise ValueError('Min value is {0}'.format(self.min))
super(Min, self).__set__(instance, value)
class MinMax(Min, Max):
"""Values must be greater than `min` value and less than a `max` one"""
pass
class Set(Descriptor):
"""Value can only be from a set of know values"""
def __init__(self, name=None, **kw):
if not 'values' in kw:
raise TypeError("missing set of values")
kw['values'] = set(kw['values'])
super(Set, self).__init__(name, **kw)
self.__doc__ = "Value must be one of {0}".format(self.values)
def __set__(self, instance, value):
if value not in self.values:
raise ValueError(self.__doc__)
super(Set, self).__set__(instance, value)
class NoneSet(Set):
"""'none' will be treated as None"""
def __init__(self, name=None, **kw):
super(NoneSet, self).__init__(name, **kw)
self.values.add(None)
def __set__(self, instance, value):
if value == 'none':
value = None
super(NoneSet, self).__set__(instance, value)
class Integer(Convertible):
expected_type = int
class Float(Convertible):
expected_type = float
class Bool(Convertible):
expected_type = bool
def __set__(self, instance, value):
if isinstance(value, str):
if value in ('false', 'f', '0'):
value = False
super(Bool, self).__set__(instance, value)
class String(Typed):
expected_type = str
class Text(String, Convertible):
pass
class ASCII(Typed):
expected_type = bytes
class Tuple(Typed):
expected_type = tuple
class Length(Descriptor):
def __init__(self, name=None, **kw):
if "length" not in kw:
raise TypeError("value length must be supplied")
super(Length, self).__init__(**kw)
def __set__(self, instance, value):
if len(value) != self.length:
raise ValueError("Value must be length {0}".format(self.length))
super(Length, self).__set__(instance, value)
class Default(Typed):
"""
When called returns an instance of the expected type.
Additional default values can be passed in to the descriptor
"""
def __init__(self, name=None, **kw):
if "defaults" not in kw:
kw['defaults'] = {}
super(Default, self).__init__(**kw)
def __call__(self):
return self.expected_type()
class Alias(Descriptor):
"""
Aliases can be used when either the desired attribute name is not allowed
or confusing in Python (eg. "type") or a more descriptve name is desired
(eg. "underline" for "u")
"""
def __init__(self, alias):
self.alias = alias
def __set__(self, instance, value):
setattr(instance, self.alias, value)
def __get__(self, instance, cls):
return getattr(instance, self.alias)
class MatchPattern(Descriptor):
"""Values must match a regex pattern """
allow_none = False
def __init__(self, name=None, **kw):
if 'pattern' not in kw and not hasattr(self, 'pattern'):
raise TypeError('missing pattern value')
super(MatchPattern, self).__init__(name, **kw)
self.test_pattern = re.compile(self.pattern, re.VERBOSE)
def __set__(self, instance, value):
if value is None and not self.allow_none:
raise ValueError("Value must not be none")
if ((self.allow_none and value is not None)
or not self.allow_none):
if not self.test_pattern.match(value):
raise ValueError('Value does not match pattern {0}'.format(self.pattern))
super(MatchPattern, self).__set__(instance, value)
class DateTime(Typed):
expected_type = datetime.datetime
def __set__(self, instance, value):
if value is not None and isinstance(value, str):
try:
value = from_ISO8601(value)
except ValueError:
raise ValueError("Value must be ISO datetime format")
super(DateTime, self).__set__(instance, value)

View File

@ -0,0 +1,112 @@
#copyright openpyxl 2010-2018
"""
Excel specific descriptors
"""
from openpyxl.xml.constants import REL_NS
from openpyxl.compat import safe_string
from openpyxl.xml.functions import Element
from . import (
MatchPattern,
MinMax,
Integer,
String,
Sequence,
)
from .serialisable import Serialisable
class HexBinary(MatchPattern):
pattern = "[0-9a-fA-F]+$"
class UniversalMeasure(MatchPattern):
pattern = r"[0-9]+(\.[0-9]+)?(mm|cm|in|pt|pc|pi)"
class TextPoint(MinMax):
"""
Size in hundredths of points.
In theory other units of measurement can be used but these are unbounded
"""
expected_type = int
min = -400000
max = 400000
Coordinate = Integer
class Percentage(MinMax):
pattern = r"((100)|([0-9][0-9]?))(\.[0-9][0-9]?)?%" # strict
min = -1000000
max = 1000000
def __set__(self, instance, value):
if isinstance(value, str) and "%" in value:
value = value.replace("%", "")
value = int(float(value) * 1000)
super(Percentage, self).__set__(instance, value)
class Extension(Serialisable):
uri = String()
def __init__(self,
uri=None,
):
self.uri = uri
class ExtensionList(Serialisable):
ext = Sequence(expected_type=Extension)
def __init__(self,
ext=(),
):
self.ext = ext
class Relation(String):
namespace = REL_NS
allow_none = True
class Base64Binary(MatchPattern):
# http://www.w3.org/TR/xmlschema11-2/#nt-Base64Binary
pattern = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$"
class Guid(MatchPattern):
# https://msdn.microsoft.com/en-us/library/dd946381(v=office.12).aspx
pattern = r"{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}"
class CellRange(MatchPattern):
pattern = r"^[$]?([A-Za-z]{1,3})[$]?(\d+)(:[$]?([A-Za-z]{1,3})[$]?(\d+)?)?$|^[A-Za-z]{1,3}:[A-Za-z]{1,3}$"
allow_none = True
def __set__(self, instance, value):
if value is not None:
value = value.upper()
super(CellRange, self).__set__(instance, value)
def _explicit_none(tagname, value, namespace=None):
"""
Override serialisation because explicit none required
"""
if namespace is not None:
tagname = "{%s}%s" % (namespace, tagname)
return Element(tagname, val=safe_string(value))

View File

@ -0,0 +1,12 @@
# copyright openpyxl 2010-2015
def namespaced(obj, tagname, namespace=None):
"""
Utility to create a namespaced tag for an object
"""
namespace = getattr(obj, "namespace", None) or namespace
if namespace is not None:
tagname = "{%s}%s" % (namespace, tagname)
return tagname

View File

@ -0,0 +1,131 @@
#copyright openpyxl 2010-2015
"""
Generic serialisable classes
"""
from .base import (
Convertible,
Bool,
Descriptor,
NoneSet,
MinMax,
Set,
Float,
Integer,
String,
Text,
)
from .sequence import Sequence
from openpyxl.compat import safe_string
from openpyxl.xml.functions import Element, localname, whitespace
class Nested(Descriptor):
nested = True
attribute = "val"
def __set__(self, instance, value):
if hasattr(value, "tag"):
tag = localname(value)
if tag != self.name:
raise ValueError("Tag does not match attribute")
value = self.from_tree(value)
super(Nested, self).__set__(instance, value)
def from_tree(self, node):
return node.get(self.attribute)
def to_tree(self, tagname=None, value=None, namespace=None):
namespace = getattr(self, "namespace", namespace)
if value is not None:
if namespace is not None:
tagname = "{%s}%s" % (namespace, tagname)
value = safe_string(value)
return Element(tagname, {self.attribute:value})
class NestedValue(Nested, Convertible):
"""
Nested tag storing the value on the 'val' attribute
"""
pass
class NestedText(NestedValue):
"""
Represents any nested tag with the value as the contents of the tag
"""
def from_tree(self, node):
return node.text
def to_tree(self, tagname=None, value=None, namespace=None):
namespace = getattr(self, "namespace", namespace)
if value is not None:
if namespace is not None:
tagname = "{%s}%s" % (namespace, tagname)
el = Element(tagname)
el.text = safe_string(value)
whitespace(el)
return el
class NestedFloat(NestedValue, Float):
pass
class NestedInteger(NestedValue, Integer):
pass
class NestedString(NestedValue, String):
pass
class NestedBool(NestedValue, Bool):
def from_tree(self, node):
return node.get("val", True)
class NestedNoneSet(Nested, NoneSet):
pass
class NestedSet(Nested, Set):
pass
class NestedMinMax(Nested, MinMax):
pass
class EmptyTag(Nested, Bool):
"""
Boolean if a tag exists or not.
"""
def from_tree(self, node):
return True
def to_tree(self, tagname=None, value=None, namespace=None):
if value:
namespace = getattr(self, "namespace", namespace)
if namespace is not None:
tagname = "{%s}%s" % (namespace, tagname)
return Element(tagname)

View File

@ -0,0 +1,127 @@
# copyright openpyxl 2010-2015
from openpyxl.compat import safe_string
from openpyxl.xml.functions import Element
from openpyxl.utils.indexed_list import IndexedList
from .base import Descriptor, Alias, _convert
from .namespace import namespaced
class Sequence(Descriptor):
"""
A sequence (list or tuple) that may only contain objects of the declared
type
"""
expected_type = type(None)
seq_types = (list, tuple)
idx_base = 0
unique = False
def __set__(self, instance, seq):
if not isinstance(seq, self.seq_types):
raise TypeError("Value must be a sequence")
seq = [_convert(self.expected_type, value) for value in seq]
if self.unique:
seq = IndexedList(seq)
super(Sequence, self).__set__(instance, seq)
def to_tree(self, tagname, obj, namespace=None):
"""
Convert the sequence represented by the descriptor to an XML element
"""
for idx, v in enumerate(obj, self.idx_base):
if hasattr(v, "to_tree"):
el = v.to_tree(tagname, idx)
else:
tagname = namespaced(obj, tagname, namespace)
el = Element(tagname)
el.text = safe_string(v)
yield el
class ValueSequence(Sequence):
"""
A sequence of primitive types that are stored as a single attribute.
"val" is the default attribute
"""
attribute = "val"
def to_tree(self, tagname, obj, namespace=None):
tagname = namespaced(self, tagname, namespace)
for v in obj:
yield Element(tagname, {self.attribute:safe_string(v)})
def from_tree(self, node):
return node.get(self.attribute)
class NestedSequence(Sequence):
"""
Wrap a sequence in an containing object
"""
count = False
def to_tree(self, tagname, obj, namespace=None):
tagname = namespaced(self, tagname, namespace)
container = Element(tagname)
if self.count:
container.set('count', str(len(obj)))
for v in obj:
container.append(v.to_tree())
return container
def from_tree(self, node):
return [self.expected_type.from_tree(el) for el in node]
class MultiSequence(Sequence):
"""
Sequences can contain objects with different tags
"""
def __set__(self, instance, seq):
if not isinstance(seq, (tuple, list)):
raise ValueError("Value must be a sequence")
seq = list(seq)
Descriptor.__set__(self, instance, seq)
def to_tree(self, tagname, obj, namespace=None):
"""
Convert the sequence represented by the descriptor to an XML element
"""
for v in obj:
el = v.to_tree(namespace=namespace)
yield el
class MultiSequencePart(Alias):
"""
Allow a multisequence to be built up from parts
Excluded from the instance __elements__ or __attrs__ as is effectively an Alias
"""
def __init__(self, expected_type, store):
self.expected_type = expected_type
self.store = store
def __set__(self, instance, value):
value = _convert(self.expected_type, value)
instance.__dict__[self.store].append(value)
def __get__(self, instance, cls):
return self

View File

@ -0,0 +1,240 @@
# copyright openpyxl 2010-2015
from copy import copy
from keyword import kwlist
KEYWORDS = frozenset(kwlist)
from . import Descriptor
from . import _Serialiasable
from .sequence import (
Sequence,
NestedSequence,
MultiSequencePart,
)
from .namespace import namespaced
from openpyxl.compat import safe_string
from openpyxl.xml.functions import (
Element,
localname,
)
seq_types = (list, tuple)
class Serialisable(_Serialiasable):
"""
Objects can serialise to XML their attributes and child objects.
The following class attributes are created by the metaclass at runtime:
__attrs__ = attributes
__nested__ = single-valued child treated as an attribute
__elements__ = child elements
"""
__attrs__ = None
__nested__ = None
__elements__ = None
__namespaced__ = None
idx_base = 0
@property
def tagname(self):
raise(NotImplementedError)
namespace = None
@classmethod
def from_tree(cls, node):
"""
Create object from XML
"""
# strip known namespaces from attributes
attrib = dict(node.attrib)
for key, ns in cls.__namespaced__:
if ns in attrib:
attrib[key] = attrib[ns]
del attrib[ns]
# strip attributes with unknown namespaces
for key in list(attrib):
if key.startswith('{'):
del attrib[key]
elif key in KEYWORDS:
attrib["_" + key] = attrib[key]
del attrib[key]
elif "-" in key:
n = key.replace("-", "_")
attrib[n] = attrib[key]
del attrib[key]
if node.text and "attr_text" in cls.__attrs__:
attrib["attr_text"] = node.text
for el in node:
tag = localname(el)
if tag in KEYWORDS:
tag = "_" + tag
desc = getattr(cls, tag, None)
if desc is None or isinstance(desc, property):
continue
if hasattr(desc, 'from_tree'):
#descriptor manages conversion
obj = desc.from_tree(el)
else:
if hasattr(desc.expected_type, "from_tree"):
#complex type
obj = desc.expected_type.from_tree(el)
else:
#primitive
obj = el.text
if isinstance(desc, NestedSequence):
attrib[tag] = obj
elif isinstance(desc, Sequence):
attrib.setdefault(tag, [])
attrib[tag].append(obj)
elif isinstance(desc, MultiSequencePart):
attrib.setdefault(desc.store, [])
attrib[desc.store].append(obj)
else:
attrib[tag] = obj
return cls(**attrib)
def to_tree(self, tagname=None, idx=None, namespace=None):
if tagname is None:
tagname = self.tagname
# keywords have to be masked
if tagname.startswith("_"):
tagname = tagname[1:]
tagname = namespaced(self, tagname, namespace)
namespace = getattr(self, "namespace", namespace)
attrs = dict(self)
for key, ns in self.__namespaced__:
if key in attrs:
attrs[ns] = attrs[key]
del attrs[key]
el = Element(tagname, attrs)
if "attr_text" in self.__attrs__:
el.text = safe_string(getattr(self, "attr_text"))
for child_tag in self.__elements__:
desc = getattr(self.__class__, child_tag, None)
obj = getattr(self, child_tag)
if hasattr(desc, "namespace") and hasattr(obj, 'namespace'):
obj.namespace = desc.namespace
if isinstance(obj, seq_types):
if isinstance(desc, NestedSequence):
# wrap sequence in container
if not obj:
continue
nodes = [desc.to_tree(child_tag, obj, namespace)]
elif isinstance(desc, Sequence):
# sequence
desc.idx_base = self.idx_base
nodes = (desc.to_tree(child_tag, obj, namespace))
else: # property
nodes = (v.to_tree(child_tag, namespace) for v in obj)
for node in nodes:
el.append(node)
else:
if child_tag in self.__nested__:
node = desc.to_tree(child_tag, obj, namespace)
elif obj is None:
continue
else:
node = obj.to_tree(child_tag)
if node is not None:
el.append(node)
return el
def __iter__(self):
for attr in self.__attrs__:
value = getattr(self, attr)
if attr.startswith("_"):
attr = attr[1:]
elif attr != "attr_text" and "_" in attr:
desc = getattr(self.__class__, attr)
if getattr(desc, "hyphenated", False):
attr = attr.replace("_", "-")
if attr != "attr_text" and value is not None:
yield attr, safe_string(value)
def __eq__(self, other):
if not self.__class__ == other.__class__:
return False
elif not dict(self) == dict(other):
return False
for el in self.__elements__:
if getattr(self, el) != getattr(other, el):
return False
return True
def __ne__(self, other):
return not self == other
def __repr__(self):
s = u"<{0}.{1} object>\nParameters:".format(
self.__module__,
self.__class__.__name__
)
args = []
for k in self.__attrs__ + self.__elements__:
v = getattr(self, k)
if isinstance(v, Descriptor):
v = None
args.append(u"{0}={1}".format(k, repr(v)))
args = u", ".join(args)
return u"\n".join([s, args])
def __hash__(self):
fields = []
for attr in self.__attrs__ + self.__elements__:
val = getattr(self, attr)
if isinstance(val, list):
val = tuple(val)
fields.append(val)
return hash(tuple(fields))
def __add__(self, other):
if type(self) != type(other):
raise TypeError("Cannot combine instances of different types")
vals = {}
for attr in self.__attrs__:
vals[attr] = getattr(self, attr) or getattr(other, attr)
for el in self.__elements__:
a = getattr(self, el)
b = getattr(other, el)
if a and b:
vals[el] = a + b
else:
vals[el] = a or b
return self.__class__(**vals)
def __copy__(self):
# serialise to xml and back to avoid shallow copies
xml = self.to_tree(tagname="dummy")
cp = self.__class__.from_tree(xml)
# copy any non-persisted attributed
for k in self.__dict__:
if k not in self.__attrs__ + self.__elements__:
v = copy(getattr(self, k))
setattr(cp, k, v)
return cp

View File

@ -0,0 +1,18 @@
# Metaclass for mixing slots and descriptors
# From "Programming in Python 3" by Mark Summerfield Ch.8 p. 383
class AutoSlotProperties(type):
def __new__(mcl, classname, bases, dictionary):
slots = list(dictionary.get("__slots__", []))
for getter_name in [key for key in dictionary if key.startswith("get_")]:
name = getter_name
slots.append("__" + name)
getter = dictionary.pop(getter_name)
setter = dictionary.get(setter_name, None)
if (setter is not None
and isinstance(setter, collections.Callable)):
del dictionary[setter_name]
dictionary[name] = property(getter. setter)
dictionary["__slots__"] = tuple(slots)
return super().__new__(mcl, classname, bases, dictionary)