forked from ScoDoc/ScoDoc
migration exports xml
This commit is contained in:
parent
6f885edfe4
commit
dc726f1d10
21
README.md
21
README.md
@ -58,11 +58,28 @@ Installation:
|
|||||||
|
|
||||||
donc utiliser:
|
donc utiliser:
|
||||||
|
|
||||||
pip install -r requirements.txt
|
pip install -r requirements-2.7.txt
|
||||||
|
|
||||||
pour régénerer ce fichier:
|
pour régénerer ce fichier:
|
||||||
|
|
||||||
pip freeze > requirements.txt
|
pip freeze > requirements-2.7.txt
|
||||||
|
|
||||||
|
## Setup python3.7
|
||||||
|
Debian 10 est livré avec Python 3.7.
|
||||||
|
|
||||||
|
apt-get install python3-dev
|
||||||
|
apt-get install python3-venv
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
Puis installation de Flask:
|
||||||
|
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install flask
|
||||||
|
pip install wheel
|
||||||
|
|
||||||
|
Installer les dépendances:
|
||||||
|
|
||||||
|
pip install -r requirements-2.7.txt
|
||||||
|
|
||||||
## Bases de données
|
## Bases de données
|
||||||
ScoDoc8 utilise les bases de département de ScoDoc7, mais une nouvelle base
|
ScoDoc8 utilise les bases de département de ScoDoc7, mais une nouvelle base
|
||||||
|
@ -43,11 +43,10 @@ Par exemple, la clé '_css_row_class' spécifie le style CSS de la ligne.
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import random
|
import random
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from xml.etree import ElementTree
|
||||||
# XML generation package (apt-get install jaxml)
|
|
||||||
import jaxml
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from xml.etree import ElementTree
|
||||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak
|
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak
|
||||||
from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
|
from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
|
||||||
from reportlab.lib.colors import Color
|
from reportlab.lib.colors import Color
|
||||||
@ -59,6 +58,7 @@ from app.scodoc import html_sco_header
|
|||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc import sco_excel
|
from app.scodoc import sco_excel
|
||||||
from app.scodoc import sco_pdf
|
from app.scodoc import sco_pdf
|
||||||
|
from app.scodoc import sco_xml
|
||||||
from app.scodoc.sco_pdf import SU
|
from app.scodoc.sco_pdf import SU
|
||||||
from app.scodoc.notes_log import log
|
from app.scodoc.notes_log import log
|
||||||
|
|
||||||
@ -567,28 +567,25 @@ class GenTable(object):
|
|||||||
The tag names <table> and <row> can be changed using
|
The tag names <table> and <row> can be changed using
|
||||||
xml_outer_tag and xml_row_tag
|
xml_outer_tag and xml_row_tag
|
||||||
"""
|
"""
|
||||||
doc = jaxml.XML_document(encoding=scu.SCO_ENCODING)
|
doc = ElementTree.Element(
|
||||||
getattr(doc, self.xml_outer_tag)(
|
self.xml_outer_tag,
|
||||||
id=self.table_id, origin=self.origin or "", caption=self.caption or ""
|
id=self.table_id,
|
||||||
|
origin=self.origin or "",
|
||||||
|
caption=self.caption or "",
|
||||||
)
|
)
|
||||||
doc._push()
|
|
||||||
for row in self.rows:
|
for row in self.rows:
|
||||||
doc._push()
|
x_row = ElementTree.Element(self.xml_row_tag)
|
||||||
row_title = row.get("row_title", "")
|
row_title = row.get("row_title", "")
|
||||||
if row_title:
|
if row_title:
|
||||||
getattr(doc, self.xml_row_tag)(title=row_title)
|
x_row.set("title", row_title)
|
||||||
else:
|
doc.append(x_row)
|
||||||
getattr(doc, self.xml_row_tag)()
|
|
||||||
for cid in self.columns_ids:
|
for cid in self.columns_ids:
|
||||||
doc._push()
|
|
||||||
v = row.get(cid, "")
|
v = row.get(cid, "")
|
||||||
if v is None:
|
if v is None:
|
||||||
v = ""
|
v = ""
|
||||||
getattr(doc, cid)(value=str(v))
|
x_cell = ElementTree.Element(cid, value=str(v))
|
||||||
doc._pop()
|
x_row.append(x_cell)
|
||||||
doc._pop()
|
return sco_xml.XML_HEADER + ElementTree.tostring(doc)
|
||||||
doc._pop()
|
|
||||||
return repr(doc)
|
|
||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
"""JSON representation of the table."""
|
"""JSON representation of the table."""
|
||||||
|
@ -102,7 +102,7 @@ import app.scodoc.notesdb as ndb
|
|||||||
from app.scodoc.notes_log import log
|
from app.scodoc.notes_log import log
|
||||||
from app.scodoc.sco_exceptions import ScoValueError, FormatError
|
from app.scodoc.sco_exceptions import ScoValueError, FormatError
|
||||||
from app.scodoc.gen_tables import GenTable
|
from app.scodoc.gen_tables import GenTable
|
||||||
from app.scodoc.sco_formsemestre import ApoEtapeVDI
|
from app.scodoc.sco_vdi import ApoEtapeVDI
|
||||||
from app.scodoc.sco_codes_parcours import code_semestre_validant
|
from app.scodoc.sco_codes_parcours import code_semestre_validant
|
||||||
from app.scodoc.sco_codes_parcours import (
|
from app.scodoc.sco_codes_parcours import (
|
||||||
ADC,
|
ADC,
|
||||||
|
@ -38,7 +38,7 @@ from app.scodoc import sco_users
|
|||||||
from app.scodoc.gen_tables import GenTable
|
from app.scodoc.gen_tables import GenTable
|
||||||
from app.scodoc.notes_log import log
|
from app.scodoc.notes_log import log
|
||||||
from app.scodoc.sco_codes_parcours import NO_SEMESTRE_ID
|
from app.scodoc.sco_codes_parcours import NO_SEMESTRE_ID
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_vdi import ApoEtapeVDI
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
@ -385,100 +385,6 @@ def _write_formsemestre_aux(context, sem, fieldname, valuename):
|
|||||||
cnx.commit()
|
cnx.commit()
|
||||||
|
|
||||||
|
|
||||||
# ------ Utilisé pour stocker le VDI avec le code étape (noms de fichiers maquettes et code semestres)
|
|
||||||
class ApoEtapeVDI(object):
|
|
||||||
_ETAPE_VDI_SEP = "!"
|
|
||||||
|
|
||||||
def __init__(self, etape_vdi=None, etape="", vdi=""):
|
|
||||||
"""Build from string representation, e.g. 'V1RT!111'"""
|
|
||||||
if etape_vdi:
|
|
||||||
self.etape_vdi = etape_vdi
|
|
||||||
self.etape, self.vdi = self.split_etape_vdi(etape_vdi)
|
|
||||||
elif etape:
|
|
||||||
if self._ETAPE_VDI_SEP in etape:
|
|
||||||
raise ScoValueError("valeur code etape invalide")
|
|
||||||
self.etape, self.vdi = etape, vdi
|
|
||||||
self.etape_vdi = self.concat_etape_vdi(etape, vdi)
|
|
||||||
else:
|
|
||||||
self.etape_vdi, self.etape, self.vdi = "", "", ""
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return self.__class__.__name__ + "('" + str(self) + "')"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.etape_vdi
|
|
||||||
|
|
||||||
def _cmp(self, other):
|
|
||||||
"""Test égalité de deux codes étapes.
|
|
||||||
Si le VDI des deux est spécifié, on l'utilise. Sinon, seul le code étape est pris en compte.
|
|
||||||
Donc V1RT == V1RT!111, V1RT!110 == V1RT, V1RT!77 != V1RT!78, ...
|
|
||||||
|
|
||||||
Compare the two objects x (=self) and y and return an integer according to
|
|
||||||
the outcome. The return value is negative if x < y, zero if x == y
|
|
||||||
and strictly positive if x > y.
|
|
||||||
"""
|
|
||||||
if other is None:
|
|
||||||
return -1
|
|
||||||
if type(other) == str:
|
|
||||||
other = ApoEtapeVDI(other)
|
|
||||||
|
|
||||||
if self.vdi and other.vdi:
|
|
||||||
x = (self.etape, self.vdi)
|
|
||||||
y = (other.etape, other.vdi)
|
|
||||||
else:
|
|
||||||
x = self.etape
|
|
||||||
y = other.etape
|
|
||||||
|
|
||||||
return (x > y) - (x < y)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self._cmp(other) == 0
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
return self._cmp(other) != 0
|
|
||||||
|
|
||||||
def __lt__(self, other):
|
|
||||||
return self._cmp(other) < 0
|
|
||||||
|
|
||||||
def __le__(self, other):
|
|
||||||
return self._cmp(other) <= 0
|
|
||||||
|
|
||||||
def __gt__(self, other):
|
|
||||||
return self._cmp(other) > 0
|
|
||||||
|
|
||||||
def __ge__(self, other):
|
|
||||||
return self._cmp(other) >= 0
|
|
||||||
|
|
||||||
def split_etape_vdi(self, etape_vdi):
|
|
||||||
"""Etape Apogee can be stored as 'V1RT' or, including the VDI version,
|
|
||||||
as 'V1RT!111'
|
|
||||||
Returns etape, VDI
|
|
||||||
"""
|
|
||||||
if etape_vdi:
|
|
||||||
t = etape_vdi.split(self._ETAPE_VDI_SEP)
|
|
||||||
if len(t) == 1:
|
|
||||||
etape = etape_vdi
|
|
||||||
vdi = ""
|
|
||||||
elif len(t) == 2:
|
|
||||||
etape, vdi = t
|
|
||||||
else:
|
|
||||||
raise ValueError("invalid code etape")
|
|
||||||
return etape, vdi
|
|
||||||
else:
|
|
||||||
return etape_vdi, ""
|
|
||||||
|
|
||||||
def concat_etape_vdi(self, etape, vdi=""):
|
|
||||||
if vdi:
|
|
||||||
return self._ETAPE_VDI_SEP.join([etape, vdi])
|
|
||||||
else:
|
|
||||||
return etape
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
[ ApoEtapeVDI('V1RT!111'), ApoEtapeVDI('V1RT!112'), ApoEtapeVDI('VCRT'), ApoEtapeVDI('V1RT') ]
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def sem_set_responsable_name(context, sem):
|
def sem_set_responsable_name(context, sem):
|
||||||
"ajoute champs responsable_name"
|
"ajoute champs responsable_name"
|
||||||
sem["responsable_name"] = ", ".join(
|
sem["responsable_name"] = ", ".join(
|
||||||
|
@ -37,8 +37,8 @@ from app.scodoc import sco_groups
|
|||||||
from app.scodoc.notes_log import log
|
from app.scodoc.notes_log import log
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator, TF
|
from app.scodoc.TrivialFormulator import TrivialFormulator, TF
|
||||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||||
from app.scodoc.sco_formsemestre import ApoEtapeVDI
|
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
from app.scodoc.sco_vdi import ApoEtapeVDI
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.scodoc import sco_codes_parcours
|
from app.scodoc import sco_codes_parcours
|
||||||
from app.scodoc import sco_compute_moy
|
from app.scodoc import sco_compute_moy
|
||||||
|
@ -50,7 +50,7 @@ from app.scodoc.gen_tables import GenTable
|
|||||||
from app.scodoc.notes_log import log
|
from app.scodoc.notes_log import log
|
||||||
from app.scodoc.sco_etape_bilan import EtapeBilan
|
from app.scodoc.sco_etape_bilan import EtapeBilan
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
from app.scodoc.sco_formsemestre import ApoEtapeVDI
|
from app.scodoc.sco_vdi import ApoEtapeVDI
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
|
@ -43,10 +43,10 @@ import time
|
|||||||
import types
|
import types
|
||||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||||
import six.moves.urllib.request, six.moves.urllib.error, six.moves.urllib.parse
|
import six.moves.urllib.request, six.moves.urllib.error, six.moves.urllib.parse
|
||||||
import xml.sax.saxutils
|
|
||||||
|
|
||||||
# XML generation package (apt-get install jaxml)
|
# XML generation package (apt-get install jaxml)
|
||||||
import jaxml
|
import jaxml # XXX
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import six
|
import six
|
||||||
@ -66,8 +66,11 @@ from config import Config
|
|||||||
|
|
||||||
from app.scodoc.SuppressAccents import suppression_diacritics
|
from app.scodoc.SuppressAccents import suppression_diacritics
|
||||||
from app.scodoc.notes_log import log
|
from app.scodoc.notes_log import log
|
||||||
|
from app.scodoc.sco_vdi import ApoEtapeVDI
|
||||||
|
from app.scodoc.sco_xml import quote_xml_attr
|
||||||
from app.scodoc.sco_codes_parcours import NOTES_TOLERANCE, CODES_EXPL
|
from app.scodoc.sco_codes_parcours import NOTES_TOLERANCE, CODES_EXPL
|
||||||
from app.scodoc import sco_exceptions
|
from app.scodoc import sco_exceptions
|
||||||
|
from app.scodoc import sco_xml
|
||||||
from app.scodoc import VERSION
|
from app.scodoc import VERSION
|
||||||
|
|
||||||
# ----- TEMPORAIRE POUR MIGRATION SCODOC7 -> SCODOC8 avant python3
|
# ----- TEMPORAIRE POUR MIGRATION SCODOC7 -> SCODOC8 avant python3
|
||||||
@ -445,74 +448,6 @@ def unescape_html_dict(d):
|
|||||||
unescape_html_dict(v)
|
unescape_html_dict(v)
|
||||||
|
|
||||||
|
|
||||||
def quote_xml_attr(data):
|
|
||||||
"""Escape &, <, >, quotes and double quotes"""
|
|
||||||
return xml.sax.saxutils.escape(str(data), {"'": "'", '"': """})
|
|
||||||
|
|
||||||
|
|
||||||
def dict_quote_xml_attr(d, fromhtml=False):
|
|
||||||
"""Quote XML entities in dict values.
|
|
||||||
Non recursive (but probbaly should be...).
|
|
||||||
Returns a new dict.
|
|
||||||
"""
|
|
||||||
if fromhtml:
|
|
||||||
# passe d'un code HTML a un code XML
|
|
||||||
return dict([(k, quote_xml_attr(unescape_html(v))) for (k, v) in d.items()])
|
|
||||||
else:
|
|
||||||
# passe d'une chaine non quotée a du XML
|
|
||||||
return dict([(k, quote_xml_attr(v)) for (k, v) in d.items()])
|
|
||||||
|
|
||||||
|
|
||||||
def simple_dictlist2xml(dictlist, doc=None, tagname=None, quote=False):
|
|
||||||
"""Represent a dict as XML data.
|
|
||||||
All keys with string or numeric values are attributes (numbers converted to strings).
|
|
||||||
All list values converted to list of childs (recursively).
|
|
||||||
*** all other values are ignored ! ***
|
|
||||||
Values (xml entities) are not quoted, except if requested by quote argument.
|
|
||||||
|
|
||||||
Exemple:
|
|
||||||
simple_dictlist2xml([ { 'id' : 1, 'ues' : [{'note':10},{}] } ], tagname='infos')
|
|
||||||
|
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<infos id="1">
|
|
||||||
<ues note="10" />
|
|
||||||
<ues />
|
|
||||||
</infos>
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not tagname:
|
|
||||||
raise ValueError("invalid empty tagname !")
|
|
||||||
if not doc:
|
|
||||||
doc = jaxml.XML_document(encoding=SCO_ENCODING)
|
|
||||||
scalar_types = [bytes, str, int, float]
|
|
||||||
for d in dictlist:
|
|
||||||
doc._push()
|
|
||||||
if (
|
|
||||||
type(d) == types.InstanceType or type(d) in scalar_types
|
|
||||||
): # pour ApoEtapeVDI et listes de chaines
|
|
||||||
getattr(doc, tagname)(code=str(d))
|
|
||||||
else:
|
|
||||||
if quote:
|
|
||||||
d_scalar = dict(
|
|
||||||
[
|
|
||||||
(k, quote_xml_attr(v))
|
|
||||||
for (k, v) in d.items()
|
|
||||||
if type(v) in scalar_types
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
d_scalar = dict(
|
|
||||||
[(k, v) for (k, v) in d.items() if type(v) in scalar_types]
|
|
||||||
)
|
|
||||||
getattr(doc, tagname)(**d_scalar)
|
|
||||||
d_list = dict([(k, v) for (k, v) in d.items() if type(v) == list])
|
|
||||||
if d_list:
|
|
||||||
for (k, v) in d_list.items():
|
|
||||||
simple_dictlist2xml(v, doc=doc, tagname=k, quote=quote)
|
|
||||||
doc._pop()
|
|
||||||
return doc
|
|
||||||
|
|
||||||
|
|
||||||
# Expressions used to check noms/prenoms
|
# Expressions used to check noms/prenoms
|
||||||
FORBIDDEN_CHARS_EXP = re.compile(r"[*\|~\(\)\\]")
|
FORBIDDEN_CHARS_EXP = re.compile(r"[*\|~\(\)\\]")
|
||||||
ALPHANUM_EXP = re.compile(r"^[\w-]+$", re.UNICODE)
|
ALPHANUM_EXP = re.compile(r"^[\w-]+$", re.UNICODE)
|
||||||
@ -615,15 +550,13 @@ def sendPDFFile(REQUEST, data, filename):
|
|||||||
|
|
||||||
class ScoDocJSONEncoder(json.JSONEncoder):
|
class ScoDocJSONEncoder(json.JSONEncoder):
|
||||||
def default(self, o): # pylint: disable=E0202
|
def default(self, o): # pylint: disable=E0202
|
||||||
from app.scodoc import sco_formsemestre
|
|
||||||
|
|
||||||
# ScoDoc 7.22 n'utilise plus mx:
|
# ScoDoc 7.22 n'utilise plus mx:
|
||||||
if str(type(o)) == "<type 'mx.DateTime.DateTime'>":
|
if str(type(o)) == "<type 'mx.DateTime.DateTime'>":
|
||||||
log("Warning: mx.DateTime object detected !")
|
log("Warning: mx.DateTime object detected !")
|
||||||
return o.strftime("%Y-%m-%dT%H:%M:%S")
|
return o.strftime("%Y-%m-%dT%H:%M:%S")
|
||||||
if isinstance(o, (datetime.date, datetime.datetime)):
|
if isinstance(o, (datetime.date, datetime.datetime)):
|
||||||
return o.isoformat()
|
return o.isoformat()
|
||||||
elif isinstance(o, sco_formsemestre.ApoEtapeVDI):
|
elif isinstance(o, ApoEtapeVDI):
|
||||||
return str(o)
|
return str(o)
|
||||||
else:
|
else:
|
||||||
return json.JSONEncoder.default(self, o)
|
return json.JSONEncoder.default(self, o)
|
||||||
@ -641,14 +574,9 @@ def sendXML(REQUEST, data, tagname=None, force_outer_xml_tag=True):
|
|||||||
data = [data] # always list-of-dicts
|
data = [data] # always list-of-dicts
|
||||||
if force_outer_xml_tag:
|
if force_outer_xml_tag:
|
||||||
root_tagname = tagname + "_list"
|
root_tagname = tagname + "_list"
|
||||||
doc = jaxml.XML_document(encoding=SCO_ENCODING)
|
data = [{root_tagname: data}]
|
||||||
getattr(doc, root_tagname)()
|
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname)
|
||||||
doc._push()
|
|
||||||
else:
|
|
||||||
doc = None
|
|
||||||
doc = simple_dictlist2xml(data, doc=doc, tagname=tagname)
|
|
||||||
if force_outer_xml_tag:
|
|
||||||
doc._pop()
|
|
||||||
if REQUEST:
|
if REQUEST:
|
||||||
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
|
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
|
||||||
return repr(doc)
|
return repr(doc)
|
||||||
|
121
app/scodoc/sco_vdi.py
Normal file
121
app/scodoc/sco_vdi.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gestion scolarite IUT
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""Classe stockant le VDI avec le code étape (noms de fichiers maquettes et code semestres)
|
||||||
|
"""
|
||||||
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
|
|
||||||
|
|
||||||
|
class ApoEtapeVDI(object):
|
||||||
|
_ETAPE_VDI_SEP = "!"
|
||||||
|
|
||||||
|
def __init__(self, etape_vdi=None, etape="", vdi=""):
|
||||||
|
"""Build from string representation, e.g. 'V1RT!111'"""
|
||||||
|
if etape_vdi:
|
||||||
|
self.etape_vdi = etape_vdi
|
||||||
|
self.etape, self.vdi = self.split_etape_vdi(etape_vdi)
|
||||||
|
elif etape:
|
||||||
|
if self._ETAPE_VDI_SEP in etape:
|
||||||
|
raise ScoValueError("valeur code etape invalide")
|
||||||
|
self.etape, self.vdi = etape, vdi
|
||||||
|
self.etape_vdi = self.concat_etape_vdi(etape, vdi)
|
||||||
|
else:
|
||||||
|
self.etape_vdi, self.etape, self.vdi = "", "", ""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.__class__.__name__ + "('" + str(self) + "')"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.etape_vdi
|
||||||
|
|
||||||
|
def _cmp(self, other):
|
||||||
|
"""Test égalité de deux codes étapes.
|
||||||
|
Si le VDI des deux est spécifié, on l'utilise. Sinon, seul le code étape est pris en compte.
|
||||||
|
Donc V1RT == V1RT!111, V1RT!110 == V1RT, V1RT!77 != V1RT!78, ...
|
||||||
|
|
||||||
|
Compare the two objects x (=self) and y and return an integer according to
|
||||||
|
the outcome. The return value is negative if x < y, zero if x == y
|
||||||
|
and strictly positive if x > y.
|
||||||
|
"""
|
||||||
|
if other is None:
|
||||||
|
return -1
|
||||||
|
if type(other) == str:
|
||||||
|
other = ApoEtapeVDI(other)
|
||||||
|
|
||||||
|
if self.vdi and other.vdi:
|
||||||
|
x = (self.etape, self.vdi)
|
||||||
|
y = (other.etape, other.vdi)
|
||||||
|
else:
|
||||||
|
x = self.etape
|
||||||
|
y = other.etape
|
||||||
|
|
||||||
|
return (x > y) - (x < y)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self._cmp(other) == 0
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self._cmp(other) != 0
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self._cmp(other) < 0
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
return self._cmp(other) <= 0
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
return self._cmp(other) > 0
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
return self._cmp(other) >= 0
|
||||||
|
|
||||||
|
def split_etape_vdi(self, etape_vdi):
|
||||||
|
"""Etape Apogee can be stored as 'V1RT' or, including the VDI version,
|
||||||
|
as 'V1RT!111'
|
||||||
|
Returns etape, VDI
|
||||||
|
"""
|
||||||
|
if etape_vdi:
|
||||||
|
t = etape_vdi.split(self._ETAPE_VDI_SEP)
|
||||||
|
if len(t) == 1:
|
||||||
|
etape = etape_vdi
|
||||||
|
vdi = ""
|
||||||
|
elif len(t) == 2:
|
||||||
|
etape, vdi = t
|
||||||
|
else:
|
||||||
|
raise ValueError("invalid code etape")
|
||||||
|
return etape, vdi
|
||||||
|
else:
|
||||||
|
return etape_vdi, ""
|
||||||
|
|
||||||
|
def concat_etape_vdi(self, etape, vdi=""):
|
||||||
|
if vdi:
|
||||||
|
return self._ETAPE_VDI_SEP.join([etape, vdi])
|
||||||
|
else:
|
||||||
|
return etape
|
||||||
|
|
||||||
|
|
||||||
|
# [ ApoEtapeVDI('V1RT!111'), ApoEtapeVDI('V1RT!112'), ApoEtapeVDI('VCRT'), ApoEtapeVDI('V1RT') ]
|
95
app/scodoc/sco_xml.py
Normal file
95
app/scodoc/sco_xml.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gestion scolarite IUT
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
""" Exports XML
|
||||||
|
"""
|
||||||
|
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
import xml.sax.saxutils
|
||||||
|
|
||||||
|
from app.scodoc.sco_vdi import ApoEtapeVDI
|
||||||
|
|
||||||
|
XML_HEADER = """<?xml version="1.0" encoding="utf-8"?>\n"""
|
||||||
|
|
||||||
|
|
||||||
|
def quote_xml_attr(data):
|
||||||
|
"""Escape &, <, >, quotes and double quotes"""
|
||||||
|
return xml.sax.saxutils.escape(str(data), {"'": "'", '"': """})
|
||||||
|
|
||||||
|
|
||||||
|
# ScoDoc7 legacy function:
|
||||||
|
def simple_dictlist2xml(dictlist, doc=None, tagname=None, quote=False):
|
||||||
|
"""Represent a dict as XML data.
|
||||||
|
All keys with string or numeric values are attributes (numbers converted to strings).
|
||||||
|
All list values converted to list of childs (recursively).
|
||||||
|
*** all other values are ignored ! ***
|
||||||
|
Values (xml entities) are not quoted, except if requested by quote argument.
|
||||||
|
|
||||||
|
Exemple:
|
||||||
|
simple_dictlist2xml([ { 'id' : 1, 'ues' : [{'note':10},{}] } ], tagname='infos')
|
||||||
|
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<infos id="1">
|
||||||
|
<ues note="10" />
|
||||||
|
<ues />
|
||||||
|
</infos>
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not tagname:
|
||||||
|
raise ValueError("invalid empty tagname !")
|
||||||
|
elements = _dictlist2xml(dictlist, root=[], tagname=tagname, quote=quote)
|
||||||
|
return XML_HEADER + "\n".join([ElementTree.tostring(x) for x in elements])
|
||||||
|
|
||||||
|
|
||||||
|
def _dictlist2xml(dictlist, root=None, tagname=None, quote=False):
|
||||||
|
scalar_types = (bytes, str, int, float)
|
||||||
|
for d in dictlist:
|
||||||
|
elem = ElementTree.Element(tagname)
|
||||||
|
root.append(elem)
|
||||||
|
if isinstance(d, scalar_types) or isinstance(d, ApoEtapeVDI):
|
||||||
|
elem.set("code", str(d))
|
||||||
|
else:
|
||||||
|
if quote:
|
||||||
|
d_scalar = dict(
|
||||||
|
[
|
||||||
|
(k, quote_xml_attr(v))
|
||||||
|
for (k, v) in d.items()
|
||||||
|
if isinstance(v, scalar_types)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
d_scalar = dict(
|
||||||
|
[(k, str(v)) for (k, v) in d.items() if isinstance(v, scalar_types)]
|
||||||
|
)
|
||||||
|
for k in d_scalar:
|
||||||
|
elem.set(k, d_scalar[k])
|
||||||
|
d_list = dict([(k, v) for (k, v) in d.items() if isinstance(v, list)])
|
||||||
|
if d_list:
|
||||||
|
for (k, v) in d_list.items():
|
||||||
|
_dictlist2xml(v, tagname=k, root=elem, quote=quote)
|
||||||
|
return root
|
Loading…
Reference in New Issue
Block a user