...
 
Commits (4)
"""
Django settings for photodb project.
Generated by 'django-admin startproject' using Django 1.11.13.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'a0gse8(@w8*1gfzzkgvfjg925s1#kv(f#2mi-_mz$yf_!c76a7'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pose',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'photodb.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'photodb.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db', 'names.db'),
}
}
# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 7,
},
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
]
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
LANGUAGE_CODE = 'fr-fr'
TIME_ZONE = 'Europe/Paris'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
MEDIA_URL = '/photos/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'photos')
......@@ -62,6 +62,8 @@
<h3>Liens :</h3>
<ul>
<li><a href="/importeCSV">Importation depuis un fichier CSV</a></li>
<li><a href="/pourPronote">Export des photos pour Pronote</a></li>
<li><a href="/trombi">Export en trombinoscopes</a></li>
</ul>
</div>
<div id="dialog"></div>
......
......@@ -10,7 +10,57 @@ import zipfile
import xml.dom.minidom
import os
import hashlib
from photodb.settings import BASE_DIR
from copy import deepcopy
class PhotoWithTitle:
"""
Une simple classe pour une photo au format JPG et une phrase de titre
"""
def __init__(self, photo, title):
"""
Le contructeur
@param photo un chemin vers un fichier
@param title une phrase
"""
self._photo=photo
self._title=title
return
@property
def photo(self):
return self._photo
@property
def title(self):
return self._title
class Trombi:
"""
Classe pour un trombinoscope. C4est à dire un titre général et une
liste de PhotoWithTitle
"""
def __init__(self, title="", photos=[]):
"""
Le constructeur
@param title le titre général
@param photos une liste d'instances de PhotoWithTitle, ou [] par
défaut
"""
self._title=title
self._photos=photos
return
@property
def title(self):
return self._title
@property
def photos(self):
return self._photos
def hashImageFileName(f):
"""
prend un nom de fichier qui se termine par .jpg et renvoie un nom de fichier
......@@ -20,47 +70,84 @@ def hashImageFileName(f):
root=root.encode("utf-8")
return "Pictures/000000000000096000000"+hashlib.md5(root).hexdigest()[:18].upper()+ext
def pageGen(template="templates/modele0.odt", data=[], title="Joli titre"):
def pageGen(template=None, trombi=None):
"""
Crée un fichier temporaire au format ODT
@param template un fichier ODT comportant 36 places de tableau
@param data une liste contenant au plus 36 photos et phrases
@param title un titre pour la page
@param template un fichier ODT comportant des emplacements
structurés comme :
<table:table-cell><draw:image/><text:p/></table:table-cell>
et où le compte des éléments <draw:image/> représente bien le
nombre d'emplacements. Un tout premier élément <text:p/> est
prévu pour recevoir le titre.
@param trombi une instance de Trombi
@return un BytesIO contenant une page au format ODT
"""
if template is None:
template = os.path.join(BASE_DIR, "templates", "modele0.odt")
if trombi is None:
trombi=Trombi()
modele=zipfile.ZipFile(template)
manifest=modele.open("META-INF/manifest.xml")
contents=modele.open("content.xml")
zipIO=BytesIO()
result=zipfile.ZipFile(zipIO,"x")
for zinf in modele.infolist():
f=zinf.filename
if f!="content.xml":
if f not in ["META-INF/manifest.xml","content.xml"]:
result.writestr(f,modele.read(zinf))
page=xml.dom.minidom.parse(contents)
print(page.toprettyxml(indent=" "))
cells=page.getElementsByTagName("table:table-cell")
manif=xml.dom.minidom.parse(manifest)
nsuri=manif.documentElement.namespaceURI
# effacement des images initialement présentes dans manifest.xml
manif_images=[f for f in manif.getElementsByTagName("manifest:file-entry") if f.getAttribute("manifest:full-path").startswith("Pictures")]
for el in manif_images:
manif.documentElement.removeChild(el)
el.unlink()
# copie du tableau pour utilisation si plus de photos que possible
# en un seul tableau
table=page.getElementsByTagName("table:table")[0]
table0=deepcopy(table)
maxPhotos=len(page.getElementsByTagName("draw:image"))
cells=table.getElementsByTagName("table:table-cell")
title0=cells[0].getElementsByTagName("text:p")[0]
title0.firstChild.replaceWholeText(title)
title0.firstChild.replaceWholeText(trombi.title)
i=1
for photo, texte in data:
for pwt in trombi.photos:
# insertion de la photo
print("GRRR", i)
image=cells[i].getElementsByTagName("draw:image")[0]
target=hashImageFileName(photo)
target=hashImageFileName(pwt.photo)
image.setAttribute("xlink:href", target)
with open(photo,"rb") as infile:
file_entry=manif.createElement("manifest:file-entry")
file_entry.setAttribute("manifest:full-path", target)
file_entry.setAttribute("manifest:media-type", "image/jpeg")
manif.documentElement.appendChild(file_entry)
with open(pwt.photo,"rb") as infile:
result.writestr(target, infile.read())
# insertion du texte
t=cells[i].getElementsByTagName("text:s")[0].nextSibling
t.replaceWholeText(texte)
t.replaceWholeText(pwt.title)
i+=1
# limitation à 36 cases de tableau !
if i > 36:
break
# limitation aux cases du tableau !
if i > maxPhotos:
# on ajoute un tableau neuf au bout quand le précédent est plein
print("GRRR, on ajoute un tableau neuf au bout")
table1=deepcopy(table0)
theText=page.getElementsByTagName("office:text")[0]
theText.appendChild(table1)
cells=table1.getElementsByTagName("table:table-cell")
title1=cells[0].getElementsByTagName("text:p")[0]
title1.firstChild.replaceWholeText(trombi.title)
i=1
newcontents=StringIO()
page.writexml(newcontents)
newcontents.seek(0)
result.writestr("content.xml", newcontents.read())
newmanifest=StringIO()
manif.writexml(newmanifest)
newmanifest.seek(0)
result.writestr("META-INF/manifest.xml", newmanifest.read())
result.close()
zipIO.seek(0)
return zipIO
......@@ -74,7 +161,10 @@ if __name__=="__main__":
example="/home/georgesk/tex/classes/2017-2018/photodb/var-lib-photodb/photodb/photos"
_,_,fnames=next(os.walk(example))
fnames=[f for f in fnames if f.endswith('.jpg')]
data=[(os.path.join(example,f),textFrom(f)) for f in fnames[:36]]
zipIO=pageGen(data=data)
trombi=Trombi(
"Exemple",
[PhotoWithTitle(os.path.join(example,f),textFrom(f)) for f in fnames[:50]]
)
zipIO=pageGen(trombi=trombi)
with open("/tmp/montest.odt",'wb') as outfile:
outfile.write(zipIO.read())
......@@ -9,4 +9,5 @@ urlpatterns = [
url(r'^cherchePrenom$', views.cherchePrenom, name='cherchePrenom'),
url(r'^importeCSV$', views.importeCSV, name='importeCSV'),
url(r'^pourPronote$', views.pourPronote, name='pourPronote'),
url(r'^trombi$', views.trombi, name='trombi'),
]
......@@ -400,3 +400,10 @@ def pourPronote(request):
"currentexport": currentexport,
})
def trombi(request):
"""
Export de trombinoscopes sous forme de fichiers ODT ou HTML
autonomes
"""
return render(request,"pose/trombi.html",{
})