feat: referentiel projets dynamique + validation nommage by design
Durcit la convention de nommage des projets (dérive constatée : 'Sliding Automation', 'code_versioning'... au lieu des formes canoniques). - trilium_api.py : projets_canoniques() lit le référentiel = valeurs du label projet sur les notes de type=projet (source unique, pas de constante en dur). Note-projet CodeVersioning créée (manquait). - mcp_server.py : _valider_projet() branché dans les 6 tools de création (add_decision/history/backlog, new_conversation, create_entite, add_skill). Refuse un projet non canonique (suggestion si faute) ou inconnu (renvoi au processus de création de projet). Ne verrouille pas si référentiel illisible. - lint_audit.py : VAL-nommage aligné sur le référentiel (attrape casse, espace ET snake_case ; l'ancien 'contient un espace' ratait code_versioning). - Données : 79 notes ré-étiquetées vers les 3 formes canoniques. Quality by design : l'erreur de nommage devient impossible à l'écriture, le Lint n'est plus que le filet de sécurité.
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env python
|
||||
#coding:utf-8
|
||||
# Author: mozman
|
||||
# Purpose: svg base element
|
||||
# Created: 08.09.2010
|
||||
# Copyright (c) 2010-2020, Manfred Moitzi
|
||||
# License: MIT License
|
||||
"""
|
||||
The **BaseElement** is the root for all SVG elements.
|
||||
"""
|
||||
|
||||
from svgwrite.etree import etree
|
||||
|
||||
import copy
|
||||
|
||||
from svgwrite.params import Parameter
|
||||
from svgwrite.utils import AutoID
|
||||
|
||||
|
||||
class BaseElement(object):
|
||||
"""
|
||||
The **BaseElement** is the root for all SVG elements. The SVG attributes
|
||||
are stored in **attribs**, and the SVG subelements are stored in
|
||||
**elements**.
|
||||
|
||||
"""
|
||||
elementname = 'baseElement'
|
||||
|
||||
def __init__(self, **extra):
|
||||
"""
|
||||
:param extra: extra SVG attributes (keyword arguments)
|
||||
|
||||
* add trailing '_' to reserved keywords: ``'class_'``, ``'from_'``
|
||||
* replace inner '-' by '_': ``'stroke_width'``
|
||||
|
||||
|
||||
SVG attribute names will be checked, if **debug** is `True`.
|
||||
|
||||
workaround for removed **attribs** parameter in Version 0.2.2::
|
||||
|
||||
# replace
|
||||
element = BaseElement(attribs=adict)
|
||||
|
||||
#by
|
||||
element = BaseElement()
|
||||
element.update(adict)
|
||||
|
||||
"""
|
||||
# the keyword 'factory' specifies the object creator
|
||||
factory = extra.pop('factory', None)
|
||||
if factory is not None:
|
||||
# take parameter from 'factory'
|
||||
self._parameter = factory._parameter
|
||||
else:
|
||||
# default parameter debug=True profile='full'
|
||||
self._parameter = Parameter()
|
||||
|
||||
# override debug setting
|
||||
debug = extra.pop('debug', None)
|
||||
if debug is not None:
|
||||
self._parameter.debug = debug
|
||||
|
||||
# override profile setting
|
||||
profile = extra.pop('profile', None)
|
||||
if profile is not None:
|
||||
self._parameter.profile = profile
|
||||
|
||||
self.attribs = dict()
|
||||
self.update(extra)
|
||||
self.elements = list()
|
||||
|
||||
def update(self, attribs):
|
||||
""" Update SVG Attributes from `dict` attribs.
|
||||
|
||||
Rules for keys:
|
||||
|
||||
1. trailing '_' will be removed (``'class_'`` -> ``'class'``)
|
||||
2. inner '_' will be replaced by '-' (``'stroke_width'`` -> ``'stroke-width'``)
|
||||
|
||||
"""
|
||||
for key, value in attribs.items():
|
||||
# remove trailing underscores
|
||||
# and replace inner underscores
|
||||
key = key.rstrip('_').replace('_', '-')
|
||||
self.__setitem__(key, value)
|
||||
|
||||
def copy(self):
|
||||
newobj = copy.copy(self) # shallow copy of object
|
||||
newobj.attribs = copy.copy(self.attribs) # shallow copy of attributes
|
||||
newobj.elements = copy.copy(self.elements) # shallow copy of subelements
|
||||
if 'id' in newobj.attribs: # create a new 'id'
|
||||
newobj['id'] = newobj.next_id()
|
||||
return newobj
|
||||
|
||||
@property
|
||||
def debug(self):
|
||||
return self._parameter.debug
|
||||
|
||||
@property
|
||||
def profile(self):
|
||||
return self._parameter.profile
|
||||
|
||||
@property
|
||||
def validator(self):
|
||||
return self._parameter.validator
|
||||
|
||||
@validator.setter
|
||||
def validator(self, value):
|
||||
self._parameter.validator = value
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
return self._parameter.get_version()
|
||||
|
||||
def set_parameter(self, parameter):
|
||||
self._parameter = parameter
|
||||
|
||||
def next_id(self, value=None):
|
||||
return AutoID.next_id(value)
|
||||
|
||||
def get_id(self):
|
||||
""" Get the object `id` string, if the object does not have an `id`,
|
||||
a new `id` will be created.
|
||||
|
||||
:returns: `string`
|
||||
"""
|
||||
if 'id' not in self.attribs:
|
||||
self.attribs['id'] = self.next_id()
|
||||
return self.attribs['id']
|
||||
|
||||
def get_iri(self):
|
||||
"""
|
||||
Get the `IRI` reference string of the object. (i.e., ``'#id'``).
|
||||
|
||||
:returns: `string`
|
||||
"""
|
||||
return "#%s" % self.get_id()
|
||||
|
||||
def get_funciri(self):
|
||||
"""
|
||||
Get the `FuncIRI` reference string of the object. (i.e. ``'url(#id)'``).
|
||||
|
||||
:returns: `string`
|
||||
"""
|
||||
return "url(%s)" % self.get_iri()
|
||||
|
||||
def __getitem__(self, key):
|
||||
""" Get SVG attribute by `key`.
|
||||
|
||||
:param string key: SVG attribute name
|
||||
:return: SVG attribute value
|
||||
|
||||
"""
|
||||
return self.attribs[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
""" Set SVG attribute by `key` to `value`.
|
||||
|
||||
:param string key: SVG attribute name
|
||||
:param object value: SVG attribute value
|
||||
|
||||
"""
|
||||
# Attribute checking is only done by using the __setitem__() method or
|
||||
# by self['attribute'] = value
|
||||
if self.debug:
|
||||
self.validator.check_svg_attribute_value(self.elementname, key, value)
|
||||
self.attribs[key] = value
|
||||
|
||||
def add(self, element):
|
||||
""" Add an SVG element as subelement.
|
||||
|
||||
:param element: append this SVG element
|
||||
:returns: the added element
|
||||
|
||||
"""
|
||||
if self.debug:
|
||||
self.validator.check_valid_children(self.elementname, element.elementname)
|
||||
self.elements.append(element)
|
||||
return element
|
||||
|
||||
def tostring(self):
|
||||
""" Get the XML representation as unicode `string`.
|
||||
|
||||
:return: unicode XML string of this object and all its subelements
|
||||
|
||||
"""
|
||||
xml = self.get_xml()
|
||||
# required for Python 2 support
|
||||
xml_utf8_str = etree.tostring(xml, encoding='utf-8')
|
||||
return xml_utf8_str.decode('utf-8')
|
||||
# just Python 3: return etree.tostring(xml, encoding='unicode')
|
||||
|
||||
def _repr_svg_(self):
|
||||
""" Show SVG in IPython, Jupyter Notebook, and Jupyter Lab
|
||||
|
||||
:return: unicode XML string of this object and all its subelements
|
||||
|
||||
"""
|
||||
return self.tostring()
|
||||
|
||||
def get_xml(self):
|
||||
""" Get the XML representation as `ElementTree` object.
|
||||
|
||||
:return: XML `ElementTree` of this object and all its subelements
|
||||
|
||||
"""
|
||||
xml = etree.Element(self.elementname)
|
||||
if self.debug:
|
||||
self.validator.check_all_svg_attribute_values(self.elementname, self.attribs)
|
||||
for attribute, value in sorted(self.attribs.items()):
|
||||
# filter 'None' values
|
||||
if value is not None:
|
||||
value = self.value_to_string(value)
|
||||
if value: # just add not empty attributes
|
||||
xml.set(attribute, value)
|
||||
|
||||
for element in self.elements:
|
||||
xml.append(element.get_xml())
|
||||
return xml
|
||||
|
||||
def value_to_string(self, value):
|
||||
"""
|
||||
Converts *value* into a <string> includes a value check, depending
|
||||
on :attr:`self.debug` and :attr:`self.profile`.
|
||||
|
||||
"""
|
||||
if isinstance(value, (int, float)):
|
||||
if self.debug:
|
||||
self.validator.check_svg_type(value, 'number')
|
||||
if isinstance(value, float) and self.profile == 'tiny':
|
||||
value = round(value, 4)
|
||||
return str(value)
|
||||
|
||||
def set_desc(self, title=None, desc=None):
|
||||
""" Insert a **title** and/or a **desc** element as first subelement.
|
||||
"""
|
||||
if desc is not None:
|
||||
self.elements.insert(0, Desc(desc))
|
||||
if title is not None:
|
||||
self.elements.insert(0, Title(title))
|
||||
|
||||
def set_metadata(self, xmldata):
|
||||
"""
|
||||
:param xmldata: an xml.etree.ElementTree - Element() object.
|
||||
"""
|
||||
metadata = Metadata(xmldata)
|
||||
if len(self.elements) == 0:
|
||||
self.elements.append(metadata)
|
||||
else:
|
||||
pos = 0
|
||||
while self.elements[pos].elementname in ('title', 'desc'):
|
||||
pos += 1
|
||||
if pos == len(self.elements):
|
||||
self.elements.append(metadata)
|
||||
return
|
||||
if self.elements[pos].elementname == 'metadata':
|
||||
self.elements[pos].xml.append(xmldata)
|
||||
else:
|
||||
self.elements.insert(pos, metadata)
|
||||
|
||||
|
||||
class Title(object):
|
||||
elementname = 'title'
|
||||
|
||||
def __init__(self, text):
|
||||
self.xml = etree.Element(self.elementname)
|
||||
self.xml.text = str(text)
|
||||
|
||||
def get_xml(self):
|
||||
return self.xml
|
||||
|
||||
|
||||
class Desc(Title):
|
||||
elementname = 'desc'
|
||||
|
||||
|
||||
class Metadata(Title):
|
||||
elementname = 'metadata'
|
||||
|
||||
def __init__(self, xmldata):
|
||||
"""
|
||||
:param xmldata: an xml.etree.ElementTree - Element() object.
|
||||
"""
|
||||
self.xml = etree.Element('metadata')
|
||||
self.xml.append(xmldata)
|
||||
Reference in New Issue
Block a user