Browse Source

Migration to python 3

feature/python-3-migration
tr4ck3ur des JM2L 3 years ago
parent
commit
ad9883ae09
37 changed files with 1670 additions and 1547 deletions
  1. +2
    -2
      jm2l/__init__.py
  2. +1
    -1
      jm2l/auth.py
  3. +127
    -113
      jm2l/badge.py
  4. +4
    -1
      jm2l/captcha.py
  5. +1
    -1
      jm2l/const.py
  6. +42
    -39
      jm2l/helpers.py
  7. +390
    -363
      jm2l/models.py
  8. +12
    -13
      jm2l/scripts/initializedb.py
  9. +1
    -1
      jm2l/templates/Errors/403.mako
  10. +1
    -1
      jm2l/templates/Errors/404.mako
  11. +12
    -12
      jm2l/templates/Interventions/Interventions.mako
  12. +1
    -1
      jm2l/templates/Logistique/Dialog_Covoit.mako
  13. +1
    -1
      jm2l/templates/Logistique/Dialog_Heberg.mako
  14. +2
    -2
      jm2l/templates/Logistique/Dialog_Matos.mako
  15. +17
    -17
      jm2l/templates/Logistique/Logistique.mako
  16. +4
    -4
      jm2l/templates/Logistique/Tables.mako
  17. +4
    -4
      jm2l/templates/Profil/Profil.mako
  18. +13
    -13
      jm2l/templates/Profil/Sejour.mako
  19. +1
    -1
      jm2l/templates/Public/Programme.mako
  20. +2
    -2
      jm2l/templates/Salles/list_phy.mako
  21. +2
    -2
      jm2l/templates/Salles/salle.mako
  22. +1
    -1
      jm2l/templates/Staff/compta.mako
  23. +1
    -1
      jm2l/templates/Staff/list.mako
  24. +3
    -3
      jm2l/templates/edit_event.mako
  25. +14
    -14
      jm2l/templates/helpers.mako
  26. +7
    -7
      jm2l/templates/jm2l.mako
  27. +16
    -16
      jm2l/templates/layout.mako
  28. +3
    -3
      jm2l/templates/mail_html.mako
  29. +3
    -3
      jm2l/templates/mail_plain.mako
  30. +1
    -1
      jm2l/templates/show_user.mako
  31. +2
    -2
      jm2l/templates/view_event.mako
  32. +1
    -1
      jm2l/templates/view_tiers.mako
  33. +1
    -1
      jm2l/templates/view_user.mako
  34. +4
    -1
      jm2l/to_print.py
  35. +172
    -168
      jm2l/upload.py
  36. +798
    -729
      jm2l/views.py
  37. +3
    -2
      setup.py

+ 2
- 2
jm2l/__init__.py View File

@@ -19,7 +19,7 @@ from pyramid.request import Request
from mako.template import Template from mako.template import Template
from .models import User from .models import User
from jm2l.const import CurrentYear from jm2l.const import CurrentYear
from models import JM2L_Year
from .models import JM2L_Year


import logging import logging


@@ -80,7 +80,7 @@ def main(global_config, **settings):
authorization_policy=authorization_policy authorization_policy=authorization_policy
) )
config.add_subscriber(add_renderer_globals, BeforeRender) config.add_subscriber(add_renderer_globals, BeforeRender)
config.registry['mailer'] = mailer_factory_from_settings(settings)
# config.registry['mailer'] = mailer_factory_from_settings(settings)
config.registry['event_date'] = JM2L_Year.get_latest_jm2l_startdate() config.registry['event_date'] = JM2L_Year.get_latest_jm2l_startdate()
sched = BackgroundScheduler() sched = BackgroundScheduler()
sched.add_job(mailer_tasks, 'cron', day_of_week='fri', hour=18, args=[ config ]) sched.add_job(mailer_tasks, 'cron', day_of_week='fri', hour=18, args=[ config ])


+ 1
- 1
jm2l/auth.py View File

@@ -8,7 +8,7 @@ from pyramid_mailer import get_mailer
from pyramid_mailer.message import Attachment, Message from pyramid_mailer.message import Attachment, Message
from .forms import UserPasswordForm from .forms import UserPasswordForm
from passlib.hash import argon2 from passlib.hash import argon2
from security import check_logged
from .security import check_logged
import datetime import datetime
import re import re




+ 127
- 113
jm2l/badge.py View File

@@ -1,7 +1,11 @@
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden
from pyramid.response import Response from pyramid.response import Response
import cStringIO as StringIO

try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from pyramid.view import view_config from pyramid.view import view_config
from .models import DBSession, User from .models import DBSession, User
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
@@ -12,78 +16,86 @@ import qrcode
import subprocess import subprocess
from .upload import MediaPath from .upload import MediaPath
from jm2l.const import CurrentYear from jm2l.const import CurrentYear

# Create PDF container # Create PDF container
EXPIRATION_TIME = 300 # seconds
EXPIRATION_TIME = 300 # seconds
WIDTH = 85 * mm WIDTH = 85 * mm
HEIGHT = 60 * mm HEIGHT = 60 * mm
ICONSIZE = 10 * mm ICONSIZE = 10 * mm


def JM2L_Logo(canvas, Offset=(0,0)):

def JM2L_Logo(canvas, Offset=(0, 0)):
OffX, OffY = Offset OffX, OffY = Offset
logoobject = canvas.beginText() logoobject = canvas.beginText()
logoobject.setFont('Logo', 32) logoobject.setFont('Logo', 32)
logoobject.setFillColorRGB(.83,0,.33)
logoobject.setTextOrigin(OffX+5, OffY+17)
logoobject.setFillColorRGB(.83, 0, .33)
logoobject.setTextOrigin(OffX + 5, OffY + 17)
logoobject.textLines("JM2L") logoobject.textLines("JM2L")
canvas.drawText(logoobject) canvas.drawText(logoobject)
yearobject = canvas.beginText() yearobject = canvas.beginText()
yearobject.setFont("Helvetica-Bold", 10) yearobject.setFont("Helvetica-Bold", 10)
yearobject.setFillColorRGB(1,1,1)
yearobject.setFillColorRGB(1, 1, 1)
yearobject.setTextRenderMode(0) yearobject.setTextRenderMode(0)
yearobject.setTextOrigin(OffX+12 , OffY+35)
yearobject.setTextOrigin(OffX + 12, OffY + 35)
yearobject.setWordSpace(13) yearobject.setWordSpace(13)
yearobject.textLines(" ".join(str(CurrentYear))) yearobject.textLines(" ".join(str(CurrentYear)))
canvas.drawText(yearobject) canvas.drawText(yearobject)


def Tiers_Logo(canvas, DispUser, StartPos=None, Offset=(0,0)):

def Tiers_Logo(canvas, DispUser, StartPos=None, Offset=(0, 0)):
Border = 0 Border = 0
OffX, OffY = Offset OffX, OffY = Offset
if StartPos is None: if StartPos is None:
StartPos = ( 30 * mm, 2 )
StartPos = (30 * mm, 2)
StartX, StartY = StartPos StartX, StartY = StartPos
MaxX, MaxY = 34*mm, 18*mm
MaxX, MaxY = 34 * mm, 18 * mm
num = 0 num = 0
canvas.setStrokeColorRGB(0.5,0.5,0.5)
Logos = filter(lambda x:x.ThumbLinks, DispUser.tiers)[:3]
canvas.setStrokeColorRGB(0.5, 0.5, 0.5)
Logos = list()
for thumb in DispUser.tiers:
if thumb.ThumbLinks:
Logos.append(thumb.ThumbLinks[:3])
# Logos = list(filter(lambda x: x.ThumbLinks, DispUser.tiers)[:3])
# Should We compute a better positionning for logos ? # Should We compute a better positionning for logos ?
DicPos = {} DicPos = {}
DicPos[1] = { 0:(1./2, 1./2) }
DicPos[2] = { 0:(1./3, 1./2), 1:(2./3, 1./2) }
DicPos[3] = { 0:(1./2, 1./4), 1:(1./3, 3./4), 2:(2./3, 3./4) }
DicPos[4] = { 0:(1./3, 1./4), 1:(2./3, 1./4), 2:(1./3, 3./4),
3:(2./3, 3./4) }
DicPos[5] = { 0:(1./3, 1./4), 1:(2./3, 1./4), 2:(1./6, 3./4),
3:(3./6, 3./4), 4:(5./6, 3./4) }
DicPos[6] = { 0:(1./6, 1./4), 1:(3./6, 1./4), 2:(5./6, 1./4),
3:(1./6, 3./4), 4:(3./6, 3./4), 5:(5./6, 3./4) }
DicPos[7] = { 0:(1./6, 1./4), 1:(3./6, 1./4), 2:(5./6, 1./4),
3:(1./8, 3./4), 4:(3./8, 3./4), 5:(5./8, 3./4),
6:(7./8, 3./4) }
DicPos[8] = { 0:(1./8, 1./4), 1:(3./8, 1./4), 2:(5./8, 1./4),
3:(7./8, 1./4), 4:(1./8, 3./4), 5:(3./8, 3./4),
6:(5./8, 3./4), 7:(7./8, 3./4) }
DicPos[1] = {0: (1. / 2, 1. / 2)}
DicPos[2] = {0: (1. / 3, 1. / 2), 1: (2. / 3, 1. / 2)}
DicPos[3] = {0: (1. / 2, 1. / 4), 1: (1. / 3, 3. / 4), 2: (2. / 3, 3. / 4)}
DicPos[4] = {0: (1. / 3, 1. / 4), 1: (2. / 3, 1. / 4), 2: (1. / 3, 3. / 4),
3: (2. / 3, 3. / 4)}
DicPos[5] = {0: (1. / 3, 1. / 4), 1: (2. / 3, 1. / 4), 2: (1. / 6, 3. / 4),
3: (3. / 6, 3. / 4), 4: (5. / 6, 3. / 4)}
DicPos[6] = {0: (1. / 6, 1. / 4), 1: (3. / 6, 1. / 4), 2: (5. / 6, 1. / 4),
3: (1. / 6, 3. / 4), 4: (3. / 6, 3. / 4), 5: (5. / 6, 3. / 4)}
DicPos[7] = {0: (1. / 6, 1. / 4), 1: (3. / 6, 1. / 4), 2: (5. / 6, 1. / 4),
3: (1. / 8, 3. / 4), 4: (3. / 8, 3. / 4), 5: (5. / 8, 3. / 4),
6: (7. / 8, 3. / 4)}
DicPos[8] = {0: (1. / 8, 1. / 4), 1: (3. / 8, 1. / 4), 2: (5. / 8, 1. / 4),
3: (7. / 8, 1. / 4), 4: (1. / 8, 3. / 4), 5: (3. / 8, 3. / 4),
6: (5. / 8, 3. / 4), 7: (7. / 8, 3. / 4)}
# draw overall border # draw overall border
# canvas.roundRect(StartX, StartY, MaxX, MaxY, radius=2, stroke=True) # canvas.roundRect(StartX, StartY, MaxX, MaxY, radius=2, stroke=True)
for tiers in Logos: for tiers in Logos:
FileName = tiers.ThumbLinks.pop().split("/")[-1] FileName = tiers.ThumbLinks.pop().split("/")[-1]
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName)
PosX = OffX+StartX + DicPos[len(Logos)][num][0] * MaxX - (ICONSIZE+Border)/2
PosY = OffY+StartY + DicPos[len(Logos)][num][1] * MaxY - (ICONSIZE+Border)/2
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName)
PosX = OffX + StartX + DicPos[len(Logos)][num][0] * MaxX - (ICONSIZE + Border) / 2
PosY = OffY + StartY + DicPos[len(Logos)][num][1] * MaxY - (ICONSIZE + Border) / 2
canvas.setLineWidth(.1) canvas.setLineWidth(.1)
if len(Logos)>1:
if len(Logos) > 1:
size = ICONSIZE size = ICONSIZE
else: else:
size = ICONSIZE*1.5
canvas.drawImage(ImagePath,
PosX, PosY, size, size,\
preserveAspectRatio=True,
anchor='c',
mask='auto'
)
size = ICONSIZE * 1.5
canvas.drawImage(ImagePath,
PosX, PosY, size, size,
preserveAspectRatio=True,
anchor='c',
mask='auto'
)
# draw icon border # draw icon border
# canvas.roundRect(PosX, PosY, ICONSIZE, ICONSIZE, radius=2, stroke=True) # canvas.roundRect(PosX, PosY, ICONSIZE, ICONSIZE, radius=2, stroke=True)
num+=1
num += 1



def QRCode(DispUser): def QRCode(DispUser):
qr = qrcode.QRCode( qr = qrcode.QRCode(
@@ -98,78 +110,79 @@ def QRCode(DispUser):


return qr.make_image() return qr.make_image()


def one_badge(c, DispUser, Offset=(0,0)):

def one_badge(c, DispUser, Offset=(0, 0)):
# Logo on Top # Logo on Top
JM2L_Logo(c, Offset) JM2L_Logo(c, Offset)
OffX, OffY = Offset OffX, OffY = Offset
c.rect(OffX-3, OffY-3, WIDTH+6, HEIGHT+6, fill=0, stroke=1)
c.rect(OffX - 3, OffY - 3, WIDTH + 6, HEIGHT + 6, fill=0, stroke=1)
if DispUser.Staff: if DispUser.Staff:
# Staff # Staff
c.setFillColorRGB(.83,0,.33)
c.rect(OffX-3, OffY+HEIGHT-30, WIDTH+6, 33, fill=1, stroke=0)
c.setFillColorRGB(1,1,1)
c.setFillColorRGB(.83, 0, .33)
c.rect(OffX - 3, OffY + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0)
c.setFillColorRGB(1, 1, 1)
c.setFont('Liberation', 30) c.setFont('Liberation', 30)
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT-24, "STAFF")
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT - 24, "STAFF")
elif DispUser.is_Intervenant: elif DispUser.is_Intervenant:
# Intervenant # Intervenant
c.setFillColorRGB(.21,.67,.78)
c.rect(OffX-3, OffY+HEIGHT-30, WIDTH+6, 33, fill=1, stroke=0)
c.setFillColorRGB(1,1,1)
c.setFillColorRGB(.21, .67, .78)
c.rect(OffX - 3, OffY + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0)
c.setFillColorRGB(1, 1, 1)
c.setFont('Liberation', 30) c.setFont('Liberation', 30)
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT-24, "Intervenant")
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT - 24, "Intervenant")
elif DispUser.is_crew: elif DispUser.is_crew:
# Benevole # Benevole
c.setFillColorRGB(.18,.76,.23)
c.rect(OffX-3, OffY+HEIGHT-30, WIDTH+6, 33, fill=1, stroke=0)
c.setFillColorRGB(1,1,1)
c.setFillColorRGB(.18, .76, .23)
c.rect(OffX - 3, OffY + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0)
c.setFillColorRGB(1, 1, 1)
c.setFont('Liberation', 30) c.setFont('Liberation', 30)
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT-24, "Bénévole")
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT - 24, "Bénévole")
else: else:
# Visiteur # Visiteur
c.setFillColorRGB(.8,.8,.8)
c.rect(OffX-3, OffY+HEIGHT-30, WIDTH+6, 33, fill=1, stroke=0)
c.setFillColorRGB(1,1,1)
c.setFillColorRGB(.8, .8, .8)
c.rect(OffX - 3, OffY + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0)
c.setFillColorRGB(1, 1, 1)
c.setFont('Liberation', 30) c.setFont('Liberation', 30)
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT-24, "Visiteur")
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT - 24, "Visiteur")


c.restoreState() c.restoreState()
c.setFont('Liberation', 18) c.setFont('Liberation', 18)
c.setStrokeColorRGB(0,0,0)
c.setFillColorRGB(0,0,0)
c.setStrokeColorRGB(0, 0, 0)
c.setFillColorRGB(0, 0, 0)
# Feed Name and SurName # Feed Name and SurName
if DispUser.prenom and DispUser.nom and len(DispUser.prenom) + len(DispUser.nom)>18:
if DispUser.prenom and DispUser.nom and len(DispUser.prenom) + len(DispUser.nom) > 18:
if DispUser.pseudo: if DispUser.pseudo:
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT/2 + 0 * mm , "%s" % DispUser.prenom )
#c.setFont('Courier', 17)
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT/2 - 8 * mm , "%s" % DispUser.nom )
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 + 0 * mm, "%s" % DispUser.prenom)
# c.setFont('Courier', 17)
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 - 8 * mm, "%s" % DispUser.nom)
else: else:
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT/2 + 4 * mm , "%s" % DispUser.prenom )
#c.setFont('Courier', 17)
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT/2 - 8 * mm , "%s" % DispUser.nom )
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 + 4 * mm, "%s" % DispUser.prenom)
# c.setFont('Courier', 17)
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 - 8 * mm, "%s" % DispUser.nom)
else: else:
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT/2 + 0 * mm , "%s %s" % (DispUser.prenom, DispUser.nom) )
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 + 0 * mm, "%s %s" % (DispUser.prenom, DispUser.nom))


if DispUser.pseudo: if DispUser.pseudo:
c.setFont("Helvetica-Oblique", 18) c.setFont("Helvetica-Oblique", 18)
c.drawCentredString(OffX+WIDTH/2, OffY+HEIGHT/2 + 10 * mm , "%s" % DispUser.pseudo )
# Put QR code to user profile
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 + 10 * mm, "%s" % DispUser.pseudo)
#  Put QR code to user profile
c.drawInlineImage(QRCode(DispUser), \ c.drawInlineImage(QRCode(DispUser), \
OffX+WIDTH - 20 * mm -5, OffY+5, \
20 * mm, 20 * mm, \
preserveAspectRatio=True, \
anchor='s')
Tiers_Logo(c, DispUser, None, Offset)
@view_config(route_name='badge_user', http_cache = (EXPIRATION_TIME, {'public':True}))
OffX + WIDTH - 20 * mm - 5, OffY + 5, \
20 * mm, 20 * mm, \
preserveAspectRatio=True, \
anchor='s')
Tiers_Logo(c, DispUser, None, Offset)
@view_config(route_name='badge_user') # , http_cache=(EXPIRATION_TIME, {'public': True}))
def badge_user(request): def badge_user(request):
isoutpng = request.params.get('png') isoutpng = request.params.get('png')
user_slug = request.matchdict.get('user_slug', None) user_slug = request.matchdict.get('user_slug', None)
if user_slug is None or len(user_slug)==0:
if user_slug is None or len(user_slug) == 0:
raise HTTPNotFound(u"Cet utilisateur n'a pas été reconnu") raise HTTPNotFound(u"Cet utilisateur n'a pas été reconnu")
# Query database # Query database
DispUser = User.by_slug(user_slug) DispUser = User.by_slug(user_slug)
@@ -177,48 +190,49 @@ def badge_user(request):
raise HTTPNotFound() raise HTTPNotFound()


# Ok let's generate a PDF Badge # Ok let's generate a PDF Badge
# Register LiberationMono font # Register LiberationMono font
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf"
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) pdfmetrics.registerFont(TTFont("Liberation", ttfFile))
# Import font
#  Import font
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf"
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo))


pdf = StringIO.StringIO()
out_img = StringIO.StringIO()
c = canvas.Canvas( pdf, pagesize=(WIDTH, HEIGHT) )
pdf = StringIO()
out_img = StringIO()
c = canvas.Canvas(pdf, pagesize=(WIDTH, HEIGHT))
c.translate(mm, mm) c.translate(mm, mm)
# Feed some metadata # Feed some metadata
c.setCreator("linux-azur.org") c.setCreator("linux-azur.org")
c.setTitle("Badge") c.setTitle("Badge")


c.saveState() c.saveState()
one_badge(c, DispUser)


one_badge(c, DispUser)
OutPDF = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.pdf')
c.showPage() c.showPage()
c._filename = OutPDF
c.save() c.save()
pdf.seek(0) pdf.seek(0)
if isoutpng: if isoutpng:
OutPDF = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.pdf')
OutPNG = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.png') OutPNG = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.png')
# Let's generate a png file for website
with open( OutPDF ,'wb') as pdff:
pdff.write(pdf.read())
Command = ["convert","-density","150x150", OutPDF, OutPNG]
#  Let's generate a png file for website
with open("./%s" % OutPDF, 'wb') as pdff:
pdff.write(bytes(pdf.read(), 'utf8')) # .encode('utf8'))
Command = ["convert", "-density", "150x150", OutPDF, OutPNG]
subprocess.call(Command) subprocess.call(Command)
with open( OutPNG, 'rb') as pngfile:
out_img.write(pngfile.read())
with open("./%s" % OutPNG, 'r') as pngfile:
out_img.write(bytes(pngfile.read(), 'utf8')) # bytes(pngfile.read(), "utf8"))
out_img.seek(0) out_img.seek(0)
return Response(app_iter=out_img, content_type = 'image/png' )
return Response(app_iter=out_img, content_type='image/png')
else: else:
return Response(app_iter=pdf, content_type = 'application/pdf' )
return Response(app_iter=pdf, content_type='application/pdf')




@view_config(route_name='all_badges') @view_config(route_name='all_badges')
@@ -234,32 +248,32 @@ def planche_badge(request):
# Register LiberationMono font # Register LiberationMono font
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf"
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) pdfmetrics.registerFont(TTFont("Liberation", ttfFile))
# Import font
#  Import font
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf"
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo))
pdf = StringIO.StringIO() pdf = StringIO.StringIO()


FULLWIDTH = 210 * mm FULLWIDTH = 210 * mm
FULLHEIGHT = 297 * mm FULLHEIGHT = 297 * mm


c = canvas.Canvas( pdf, pagesize=(FULLWIDTH, FULLHEIGHT) )
c = canvas.Canvas(pdf, pagesize=(FULLWIDTH, FULLHEIGHT))
c.translate(mm, mm) c.translate(mm, mm)
# Feed some metadata # Feed some metadata
c.setCreator("linux-azur.org") c.setCreator("linux-azur.org")
c.setTitle("Badge") c.setTitle("Badge")
t=0
t = 0
ListUser = filter(lambda x: x.is_Intervenant or x.Staff or x.is_crew, Users) ListUser = filter(lambda x: x.is_Intervenant or x.Staff or x.is_crew, Users)
for num, DispUser in enumerate(ListUser): for num, DispUser in enumerate(ListUser):
c.saveState() c.saveState()
Offsets = (((num-t)%2)*(WIDTH+40)+40, ((num-t)/2)*(HEIGHT+25)+40)
Offsets = (((num - t) % 2) * (WIDTH + 40) + 40, ((num - t) / 2) * (HEIGHT + 25) + 40)
one_badge(c, DispUser, Offsets) one_badge(c, DispUser, Offsets)
if num%8==7:
t=num+1
if num % 8 == 7:
t = num + 1
c.showPage() c.showPage()


c.showPage() c.showPage()
c.save() c.save()
pdf.seek(0) pdf.seek(0)
return Response(app_iter=pdf, content_type = 'application/pdf' )
return Response(app_iter=pdf, content_type='application/pdf')

+ 4
- 1
jm2l/captcha.py View File

@@ -3,7 +3,10 @@


import random import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter from PIL import Image, ImageDraw, ImageFont, ImageFilter
import cStringIO as StringIO
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
import math import math
from pyramid.view import view_config from pyramid.view import view_config
from .words import TabMots from .words import TabMots


+ 1
- 1
jm2l/const.py View File

@@ -1 +1 @@
CurrentYear = 2018
CurrentYear = 2020

+ 42
- 39
jm2l/helpers.py View File

@@ -4,61 +4,63 @@ from datetime import timedelta, datetime
import itertools import itertools
from jm2l.const import CurrentYear from jm2l.const import CurrentYear



class DummySejour(object): class DummySejour(object):
def __init__(self, event): def __init__(self, event):
self.Me = event['request'].user self.Me = event['request'].user
self.CurrentEventYear = DBSession.query(JM2L_Year).filter(JM2L_Year.state=='Ongoing').first()
self.CurrentEventYear = DBSession.query(JM2L_Year).filter(JM2L_Year.state == 'Ongoing').first()
self.Sejour = None self.Sejour = None
if self.Me: if self.Me:
self.Sejour = DBSession.query(Sejour)\
.filter(Sejour.user_id==self.Me.uid)\
.filter(Sejour.for_year==self.CurrentEventYear.year_uid)\
self.Sejour = DBSession.query(Sejour) \
.filter(Sejour.user_id == self.Me.uid) \
.filter(Sejour.for_year == self.CurrentEventYear.year_uid) \
.first() .first()



class Sejour_helpers(DummySejour): class Sejour_helpers(DummySejour):


def __init__(self, event): def __init__(self, event):
super(Sejour_helpers, self).__init__(event) super(Sejour_helpers, self).__init__(event)
if self.Sejour and self.Sejour.arrival_time is None: if self.Sejour and self.Sejour.arrival_time is None:
self.Sejour = None self.Sejour = None
def StartEvent(self): def StartEvent(self):
# This function return the start of the event # This function return the start of the event
return self.CurrentYear return self.CurrentYear
def PossibleDate(self, typedate="arrival"): def PossibleDate(self, typedate="arrival"):
arrival, departure = False, False arrival, departure = False, False
TabResult = list() TabResult = list()
if typedate == "arrival": if typedate == "arrival":
# Let's say people should arrive until 2 day before # Let's say people should arrive until 2 day before
arrival = True arrival = True
myDayRange = xrange(2,-1,-1)
myDayRange = range(2, -1, -1)
elif typedate == "departure": elif typedate == "departure":
# Let's say people should go back home until 2 day after # Let's say people should go back home until 2 day after
departure = True
myDayRange = xrange(3)
departure = True
myDayRange = range(3)
else: else:
return TabResult return TabResult
if self.Sejour: if self.Sejour:
ArrDate = datetime.strftime(self.Sejour.arrival_time,"%d %B %Y").decode('utf-8')
DepDate = datetime.strftime(self.Sejour.depart_time,"%d %B %Y").decode('utf-8')
ArrDate = datetime.strftime(self.Sejour.arrival_time, "%d %B %Y")
DepDate = datetime.strftime(self.Sejour.depart_time, "%d %B %Y")
else: else:
ArrDate = datetime.strftime( self.CurrentEventYear.start_time,"%d %B %Y" ).decode('utf-8')
DepDate = datetime.strftime( self.CurrentEventYear.end_time,"%d %B %Y" ).decode('utf-8')
ArrDate = datetime.strftime(self.CurrentEventYear.start_time, "%d %B %Y")
DepDate = datetime.strftime(self.CurrentEventYear.end_time, "%d %B %Y")
for oneday in myDayRange: for oneday in myDayRange:
if arrival: if arrival:
TmpDay = self.CurrentEventYear.end_time - timedelta(days=oneday) TmpDay = self.CurrentEventYear.end_time - timedelta(days=oneday)
elif departure: elif departure:
TmpDay = self.CurrentEventYear.start_time + timedelta(days=oneday) TmpDay = self.CurrentEventYear.start_time + timedelta(days=oneday)
DayName = datetime.strftime(TmpDay,"%A")
DayNum = datetime.strftime(TmpDay,"%d/%m/%y")
DayString = datetime.strftime(TmpDay,"%d %B %Y").decode('utf-8')
if arrival and ArrDate==DayString:
DayName = datetime.strftime(TmpDay, "%A")
DayNum = datetime.strftime(TmpDay, "%d/%m/%y")
DayString = datetime.strftime(TmpDay, "%d %B %Y")
if arrival and ArrDate == DayString:
TabResult.append((DayNum, DayName, 'selected="selected"')) TabResult.append((DayNum, DayName, 'selected="selected"'))
elif departure and DepDate==DayString:
elif departure and DepDate == DayString:
TabResult.append((DayNum, DayName, 'selected="selected"')) TabResult.append((DayNum, DayName, 'selected="selected"'))
else: else:
TabResult.append((DayNum, DayName, "")) TabResult.append((DayNum, DayName, ""))
@@ -67,21 +69,21 @@ class Sejour_helpers(DummySejour):
def PossibleTime(self, typedate="arrival"): def PossibleTime(self, typedate="arrival"):
ArrTime, DepTime = "10:00", "19:00" ArrTime, DepTime = "10:00", "19:00"
TabResult = list() TabResult = list()
if self.Sejour: if self.Sejour:
ArrTime = datetime.strftime(self.Sejour.arrival_time,"%H:%M")
DepTime = datetime.strftime(self.Sejour.depart_time,"%H:%M")
ArrTime = datetime.strftime(self.Sejour.arrival_time, "%H:%M")
DepTime = datetime.strftime(self.Sejour.depart_time, "%H:%M")
for hour in range(24): for hour in range(24):
for minutes in range(0,60,10):
for minutes in range(0, 60, 10):
StrTime = "%.2d:%.2d" % (hour, minutes) StrTime = "%.2d:%.2d" % (hour, minutes)
DispTime = "%dh%.2d" % (hour, minutes) DispTime = "%dh%.2d" % (hour, minutes)
if typedate == "arrival" and StrTime==ArrTime:
TabResult.append( (StrTime, DispTime, 'selected="selected"') )
elif typedate == "departure" and StrTime==DepTime:
TabResult.append( (StrTime, DispTime, 'selected="selected"') )
if typedate == "arrival" and StrTime == ArrTime:
TabResult.append((StrTime, DispTime, 'selected="selected"'))
elif typedate == "departure" and StrTime == DepTime:
TabResult.append((StrTime, DispTime, 'selected="selected"'))
else: else:
TabResult.append( (StrTime, DispTime, "") )
TabResult.append((StrTime, DispTime, ""))
return TabResult return TabResult


def IsCheck(self, InputControl): def IsCheck(self, InputControl):
@@ -91,13 +93,13 @@ class Sejour_helpers(DummySejour):
return "" return ""
if self.Sejour: if self.Sejour:
if InputControl.startswith('Arrival'): if InputControl.startswith('Arrival'):
CtrlVal = 2**ListControlB.index(InputControl[8:])
CtrlVal = 2 ** ListControlB.index(InputControl[8:])
if self.Sejour.arrival_check & CtrlVal == CtrlVal: if self.Sejour.arrival_check & CtrlVal == CtrlVal:
return "checked=\"checked\"" return "checked=\"checked\""
else: else:
return "" return ""
elif InputControl.startswith('Departure'): elif InputControl.startswith('Departure'):
CtrlVal = 2**ListControlB.index(InputControl[10:])
CtrlVal = 2 ** ListControlB.index(InputControl[10:])
if self.Sejour.depart_check & CtrlVal == CtrlVal: if self.Sejour.depart_check & CtrlVal == CtrlVal:
return "checked=\"checked\"" return "checked=\"checked\""
else: else:
@@ -107,6 +109,7 @@ class Sejour_helpers(DummySejour):
else: else:
return "" return ""



class Orga_helpers(DummySejour): class Orga_helpers(DummySejour):


def __init__(self, event): def __init__(self, event):
@@ -129,10 +132,10 @@ class Orga_helpers(DummySejour):
u"Faire du café et s'assurer de sa disponibilité.", u"Faire du café et s'assurer de sa disponibilité.",
u"Participer à la publication / au montage des vidéos des conférenciers.", u"Participer à la publication / au montage des vidéos des conférenciers.",
u"Autres" u"Autres"
]
]


def IsChecked(self, nb): def IsChecked(self, nb):
nb = 2**nb
nb = 2 ** nb
if self.Sejour and self.Sejour.orga_part: if self.Sejour and self.Sejour.orga_part:
if self.Sejour.orga_part & nb == nb: if self.Sejour.orga_part & nb == nb:
return "checked=\"checked\"" return "checked=\"checked\""
@@ -140,12 +143,12 @@ class Orga_helpers(DummySejour):
return "" return ""
else: else:
return "" return ""
def ChoosedList(self): def ChoosedList(self):
""" Return choice validated by user """ """ Return choice validated by user """
ListOrga = [] ListOrga = []
for num in range(0,len(self.Orga_tasks)):
curs = 2**num
for num in range(0, len(self.Orga_tasks)):
curs = 2 ** num
if self.Sejour.orga_part & curs == curs: if self.Sejour.orga_part & curs == curs:
ListOrga.append(self.Orga_tasks[num]) ListOrga.append(self.Orga_tasks[num])
return ListOrga
return ListOrga

+ 390
- 363
jm2l/models.py
File diff suppressed because it is too large
View File


+ 12
- 13
jm2l/scripts/initializedb.py View File

@@ -48,14 +48,14 @@ def pull_data(from_db, to_db, tables):
destination, dengine = make_session(to_db) destination, dengine = make_session(to_db)


for table_name in tables: for table_name in tables:
print 'Processing', table_name
print 'Pulling schema from source server'
print('Processing', table_name)
print('Pulling schema from source server')
table = Table(table_name, smeta, autoload=True) table = Table(table_name, smeta, autoload=True)
print 'Creating table on destination server'
print('Creating table on destination server')
table.metadata.create_all(dengine) table.metadata.create_all(dengine)
NewRecord = quick_mapper(table) NewRecord = quick_mapper(table)
columns = table.columns.keys() columns = table.columns.keys()
print 'Transferring records'
print('Transferring records')
for record in source.query(table).all(): for record in source.query(table).all():
data = dict( data = dict(
[(str(column), getattr(record, column)) for column in columns] [(str(column), getattr(record, column)) for column in columns]
@@ -69,20 +69,20 @@ def pull_data(from_db, to_db, tables):
try: try:
destination.merge(NewRecord(**data)) destination.merge(NewRecord(**data))
except: except:
print data
print(data)
pass pass
print 'Committing changes'
print('Committing changes')
destination.commit() destination.commit()




def main(argv=sys.argv): def main(argv=sys.argv):
connection_string = "sqlite:////home/tr4ck3ur/Dev/jm2l/JM2L.sqlite"
connection_string = "sqlite:////home/tr4ck3ur/git_repository/jm2l/JM2L.sqlite"
engine = create_engine(connection_string, echo=False, convert_unicode=True) engine = create_engine(connection_string, echo=False, convert_unicode=True)
DBSession.configure(bind=engine) DBSession.configure(bind=engine)
Users = DBSession.query(User) Users = DBSession.query(User)
ListUser = filter(lambda x: x.is_Intervenant, Users) ListUser = filter(lambda x: x.is_Intervenant, Users)
for i in ListUser: for i in ListUser:
print i.mail
print(i.mail)


def main4(argv=sys.argv): def main4(argv=sys.argv):
import csv import csv
@@ -116,7 +116,7 @@ def main4(argv=sys.argv):
u.wifi_user = w_user u.wifi_user = w_user
u.wifi_pass = w_pass u.wifi_pass = w_pass
DBSession.merge(u) DBSession.merge(u)
print row, u
print(row, u)
finally: finally:
f.close() f.close()
@@ -130,7 +130,7 @@ def main_3(argv=sys.argv):
connection_string = "sqlite:////home/tr4ck3ur/Dev/jm2l/JM2L.sqlite" connection_string = "sqlite:////home/tr4ck3ur/Dev/jm2l/JM2L.sqlite"
engine = create_engine(connection_string, echo=True, convert_unicode=True) engine = create_engine(connection_string, echo=True, convert_unicode=True)
DBSession.configure(bind=engine) DBSession.configure(bind=engine)
p0, p1 = orm.aliased(User,name="p0"), orm.aliased(User ,name="p1")
p0, p1 = orm.aliased(User, name="p0"), orm.aliased(User , name="p1")
import pprint import pprint
## permtation ## permtation
@@ -141,7 +141,7 @@ def main_3(argv=sys.argv):
.filter(p0.last_logged<p1.last_logged)\ .filter(p0.last_logged<p1.last_logged)\
.with_entities(p0.slug,p0.uid,p1.uid).all() .with_entities(p0.slug,p0.uid,p1.uid).all()
for slug, idsrc, iddst in Datas: for slug, idsrc, iddst in Datas:
print slug
print(slug)
# Events # Events
Events = DBSession.query(User_Event)\ Events = DBSession.query(User_Event)\
.filter(User_Event.user_uid==idsrc) .filter(User_Event.user_uid==idsrc)
@@ -238,5 +238,4 @@ def Initialize():
u.password = password u.password = password
u.Staff = 0 u.Staff = 0
DBSession.merge(u) DBSession.merge(u)
print u.nom, u.prenom, u.Staff
print(u.nom, u.prenom, u.Staff)

+ 1
- 1
jm2l/templates/Errors/403.mako View File

@@ -58,7 +58,7 @@
% if reason: % if reason:
<p>${reason}</p> <p>${reason}</p>
% else: % else:
<p>Vous n'êtes pas authentifi&eacute;, ou n'avez pas les authorisations n&eacute;cessaires.</p>
<p>Vous n'êtes pas authentifié, ou n'avez pas les authorisations nécessaires.</p>
% endif % endif
</body> </body>
</html> </html>


+ 1
- 1
jm2l/templates/Errors/404.mako View File

@@ -54,7 +54,7 @@
</head> </head>
<body> <body>
<img src="/img/error404.png" width="200px" /> <img src="/img/error404.png" width="200px" />
<h1>Page non trouv&eacute;e</h1>
<h1>Page non trouvée</h1>
% if reason: % if reason:
<p>${reason}</p> <p>${reason}</p>
% else: % else:


+ 12
- 12
jm2l/templates/Interventions/Interventions.mako View File

@@ -10,7 +10,7 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>Conf&eacute;rences</td> <td style="text-align:center">
<td>Conférences</td> <td style="text-align:center">
% if len( request.user.year_events('Conference') ): % if len( request.user.year_events('Conference') ):
% for evt in request.user.year_events('Conference'): % for evt in request.user.year_events('Conference'):
% endfor % endfor
@@ -106,11 +106,11 @@ elif Type=='T':
%> %>
% if Type!='O': % if Type!='O':
<fieldset> <fieldset>
<legend class="lowshadow">Vos ${CurTitles} programm&eacute;s pour ${CurrentYear}</legend>
<legend class="lowshadow">Vos ${CurTitles} programmés pour ${CurrentYear}</legend>
<% <%
Selection = filter(lambda x:(x.event_type==CurEventType and x.for_year==CurrentYear), uprofil.events)
Selection = list(filter(lambda x:(x.event_type==CurEventType and x.for_year==CurrentYear), uprofil.events))
HeadHistTitle = u"L'historique de vos %s ( %d ) " % ( CurTitles, len(Selection) ) HeadHistTitle = u"L'historique de vos %s ( %d ) " % ( CurTitles, len(Selection) )
NothingTitle = u"Vous n'avez pas sollicit&eacute; d'intervention %s." % CurEvent
NothingTitle = u"Vous n'avez pas sollicité d'intervention %s." % CurEvent
%> %>
${helpers.show_Interventions(Selection, "Sujet", NothingTitle )} ${helpers.show_Interventions(Selection, "Sujet", NothingTitle )}
</fieldset> </fieldset>
@@ -118,21 +118,21 @@ NothingTitle = u"Vous n'avez pas sollicit&eacute; d'intervention %s." % CurEvent


% if Type=='C': % if Type=='C':
<p> <p>
<strong>Proposer une conf&eacute;rence / un lighting talk</strong><br/>
<strong>Proposer une conférence / un lighting talk</strong><br/>
<ul> <ul>
<li>Si vous avez une exp&eacute;rience particulière avec les logiciels libres
<li>Si vous avez une expérience particulière avec les logiciels libres
que vous souhaitez partager.</li> que vous souhaitez partager.</li>
<li>Si vous êtes acteur d’un des sujets actuels qui menacent ou qui <li>Si vous êtes acteur d’un des sujets actuels qui menacent ou qui
promeuvent le logiciel libre.</li> promeuvent le logiciel libre.</li>
<li>Si vous voulez pr&eacute;senter un logiciel libre dont vous êtes l’auteur.</li>
<li>Si vous voulez présenter un logiciel libre dont vous êtes l’auteur.</li>
</ul> </ul>
Nous serons heureux de vous &eacute;couter.
Nous serons heureux de vous écouter.
<br> <br>
Nous souhaitons proposer des conf&eacute;rences pour un public d&eacute;butant
Nous souhaitons proposer des conférences pour un public débutant
autant que pour des visiteurs avertis. Les sujets ne doivent pas autant que pour des visiteurs avertis. Les sujets ne doivent pas
forc&eacute;ment être techniques, mais aussi d’ordre g&eacute;n&eacute;ral avec la seule
forcément être techniques, mais aussi d’ordre général avec la seule
contrainte de traiter de près ou de loin des logiciels libres, de la contrainte de traiter de près ou de loin des logiciels libres, de la
communaut&eacute; ou de vos propres exp&eacute;riences d’utilisateur quotidien. <br>
communauté ou de vos propres exp&eacute;riences d’utilisateur quotidien. <br>
Le but de ces conf&eacute;rences est double : Le but de ces conf&eacute;rences est double :
<ul> <ul>
<li>donner confiance aux futurs utilisateurs de logiciels libres</li> <li>donner confiance aux futurs utilisateurs de logiciels libres</li>
@@ -205,7 +205,7 @@ NothingTitle = u"Vous n'avez pas sollicit&eacute; d'intervention %s." % CurEvent
<fieldset> <fieldset>
<legend class="lowshadow">Historique</legend> <legend class="lowshadow">Historique</legend>
<% <%
Selection = filter(lambda x:(x.event_type==CurEventType and x.for_year!=CurrentYear), uprofil.events)
Selection = list(filter(lambda x:(x.event_type==CurEventType and x.for_year!=CurrentYear), uprofil.events))
HeadHistTitle = u"L'historique de vos %s ( %d ) " % ( CurTitles, len(Selection) ) HeadHistTitle = u"L'historique de vos %s ( %d ) " % ( CurTitles, len(Selection) )
NothingTitle = u"D&eacute;sol&eacute;, Il n'y a rien dans l'historique vous concernant." NothingTitle = u"D&eacute;sol&eacute;, Il n'y a rien dans l'historique vous concernant."
%> %>


+ 1
- 1
jm2l/templates/Logistique/Dialog_Covoit.mako View File

@@ -30,7 +30,7 @@
</tbody> </tbody>
</table> </table>
<div class="center"> <div class="center">
Pour un co-voiturage le <u>${Exch.start_time.strftime("%a %d %b").decode('utf-8')}</u>
Pour un co-voiturage le <u>${Exch.start_time.strftime("%a %d %b")}</u>
vers <strong>${Exch.start_time.strftime("%H:%M")}</strong> vers <strong>${Exch.start_time.strftime("%H:%M")}</strong>
</div> </div>
Temps de voyage estimé à <span id="summary"></span> Temps de voyage estimé à <span id="summary"></span>


+ 1
- 1
jm2l/templates/Logistique/Dialog_Heberg.mako View File

@@ -12,7 +12,7 @@
<dd>Un hébergement</dd> <dd>Un hébergement</dd>
% endif % endif
<dt>Quand </dt> <dt>Quand </dt>
<dd>La nuit du ${Exch.start_time.strftime('%A %d %b %Y').decode('utf-8')} jusqu'au lendemain</dd>
<dd>La nuit du ${Exch.start_time.strftime('%A %d %b %Y')} jusqu'au lendemain</dd>
% if Exch.description: % if Exch.description:
<dt>Détails </dt> <dt>Détails </dt>
<dd>${Exch.description}</dd> <dd>${Exch.description}</dd>


+ 2
- 2
jm2l/templates/Logistique/Dialog_Matos.mako View File

@@ -8,8 +8,8 @@
<dt>Catégorie</dt> <dt>Catégorie</dt>
<dd>${Exch.Category.exch_subtype}</dd> <dd>${Exch.Category.exch_subtype}</dd>
<dt>Quand </dt> <dt>Quand </dt>
<dd>de ${Exch.start_time.strftime('%A %d %b %Y').decode('utf-8')} vers ${Exch.start_time.strftime('%Hh%M')}
à ${Exch.end_time.strftime('%A %d %b %Y').decode('utf-8')} vers ${Exch.end_time.strftime('%Hh%M')}
<dd>de ${Exch.start_time.strftime('%A %d %b %Y')} vers ${Exch.start_time.strftime('%Hh%M')}
à ${Exch.end_time.strftime('%A %d %b %Y')} vers ${Exch.end_time.strftime('%Hh%M')}
</dd> </dd>
<dt>Détails </dt> <dt>Détails </dt>
<dd>${Exch.description}</dd> <dd>${Exch.description}</dd>


+ 17
- 17
jm2l/templates/Logistique/Logistique.mako View File

@@ -8,11 +8,11 @@ DicExch = Exchanges.get_overview( request.user.uid )
<div class="tabbable tabs-left" id="Intendance_tab"> <div class="tabbable tabs-left" id="Intendance_tab">
<ul class="nav nav-tabs navbar" style="margin-bottom:0;background-color: #f7f7f7;"> <ul class="nav nav-tabs navbar" style="margin-bottom:0;background-color: #f7f7f7;">
<li class="active"> <a href="#ResumeInt" data-toggle="tab">Resum&eacute;</a> </li>
<li class="active"> <a href="#ResumeInt" data-toggle="tab">Resumé</a> </li>
<li> <a href="#Miam" data-toggle="tab"><span style="font-size:1.8em;">&#127869;</span> Miam</a> </li> <li> <a href="#Miam" data-toggle="tab"><span style="font-size:1.8em;">&#127869;</span> Miam</a> </li>
<li> <a href="#Covoiturage" data-toggle="tab"><span style="font-size:1.8em;">&#128664;</span> Covoiturage</a> </li> <li> <a href="#Covoiturage" data-toggle="tab"><span style="font-size:1.8em;">&#128664;</span> Covoiturage</a> </li>
<li> <a href="#Hebergement" data-toggle="tab"><span style="font-size:1.8em;">&#127962;</span> H&eacute;bergement</a> </li>
<li> <a href="#Materiel" data-toggle="tab"><span style="font-size:1.8em;">&#128722;</span> Mat&eacute;riel</a> </li>
<li> <a href="#Hebergement" data-toggle="tab"><span style="font-size:1.8em;">&#127962;</span> Hébergement</a> </li>
<li> <a href="#Materiel" data-toggle="tab"><span style="font-size:1.8em;">&#128722;</span> Matériel</a> </li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
@@ -30,7 +30,7 @@ DicExch = Exchanges.get_overview( request.user.uid )
${tables.DoTable(Type, 'Ask', DicExch)} ${tables.DoTable(Type, 'Ask', DicExch)}
${tables.DoTable(Type, 'Proposal', DicExch)} ${tables.DoTable(Type, 'Proposal', DicExch)}
<fieldset> <fieldset>
<legend>Tous les &eacute;changes</legend>
<legend>Tous les échanges</legend>
${Missing(Type, DicExch['Missing'])} ${Missing(Type, DicExch['Missing'])}
</fieldset> </fieldset>
</%def> </%def>
@@ -59,23 +59,23 @@ DicForm = {
</td> </td>
<td> <td>
<p> <p>
Compl&eacute;tez dès à pr&eacute;sent votre partie repas afin que l'on puisse faire les r&eacute;servations n&eacute;cessaires !
Complétez dès à présent votre partie repas afin que l'on puisse faire les réservations nécessaires !
</p> </p>


<u>Vendredi soir :</u> <u>Vendredi soir :</u>
<p> <p>
Certains conf&eacute;renciers viennent de très loin et seront pr&eacute;sent d&eacute;s le vendredi.<br />
Nous vous proposons de nous retrouver à proximit&eacute;, à la CASA.<br />
Certains conférenciers viennent de très loin et seront présent dés le vendredi.<br />
Nous vous proposons de nous retrouver à proximité, à la CASA.<br />
<a href="http://groupelacasa.com/la-carte-et-les-menus-1-2-75"> La carte CASA </a> <a href="http://groupelacasa.com/la-carte-et-les-menus-1-2-75"> La carte CASA </a>
le vendredi soir autour d'un verre et d'un bon repas ! le vendredi soir autour d'un verre et d'un bon repas !
</p> </p>
<u>Samedi Midi :</u> <u>Samedi Midi :</u>
<p> <p>
&Agrave; la pause du midi, nous vous proposons un repas avec le food-truck 'les frères toqu&eacute;s' qui sera pr&eacute;sent sur le parking de PolyTech<br />
&Agrave; la pause du midi, nous vous proposons un repas avec le food-truck 'les frères toqués' qui sera présent sur le parking de PolyTech<br />
</p> </p>
<u>Samedi Soir :</u> <u>Samedi Soir :</u>
<p> <p>
Pour conclure la journ&eacute;e nous avons l'habitude de nous retrouver au repas de cloture.<br />
Pour conclure la journée nous avons l'habitude de nous retrouver au repas de cloture.<br />
Nous vous proposons de nous retrouver à Antibes au restaurant Les Tonnelles<br /> Nous vous proposons de nous retrouver à Antibes au restaurant Les Tonnelles<br />
<a href="https://fr-fr.facebook.com/lestonnellesantibes/?_fb_noscript=1"> Les Tonnelles </a> <a href="https://fr-fr.facebook.com/lestonnellesantibes/?_fb_noscript=1"> Les Tonnelles </a>
</p> </p>
@@ -100,7 +100,7 @@ elif Type=='M':
<thead> <thead>
<tr> <tr>
<th colspan="5"> <th colspan="5">
Les &eacute;changes ${CurTitle}
Les échanges ${CurTitle}
% if 0: % if 0:
<span style="float:right;"> <span style="float:right;">
<a data-original-title="Afficher les demandes" data-toggle="tooltip" id="${Type}_Demande"> <a data-original-title="Afficher les demandes" data-toggle="tooltip" id="${Type}_Demande">
@@ -124,7 +124,7 @@ elif Type=='M':
<tr> <tr>
<th style="width:1em;"></th> <th style="width:1em;"></th>
<th>D&eacute;tails</th>
<th>Détails</th>
<th style="width:1em;"></th> <th style="width:1em;"></th>
<tr> <tr>
</thead> </thead>
@@ -132,7 +132,7 @@ elif Type=='M':
% if len(Selection)==0: % if len(Selection)==0:
<tr> <tr>
<td colspan="5" style="text-align:center;"> <td colspan="5" style="text-align:center;">
<i>Il n'y a aucun &eacute;change ${CurTitle} propos&eacute; actuellement...</i>
<i>Il n'y a aucun échange ${CurTitle} proposé actuellement...</i>
</td> </td>
</tr> </tr>
% else: % else:
@@ -155,7 +155,7 @@ elif Type=='M':
<a href="/user/${item.provider.slug}"> ${item.provider.prenom} ${item.provider.nom} </a> offre <a href="/user/${item.provider.slug}"> ${item.provider.prenom} ${item.provider.nom} </a> offre
% endif % endif
% if item.exch_type=="C": % if item.exch_type=="C":
un co-voiturage le ${item.start_time.strftime('%a %d %b vers %Hh%M').decode('utf-8')}
un co-voiturage le ${item.start_time.strftime('%a %d %b vers %Hh%M')}
de <a href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/${item.Itin.start.place_id}')">${item.Itin.start.display_name}</a> de <a href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/${item.Itin.start.place_id}')">${item.Itin.start.display_name}</a>
à <a href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/${item.Itin.arrival.place_id}')">${item.Itin.arrival.display_name}</a> à <a href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/${item.Itin.arrival.place_id}')">${item.Itin.arrival.display_name}</a>
% elif item.exch_type=="M": % elif item.exch_type=="M":
@@ -165,8 +165,8 @@ elif Type=='M':
% if item.description: % if item.description:
${item.description[:30]} ${item.description[:30]}
% endif % endif
de ${item.start_time.strftime('%a %d %b %Hh%M').decode('utf-8')}
à ${item.end_time.strftime('%a %d %b %Hh%M').decode('utf-8')}
de ${item.start_time.strftime('%a %d %b %Hh%M')}
à ${item.end_time.strftime('%a %d %b %Hh%M')}
% else: % else:
% if item.Category: % if item.Category:
<i>${item.Category.exch_subtype}</i> <i>${item.Category.exch_subtype}</i>
@@ -174,7 +174,7 @@ elif Type=='M':
% if item.description: % if item.description:
${item.description[:30]} ${item.description[:30]}
% endif % endif
${item.start_time.strftime('%a %d %b').decode('utf-8')} soir
${item.start_time.strftime('%a %d %b')} soir
% endif % endif
</p> </p>
</td> </td>
@@ -284,7 +284,7 @@ ListWrap = ["Co-voiturage",u"Hébergement","Matos"]
</div> </div>
<div class="accordion-group"> <div class="accordion-group">
<div class="accordion-heading"> <div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#AccordionCounter" href="#collapseAll">Les compteurs de l´&eacute;v&eacute;nement</a>
<a class="accordion-toggle" data-toggle="collapse" data-parent="#AccordionCounter" href="#collapseAll">Les compteurs de l´év&eacute;nement</a>
</div> </div>
<div id="collapseAll" class="accordion-body collapse"> <div id="collapseAll" class="accordion-body collapse">
<div class="accordion-inner"> <div class="accordion-inner">


+ 4
- 4
jm2l/templates/Logistique/Tables.mako View File

@@ -97,17 +97,17 @@ elif Type=='M':
</td> </td>
<td> <td>
%if Type=='C': %if Type=='C':
${item.start_time.strftime('%A %d %b %Y').decode('utf-8')} vers ${item.start_time.strftime('%Hh%M')}
${item.start_time.strftime('%A %d %b %Y')} vers ${item.start_time.strftime('%Hh%M')}
de <a href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/${item.Itin.start.place_id}')">${item.Itin.start.display_name}</a> de <a href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/${item.Itin.start.place_id}')">${item.Itin.start.display_name}</a>
à <a href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/${item.Itin.arrival.place_id}')">${item.Itin.arrival.display_name}</a> à <a href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/${item.Itin.arrival.place_id}')">${item.Itin.arrival.display_name}</a>
%elif Type=='H': %elif Type=='H':
% if item.Category: % if item.Category:
<i>${item.Category.exch_subtype}</i>, <i>${item.Category.exch_subtype}</i>,
% endif % endif
La nuit du ${item.start_time.strftime('%A %d %b %Y').decode('utf-8')}<br>
La nuit du ${item.start_time.strftime('%A %d %b %Y')}<br>
%elif Type=='M': %elif Type=='M':
de ${item.start_time.strftime('%A %d %b %Y').decode('utf-8')} vers ${item.start_time.strftime('%Hh%M')}
à ${item.end_time.strftime('%A %d %b %Y').decode('utf-8')} vers ${item.end_time.strftime('%Hh%M')}<br>
de ${item.start_time.strftime('%A %d %b %Y')} vers ${item.start_time.strftime('%Hh%M')}
à ${item.end_time.strftime('%A %d %b %Y')} vers ${item.end_time.strftime('%Hh%M')}<br>
${item.Category.exch_subtype} ${item.Category.exch_subtype}
%endif %endif
%if item.description: %if item.description:


+ 4
- 4
jm2l/templates/Profil/Profil.mako View File

@@ -23,12 +23,12 @@
<% <%
DicFormA = { DicFormA = {
'nom': {'PlaceHolder':u"Mon Nom", 'ContainerClass':"span6", 'next':False}, 'nom': {'PlaceHolder':u"Mon Nom", 'ContainerClass':"span6", 'next':False},
'prenom': {'PlaceHolder':u"Mon Pr&eacute;nom", 'ContainerClass':"span6", 'next':True},
'prenom': {'PlaceHolder':u"Mon Prénom", 'ContainerClass':"span6", 'next':True},
'pseudo': {'PlaceHolder':u"Mon Pseudo", 'ContainerClass':"span6", 'next':False}, 'pseudo': {'PlaceHolder':u"Mon Pseudo", 'ContainerClass':"span6", 'next':False},
'mail': {'PlaceHolder':u"mon.mail@fqdn.tld", 'ContainerClass':"span6", 'next':True}, 'mail': {'PlaceHolder':u"mon.mail@fqdn.tld", 'ContainerClass':"span6", 'next':True},
'phone': {'PlaceHolder':u"0612345678", 'ContainerClass':"span6", 'next':False}, 'phone': {'PlaceHolder':u"0612345678", 'ContainerClass':"span6", 'next':False},
'website': {'PlaceHolder':u"http://ma-page-web.moi",'ContainerClass':"span6", 'next':True}, 'website': {'PlaceHolder':u"http://ma-page-web.moi",'ContainerClass':"span6", 'next':True},
'gpg_key': {'PlaceHolder':u"Ma cl&eacute; gpg", 'ContainerClass':"span6", 'next':False},
'gpg_key': {'PlaceHolder':u"Ma clé gpg", 'ContainerClass':"span6", 'next':False},
'soc_link':{'PlaceHolder':u"#jm2l sur irc.freenode.org",'ContainerClass':"span6", 'next':True}, 'soc_link':{'PlaceHolder':u"#jm2l sur irc.freenode.org",'ContainerClass':"span6", 'next':True},
'bio': {'Ignore':True}, 'bio': {'Ignore':True},
'tiersship': {'Ignore':True}, 'tiersship': {'Ignore':True},
@@ -52,12 +52,12 @@ DicFormB = {


DicForm2 = { DicForm2 = {
'nom': {'PlaceHolder':u"Mon Nom", "FieldStyle":"width:16em;", 'ContainerStyle':"float:left;"}, 'nom': {'PlaceHolder':u"Mon Nom", "FieldStyle":"width:16em;", 'ContainerStyle':"float:left;"},
'prenom': {'PlaceHolder':u"Mon Pr&eacute;nom", "FieldStyle":"width:16em;"},
'prenom': {'PlaceHolder':u"Mon Prénom", "FieldStyle":"width:16em;"},
'pseudo': {'PlaceHolder':u"Mon Pseudo", "FieldStyle":"width:16em;", 'ContainerStyle':"float:left;"}, 'pseudo': {'PlaceHolder':u"Mon Pseudo", "FieldStyle":"width:16em;", 'ContainerStyle':"float:left;"},
'mail': {'PlaceHolder':u"mon.mail@fqdn.tld", "FieldStyle":"width:16em;"}, 'mail': {'PlaceHolder':u"mon.mail@fqdn.tld", "FieldStyle":"width:16em;"},
'phone': {'PlaceHolder':u"0612345678", "FieldStyle":"width:16em;", 'ContainerStyle':"float:left;"}, 'phone': {'PlaceHolder':u"0612345678", "FieldStyle":"width:16em;", 'ContainerStyle':"float:left;"},
'website': {'PlaceHolder':u"http://ma-page-web.moi","FieldStyle":"width:16em;"}, 'website': {'PlaceHolder':u"http://ma-page-web.moi","FieldStyle":"width:16em;"},
'gpg_key': {'PlaceHolder':u"Ma cl&eacute; gpg", "FieldStyle":"width:90%;"},
'gpg_key': {'PlaceHolder':u"Ma clé gpg", "FieldStyle":"width:90%;"},
'soc_link':{'PlaceHolder':u"#jm2l sur irc.freenode.org","FieldStyle":"width:90%;"}, 'soc_link':{'PlaceHolder':u"#jm2l sur irc.freenode.org","FieldStyle":"width:90%;"},
'bio': {'PlaceHolder':u"Ma Bilibiographie", "FieldStyle":"width:95%;min-height:150px;", "fieldset":True, "ckeditor":1 }, 'bio': {'PlaceHolder':u"Ma Bilibiographie", "FieldStyle":"width:95%;min-height:150px;", "fieldset":True, "ckeditor":1 },
'tiersship': {'Ignore':True} 'tiersship': {'Ignore':True}


+ 13
- 13
jm2l/templates/Profil/Sejour.mako View File

@@ -14,14 +14,14 @@ fieldset:disabled {
% else: % else:
<legend class="lowshadow"> <legend class="lowshadow">
<img style="max-height:50px;" src="/img/warn.png" alt="Attention !"> <img style="max-height:50px;" src="/img/warn.png" alt="Attention !">
Vous n'avez pas confirm&eacute; votre venue aux JM2L ${CurrentYear}
Vous n'avez pas confirmé votre venue aux JM2L ${CurrentYear}
</legend> </legend>
<h4 class="lowshadow">Compl&eacute;tez et validez vite ce formulaire !</h4>
<h4 class="lowshadow">Complétez et validez vite ce formulaire !</h4>
% endif % endif


<form id="ProfilForm" action="/MonSejour" method="POST"> <form id="ProfilForm" action="/MonSejour" method="POST">
<fieldset class="ComeToJM2L"> <fieldset class="ComeToJM2L">
<legend>Arriv&eacute;e</legend>
<legend>Arrivée</legend>
<div class="form-inline"> <div class="form-inline">
J'arrive J'arrive
<select style="width:12em;" id="Arrival:Place" name="Arrival:Place" title="Lieu"> <select style="width:12em;" id="Arrival:Place" name="Arrival:Place" title="Lieu">
@@ -63,16 +63,16 @@ fieldset:disabled {
<ul style="list-style-type: none;"> <ul style="list-style-type: none;">
<li><label class="checkbox"> <li><label class="checkbox">
<input id="PMR" ${mytrip.IsCheck("Arrival:PMR")|n} name="Arrival:PMR" title="Assistance Personne à mobilit&eacute; r&eacute;duite (PMR)" type="checkbox">
d'assistance : Personne à mobilit&eacute; r&eacute;duite (PMR)</input></label>
<input id="PMR" ${mytrip.IsCheck("Arrival:PMR")|n} name="Arrival:PMR" title="Assistance Personne à mobilité réduite (PMR)" type="checkbox">
d'assistance : Personne à mobilité réduite (PMR)</input></label>
</li> </li>
<li><label class="checkbox"> <li><label class="checkbox">
<input id="Cov" ${mytrip.IsCheck("Arrival:Cov")|n} name="Arrival:Cov" title="Covoiturage" type="checkbox"> <input id="Cov" ${mytrip.IsCheck("Arrival:Cov")|n} name="Arrival:Cov" title="Covoiturage" type="checkbox">
d'un covoiturage, d'un h&eacute;bergement...<br>(j'ai rempli/je vais remplir la section Logistique).</input></label>
d'un covoiturage, d'un hébergement...<br>(j'ai rempli/je vais remplir la section Logistique).</input></label>
</li> </li>
<li><label class="checkbox"> <li><label class="checkbox">
<input id="Bras" ${mytrip.IsCheck("Arrival:Bras")|n} name="Arrival:Bras" title="Bras" type="checkbox"> <input id="Bras" ${mytrip.IsCheck("Arrival:Bras")|n} name="Arrival:Bras" title="Bras" type="checkbox">
de bras, car je rapporte plein de mat&eacute;riel. <br>(Je transporte ma maison, mon garage ...)</input></label>
de bras, car je rapporte plein de matériel. <br>(Je transporte ma maison, mon garage ...)</input></label>
</li> </li>
<li> <li>
<div class="form-inline"> <div class="form-inline">
@@ -81,7 +81,7 @@ fieldset:disabled {
Autres Autres
</input></label> </input></label>
<input type="text" style="width:20em;" name="Arrival:Comment" <input type="text" style="width:20em;" name="Arrival:Comment"
placeholder="Pr&eacute;cisions à propos de mon arriv&eacute;e…" />
placeholder="Précisions à propos de mon arrivée…" />
</div> </div>
</li> </li>
</ul> </ul>
@@ -90,7 +90,7 @@ fieldset:disabled {
</fieldset> </fieldset>
<fieldset class="ComeToJM2L"> <fieldset class="ComeToJM2L">
<legend>D&eacute;part</legend>
<legend>Départ</legend>
<div class="form-inline"> <div class="form-inline">
Je repars Je repars
<select style="width:12em;" id="Departure:Place" class="formforform-field" name="Departure:Place" title="Alors, j'arrive"> <select style="width:12em;" id="Departure:Place" class="formforform-field" name="Departure:Place" title="Alors, j'arrive">
@@ -130,8 +130,8 @@ fieldset:disabled {
Je vais avoir besoin: &nbsp;&nbsp;<small style="color: #aaa;">(Cochez les cases correspondantes)</small> Je vais avoir besoin: &nbsp;&nbsp;<small style="color: #aaa;">(Cochez les cases correspondantes)</small>
<ul style="list-style-type: none;"> <ul style="list-style-type: none;">
<li><label class="checkbox"> <li><label class="checkbox">
<input id="PMR" ${mytrip.IsCheck("Departure:PMR")|n} name="Departure:PMR" title="d'Assistance : Personne à mobilit&eacute; r&eacute;duite (PMR)" type="checkbox">
d'assistance : Personne à mobilit&eacute; r&eacute;duite (PMR)</input>
<input id="PMR" ${mytrip.IsCheck("Departure:PMR")|n} name="Departure:PMR" title="d'Assistance : Personne à mobilité réduite (PMR)" type="checkbox">
d'assistance : Personne à mobilité réduite (PMR)</input>
</label> </label>
</li> </li>
<li><label class="checkbox"> <li><label class="checkbox">
@@ -141,7 +141,7 @@ fieldset:disabled {
</li> </li>
<li><label class="checkbox"> <li><label class="checkbox">
<input id="Bras" ${mytrip.IsCheck("Departure:Bras")|n} name="Departure:Bras" title="de bras" type="checkbox"> <input id="Bras" ${mytrip.IsCheck("Departure:Bras")|n} name="Departure:Bras" title="de bras" type="checkbox">
de bras, car j'ai en stock plein de mat&eacute;riel (Ma maison).</input>
de bras, car j'ai en stock plein de matériel (Ma maison).</input>
</label> </label>
</li> </li>
<li> <li>
@@ -151,7 +151,7 @@ fieldset:disabled {
Autres Autres
</input></label> </input></label>
<input type="text" style="width:20em;" name="Departure:Comment" <input type="text" style="width:20em;" name="Departure:Comment"
placeholder="Pr&eacute;cisions à propos de mon d&eacute;part…" />
placeholder="Précisions à propos de mon départ…" />
</div> </div>
</li> </li>
</ul> </ul>


+ 1
- 1
jm2l/templates/Public/Programme.mako View File

@@ -111,7 +111,7 @@ if Counter==0:
vid = event.video.first() vid = event.video.first()
pres = event.presentation.first() pres = event.presentation.first()
%> %>
${event.start_time.strftime("%a %d %b").decode('utf-8')}<br>
${event.start_time.strftime("%a %d %b")}<br>
${event.start_time.strftime("%H:%M")} - ${event.end_time.strftime("%H:%M")} ${event.start_time.strftime("%H:%M")} - ${event.end_time.strftime("%H:%M")}
</td> </td>
<td style="position: relative;"> <td style="position: relative;">


+ 2
- 2
jm2l/templates/Salles/list_phy.mako View File

@@ -24,7 +24,7 @@ from slugify import slugify
% if len(DicSallePhy)==0: % if len(DicSallePhy)==0:
<tr> <tr>
<td style="text-align:center;"> <td style="text-align:center;">
<i>Il n'y a pas de salle d&eacute;finie pour le moment.</i>
<i>Il n'y a pas de salle définie pour le moment.</i>
</td> </td>
</tr> </tr>
% endif % endif
@@ -43,7 +43,7 @@ from slugify import slugify
% if SallePhy.uid: % if SallePhy.uid:
[ ${SallePhy.nb_places} places ] [ ${SallePhy.nb_places} places ]
% else: % else:
[ <a href="/PhySalles">Cr&eacute;er</a> ]
[ <a href="/PhySalles">Créer</a> ]
% endif % endif
</div> </div>




+ 2
- 2
jm2l/templates/Salles/salle.mako View File

@@ -29,9 +29,9 @@
%endif %endif
<% <%
DicForm = { DicForm = {
'year_uid': {'PlaceHolder':u"Ann&eacute;e", "FieldStyle":"width:7em;", "ContainerStyle":"float:left;" },
'year_uid': {'PlaceHolder':u"Année", "FieldStyle":"width:7em;", "ContainerStyle":"float:left;" },
'phy_salle_id': {'PlaceHolder':u"Salle Physique", "FieldStyle":"width:20em;", "ContainerStyle":"float:left;" }, 'phy_salle_id': {'PlaceHolder':u"Salle Physique", "FieldStyle":"width:20em;", "ContainerStyle":"float:left;" },
'place_type': {'PlaceHolder':u"Type d'&eacute;vènement","FieldStyle":"width:15em;" },
'place_type': {'PlaceHolder':u"Type d'évènement","FieldStyle":"width:15em;" },
'name': {'PlaceHolder':u"Nom de la salle", "FieldStyle":"width:90%;" }, 'name': {'PlaceHolder':u"Nom de la salle", "FieldStyle":"width:90%;" },
'description': {'PlaceHolder':u"Description", "ContainerStyle":"width:95%;min-height:150px;padding-top: 12px;", "ckeditor":"1" }, 'description': {'PlaceHolder':u"Description", "ContainerStyle":"width:95%;min-height:150px;padding-top: 12px;", "ckeditor":"1" },
} }


+ 1
- 1
jm2l/templates/Staff/compta.mako View File

@@ -46,7 +46,7 @@
</td> </td>
</tr> </tr>
% endif % endif
% for item, one_dic in found.iteritems():
% for item, one_dic in found.items():
<tr> <tr>
<td> <td>
<a href="/user/${item}"> <a href="/user/${item}">


+ 1
- 1
jm2l/templates/Staff/list.mako View File

@@ -63,7 +63,7 @@ from slugify import slugify
</a> </a>
<span style="float:right;"> <span style="float:right;">
- <a href="/user/${task.assignee.slug}">${task.assignee.pseudo or ' '.join([task.assignee.prenom, task.assignee.nom]) }</a> - <a href="/user/${task.assignee.slug}">${task.assignee.pseudo or ' '.join([task.assignee.prenom, task.assignee.nom]) }</a>
- ${task.due_date.strftime("%d %b").decode("utf-8")}
- ${task.due_date.strftime("%d %b")}
</span> </span>
% endif % endif
</td> </td>


+ 3
- 3
jm2l/templates/edit_event.mako View File

@@ -30,7 +30,7 @@
%endif %endif
% if 'uid' in form._fields: % if 'uid' in form._fields:
<div class="borderboxtime"> <div class="borderboxtime">
${event.start_time.strftime('%d %b %Y').decode('utf-8')} -
${event.start_time.strftime('%d %b %Y')} -
${event.start_time.strftime('%H:%M')} à ${event.end_time.strftime('%H:%M')} ${event.start_time.strftime('%H:%M')} à ${event.end_time.strftime('%H:%M')}
%if event.Salle: %if event.Salle:
- <strong>Salle</strong>: ${event.Salle.name} - <strong>Salle</strong>: ${event.Salle.name}
@@ -184,10 +184,10 @@ DicForm = {


</fieldset> </fieldset>
<div class="clearfix">&nbsp;</div> <div class="clearfix">&nbsp;</div>
<p style="float:right;">Créé le ${event.created.strftime('%d %b %Y').decode('utf-8')}</p>
<p style="float:right;">Créé le ${event.created.strftime('%d %b %Y')}</p>
%else: %else:
<p style="float:right;">Créé le <p style="float:right;">Créé le
${datetime.now().strftime('%d %b %Y').decode('utf-8')}
${datetime.now().strftime('%d %b %Y')}
</p> </p>
% endif % endif
<br/> <br/>


+ 14
- 14
jm2l/templates/helpers.mako View File

@@ -7,7 +7,7 @@ TabJs = {'select':[], 'desc':[]}
%> %>
<div class="row-fluid"> <div class="row-fluid">
% for FieldName, Field in form._fields.items(): % for FieldName, Field in form._fields.items():
% if DicFormat.has_key(Field.name) and DicFormat[Field.name].get("Ignore"):
% if Field.name in DicFormat and DicFormat[Field.name].get("Ignore"):
<% continue %> <% continue %>
% endif % endif
% if Field.type in ['HiddenField', 'CSRFTokenField']: % if Field.type in ['HiddenField', 'CSRFTokenField']:
@@ -27,7 +27,7 @@ TabJs = {'select':[], 'desc':[]}
</a> </a>
% endif % endif
</label> </label>
% if DicFormat.has_key(Field.name):
% if Field.name in DicFormat:
<% <%
PlaceHolder = DicFormat[Field.name].get("PlaceHolder") PlaceHolder = DicFormat[Field.name].get("PlaceHolder")
Class = [False,"ckeditor"][ "ckeditor" in DicFormat[Field.name] ] Class = [False,"ckeditor"][ "ckeditor" in DicFormat[Field.name] ]
@@ -49,7 +49,7 @@ TabJs = {'select':[], 'desc':[]}
% endfor % endfor
</div> </div>


% if DicFormat.has_key(Field.name) and DicFormat[Field.name].get("next")==True:
% if Field.name in DicFormat and DicFormat[Field.name].get("next")==True:
</div> </div>
<div class="row-fluid"> <div class="row-fluid">
% endif % endif
@@ -72,7 +72,7 @@ TabJs = {'select':[], 'desc':[]}
TabJs = {'select':[], 'desc':[]} TabJs = {'select':[], 'desc':[]}
%> %>
% for FieldName, Field in form._fields.items(): % for FieldName, Field in form._fields.items():
% if DicFormat.has_key(Field.name) and DicFormat[Field.name].get("Ignore"):
% if Field.name in DicFormat and DicFormat[Field.name].get("Ignore"):
<% continue %> <% continue %>
% endif % endif
% if Field.type in ['HiddenField', 'CSRFTokenField']: % if Field.type in ['HiddenField', 'CSRFTokenField']:
@@ -81,11 +81,11 @@ TabJs = {'select':[], 'desc':[]}
% elif Field.type=="SelectField": % elif Field.type=="SelectField":
<% TabJs['select'].append(Field.label.field_id) %> <% TabJs['select'].append(Field.label.field_id) %>
% endif % endif
% if DicFormat.has_key(Field.name) and DicFormat[Field.name].get("fieldset"):
% if Field.name in DicFormat and DicFormat[Field.name].get("fieldset"):
<fieldset> <fieldset>
<legend>${Field.label.text}</legend> <legend>${Field.label.text}</legend>
% else: % else:
% if DicFormat.has_key(Field.name) and DicFormat[Field.name].get("ContainerStyle"):
% if Field.name in DicFormat and DicFormat[Field.name].get("ContainerStyle"):
<div style="padding-right:5px;${DicFormat[Field.name].get("ContainerStyle")}"> <div style="padding-right:5px;${DicFormat[Field.name].get("ContainerStyle")}">
% else: % else:
<div style="padding-right:5px;"> <div style="padding-right:5px;">
@@ -101,7 +101,7 @@ TabJs = {'select':[], 'desc':[]}
% endif % endif
</label> </label>
% endif % endif
% if DicFormat.has_key(Field.name):
% if Field.name in DicFormat:
<% <%
PlaceHolder = DicFormat[Field.name].get("PlaceHolder") PlaceHolder = DicFormat[Field.name].get("PlaceHolder")
FieldStyle = DicFormat[Field.name].get("FieldStyle") FieldStyle = DicFormat[Field.name].get("FieldStyle")
@@ -122,7 +122,7 @@ TabJs = {'select':[], 'desc':[]}
${ error } ${ error }
</div> </div>
% endfor % endfor
% if DicFormat.has_key(Field.name) and DicFormat[Field.name].get("fieldset"):
% if Field.name in DicFormat and DicFormat[Field.name].get("fieldset"):
</fieldset> </fieldset>
% else: % else:
</div> </div>
@@ -142,7 +142,7 @@ TabJs = {'select':[], 'desc':[]}
<%def name="sejour_wrapper(Places)"> <%def name="sejour_wrapper(Places)">


<div class="form-inline"> <div class="form-inline">
D&eacute;part :
Départ :
<select style="width:12em;" id="Arrival:Place" name="Arrival:Place" title="Lieu"> <select style="width:12em;" id="Arrival:Place" name="Arrival:Place" title="Lieu">
% for place in Places: % for place in Places:
<option value="${place.place_id}">${place.display_name}</option> <option value="${place.place_id}">${place.display_name}</option>
@@ -151,7 +151,7 @@ TabJs = {'select':[], 'desc':[]}
</div> </div>
<br /> <br />
<div class="form-inline"> <div class="form-inline">
Arriv&eacute;e :
Arrivée :
<select style="width:12em;" id="Arrival:Place" name="Arrival:Place" title="Lieu"> <select style="width:12em;" id="Arrival:Place" name="Arrival:Place" title="Lieu">
% for place in Places: % for place in Places:
<option value="${place.place_id}">${place.display_name}</option> <option value="${place.place_id}">${place.display_name}</option>
@@ -184,7 +184,7 @@ TabJs = {'select':[], 'desc':[]}
${itin_form.arrival_place(style='width:17em;')} ${itin_form.arrival_place(style='width:17em;')}
</div> </div>
<div style="padding:5px;"> <div style="padding:5px;">
<small style="color:#999">Si je n´ai pas trouv&eacute; le lieu dont j´ai besoin dans ces listes...</small>
<small style="color:#999">Si je n´ai pas trouvé le lieu dont j´ai besoin dans ces listes...</small>
<br /> <br />
<small style="color:#999">Je peux </small> <small style="color:#999">Je peux </small>
<a class="btn btn-mini btn-info" role="button" href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/0');"> <a class="btn btn-mini btn-info" role="button" href="javascript:DoGetLieu('/${CurrentYear}/modal/Place/0');">
@@ -350,9 +350,9 @@ TabJs = {'select':[], 'desc':[]}
<tr> <tr>
<td style="text-align:center;" colspan="2"> <td style="text-align:center;" colspan="2">
% if NotFoundTitle: % if NotFoundTitle:
<i>${NotFoundTitle}</i>
<i>${NotFoundTitle | h}</i>
% else: % else:
<i>D&eacute;sol&eacute;, Il n'y a rien dans l'historique.</i>
<i>Désolé;, Il n'y a rien dans l'historique.</i>
% endif % endif
</td> </td>
</tr> </tr>
@@ -366,7 +366,7 @@ TabJs = {'select':[], 'desc':[]}
vid = event.video.first() vid = event.video.first()
pres = event.presentation.first() pres = event.presentation.first()
%> %>
${event.start_time.strftime('%d %b %Y').decode('utf-8')}
${event.start_time.strftime('%d %b %Y')}
${start.hour}:${"%.2d" % start.minute}-${end.hour}:${"%.2d" % end.minute} ${start.hour}:${"%.2d" % start.minute}-${end.hour}:${"%.2d" % end.minute}
</td> </td>
<td style="position: relative;">${event.event_type}: <td style="position: relative;">${event.event_type}:


+ 7
- 7
jm2l/templates/jm2l.mako View File

@@ -61,7 +61,7 @@
<div class="tabbable" id="main_tab"> <div class="tabbable" id="main_tab">
<ul class="nav nav-tabs nav-pills" style="margin-bottom: 5px;"> <ul class="nav nav-tabs nav-pills" style="margin-bottom: 5px;">
<li class="active"><a href="#Profil" id="Map_Profil" data-toggle="tab">Mon Profil</a></li> <li class="active"><a href="#Profil" id="Map_Profil" data-toggle="tab">Mon Profil</a></li>
<li><a href="#Sejour" id="Map_Sejour" data-toggle="tab">Mon S&eacute;jour</a></li>
<li><a href="#Sejour" id="Map_Sejour" data-toggle="tab">Mon Séjour</a></li>
<li><a href="#Logistique" id="Map_Logistique" data-toggle="tab">Logistique</a></li> <li><a href="#Logistique" id="Map_Logistique" data-toggle="tab">Logistique</a></li>
<li><a href="#Interventions" id="Map_Interventions" data-toggle="tab">Mes Interventions</a></li> <li><a href="#Interventions" id="Map_Interventions" data-toggle="tab">Mes Interventions</a></li>
<li><a href="#Frais" id="Map_Frais" data-toggle="tab">Mes Frais</a></li> <li><a href="#Frais" id="Map_Frais" data-toggle="tab">Mes Frais</a></li>
@@ -79,8 +79,8 @@
<div class="tabbable tabs-left" id="Interventions_tab"> <div class="tabbable tabs-left" id="Interventions_tab">
<ul class="nav nav-tabs navbar" style="margin-bottom:0;"> <ul class="nav nav-tabs navbar" style="margin-bottom:0;">
<li class="active"> <a href="#ResumePart" data-toggle="tab">Resum&eacute;</a> </li>
<li> <a href="#Conference" data-toggle="tab">Conf&eacute;rence</a> </li>
<li class="active"> <a href="#ResumePart" data-toggle="tab">Resumé</a> </li>
<li> <a href="#Conference" data-toggle="tab">Conférence</a> </li>
<li> <a href="#Stand" data-toggle="tab">Stand</a> </li> <li> <a href="#Stand" data-toggle="tab">Stand</a> </li>
<li> <a href="#Atelier" data-toggle="tab">Atelier</a> </li> <li> <a href="#Atelier" data-toggle="tab">Atelier</a> </li>
<li> <a href="#TableRonde" data-toggle="tab">Table Ronde</a> </li> <li> <a href="#TableRonde" data-toggle="tab">Table Ronde</a> </li>
@@ -118,12 +118,12 @@
<div class="tab-pane fade" id="Frais"> <div class="tab-pane fade" id="Frais">
<fieldset> <fieldset>
<legend class="lowshadow">Une participation à mes frais ?</legend> <legend class="lowshadow">Une participation à mes frais ?</legend>
L'&eacute;quipe des JM2L participe aux <u>frais de transport</u> des intervenants !<br /><br />
L'équipe des JM2L participe aux <u>frais de transport</u> des intervenants !<br /><br />
Et bien oui, mais cette participation ne sera effective que si vous remplissez <u>toutes les conditions</u> suivantes: Et bien oui, mais cette participation ne sera effective que si vous remplissez <u>toutes les conditions</u> suivantes:
<ul style="list-style:circle;"> <ul style="list-style:circle;">
<li>Vous animez <strong>un atelier, une conf&eacute;rence ou une table ronde</strong> aux JM2L ${CurrentYear}.</li>
<li>Votre fiche est renseign&eacute;e avec <strong>votre RIB</strong>.</li>
<li>Votre fiche est renseign&eacute;e avec <strong>les preuves</strong> de vos achats.</li>
<li>Vous animez <strong>un atelier, une conférence ou une table ronde</strong> aux JM2L ${CurrentYear}.</li>
<li>Votre fiche est renseignée avec <strong>votre RIB</strong>.</li>
<li>Votre fiche est renseignée avec <strong>les preuves</strong> de vos achats.</li>
<li>Vous <strong>présentez l'original de vos tickets</strong> à l'accueil pendant l'évènement.</li> <li>Vous <strong>présentez l'original de vos tickets</strong> à l'accueil pendant l'évènement.</li>
<li>Tous vos documents sont conformes.</li> <li>Tous vos documents sont conformes.</li>
</ul> </ul>


+ 16
- 16
jm2l/templates/layout.mako View File

@@ -3,7 +3,7 @@
<%namespace name="helpers" file="jm2l:templates/helpers.mako"/> <%namespace name="helpers" file="jm2l:templates/helpers.mako"/>
<% <%
context._kwargs['postpone_js']=[] context._kwargs['postpone_js']=[]
DisplayYear = request.session.get('year', 2018)
DisplayYear = request.session.get('year', 2020)
%> %>
<head> <head>
<title>JM2L ${DisplayYear}</title> <title>JM2L ${DisplayYear}</title>
@@ -68,12 +68,12 @@ ${helpers.uploader_js()}
% if request.user and request.user.vote_logo not in [1,2,3]: % if request.user and request.user.vote_logo not in [1,2,3]:
<div class="item active"> <div class="item active">
<div class="align-center"> <div class="align-center">
<H1>JM2L 2017</H1>
<h3>Choisissez ici votre logo pr&eacute;f&eacute;r&eacute; !</h3>
<H1>JM2L 2020</H1>
<h3>Choisissez ici votre logo préféré !</h3>
<p>Utilisez les flèches pour choisir et voter !<br> <p>Utilisez les flèches pour choisir et voter !<br>
Vous pouvez changer à tout moment, mais vous n'aurez droit qu'a un seul choix, le vôtre ;)</p> Vous pouvez changer à tout moment, mais vous n'aurez droit qu'a un seul choix, le vôtre ;)</p>
<p>Vous souhaitez proposer le vôtre ? <br> <p>Vous souhaitez proposer le vôtre ? <br>
N'h&eacute;sitez pas à envoyer vos propositions par mail à l'&eacute;quipe !</p>
N'hésitez pas à envoyer vos propositions par mail à l'équipe !</p>
</div> </div>
</div> </div>
% endif % endif
@@ -105,10 +105,10 @@ ${helpers.uploader_js()}
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#"> <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
Édition&nbsp;<span class="caret"></span></a> Édition&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu pull-right" style="min-width:0"> <ul class="dropdown-menu pull-right" style="min-width:0">
% for tmpyear in range(2018, 2005, -1):
% for tmpyear in range(2020, 2005, -1):
% if tmpyear==DisplayYear: % if tmpyear==DisplayYear:
<li><a style="font-weight: bold;" href="/year/${tmpyear}">${tmpyear}</a></li> <li><a style="font-weight: bold;" href="/year/${tmpyear}">${tmpyear}</a></li>
% elif tmpyear!=2014 and tmpyear!=2016:
% elif tmpyear not in [2014, 2016, 2018, 2019]:
<li><a href="/year/${tmpyear}">${tmpyear}</a></li> <li><a href="/year/${tmpyear}">${tmpyear}</a></li>
% endif % endif
% endfor % endfor
@@ -128,17 +128,17 @@ ${helpers.uploader_js()}
% if request.user: % if request.user:
% if request.user.Staff: % if request.user.Staff:
<li><a href="/${DisplayYear}/Staff">Partie Staff</a></li> <li><a href="/${DisplayYear}/Staff">Partie Staff</a></li>
<li><a href="/${DisplayYear}/ListParticipant">G&eacute;rer les intervenants</a></li>
<li><a href="/ListSalles">G&eacute;rer les salles</a></li>
<li><a href="/entities">G&eacute;rer les entit&eacute;s</a></li>
<li><a href="/${DisplayYear}/ListParticipant">Gérer les intervenants</a></li>
<li><a href="/ListSalles">Gérer les salles</a></li>
<li><a href="/entities">Gérer les entités</a></li>
<li><a href="/${DisplayYear}/ListOrga">Participations &agrave; l'orga</a></li> <li><a href="/${DisplayYear}/ListOrga">Participations &agrave; l'orga</a></li>
<li><a href="/${DisplayYear}/Staff/compta">Comptabilit&eacute;</a></li>
<li><a href="/${DisplayYear}/Staff/compta">Comptabilité</a></li>
<li><a href="/ListSallesPhy">Les salles &agrave; Poly'tech</a></li> <li><a href="/ListSallesPhy">Les salles &agrave; Poly'tech</a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
% endif % endif
<li><a href="/MesJM2L">Mon profil</a></li> <li><a href="/MesJM2L">Mon profil</a></li>
<li><a href="/user/${request.user.slug}">Mon profil public</a></li> <li><a href="/user/${request.user.slug}">Mon profil public</a></li>
<li><a href="/sign/out">Me d&eacute;connecter</a></li>
<li><a href="/sign/out">Me déconnecter</a></li>
% else: % else:
<li><a href="/participer-l-evenement#inscription">Je m'inscris</a></li> <li><a href="/participer-l-evenement#inscription">Je m'inscris</a></li>
<li><a href="/sign/login">Je m'identifie</a></li> <li><a href="/sign/login">Je m'identifie</a></li>
@@ -186,9 +186,9 @@ ${helpers.uploader_js()}


<footer class="footer"> <footer class="footer">
<div class="container"> <div class="container">
<h4>JM2L 2005-2018</h4>
<h4>JM2L 2005-2020</h4>
<p> <p>
Concoct&eacute; par <a href="http://www.linux-azur.org/">Linux Azur</a> ~
Concocté par <a href="http://www.linux-azur.org/">Linux Azur</a> ~
<a href="http://creativecommons.org/licenses/by-sa/4.0/">CopyFriendly</a> <a href="http://creativecommons.org/licenses/by-sa/4.0/">CopyFriendly</a>
</p> </p>
<p> <p>
@@ -196,10 +196,10 @@ ${helpers.uploader_js()}
</p> </p>
<p> <p>
Conception et construction en <a href="http://git.linux-azur.org/JM2L/jm2l/src/master">DIY</a> ~ Conception et construction en <a href="http://git.linux-azur.org/JM2L/jm2l/src/master">DIY</a> ~
H&eacute;berg&eacute; par <a href="http://www.heberg-24.com/"> Heberg24 </a>
Hébergé par <a href="http://www.heberg-24.com/"> Heberg24 </a>
</p> </p>
<p> <p>
Vous avez trouv&eacute; un bug ? <a href="http://git.linux-azur.org/JM2L/jm2l/issues">Reportez-le ici</a>
Vous avez trouvé un bug ? <a href="http://git.linux-azur.org/JM2L/jm2l/issues">Reportez-le ici</a>
</p> </p>
</div> </div>
</footer> </footer>
@@ -245,7 +245,7 @@ function handlevote() {
$('.carousel-vote a').attr('href', "/vote_logo/" + currentIndex ) $('.carousel-vote a').attr('href', "/vote_logo/" + currentIndex )
if (currentIndex==${request.user.vote_logo or 0}) { if (currentIndex==${request.user.vote_logo or 0}) {
$('.carousel-vote a').removeClass('btn-primary').addClass('btn-success') $('.carousel-vote a').removeClass('btn-primary').addClass('btn-success')
$('.carousel-vote a').html("<i class='icon-ok icon-white'></i> Mon pr&eacute;f&eacute;r&eacute; ! ");
$('.carousel-vote a').html("<i class='icon-ok icon-white'></i> Mon préféré ! ");
} else { } else {
$('.carousel-vote a').removeClass('btn-success').addClass('btn-primary'); $('.carousel-vote a').removeClass('btn-success').addClass('btn-primary');
$('.carousel-vote a').html("<i class='icon-star icon-white'></i> Je vote pour ce logo ! "); $('.carousel-vote a').html("<i class='icon-star icon-white'></i> Je vote pour ce logo ! ");


+ 3
- 3
jm2l/templates/mail_html.mako View File

@@ -96,7 +96,7 @@ Voici ce qu'il y'a dans la liste des tâches qui te sont assignées:
% for t in sorted(User.task_assoc, key=lambda k:k.due_date): % for t in sorted(User.task_assoc, key=lambda k:k.due_date):
% if not t.closed: % if not t.closed:
<tr> <tr>
<td>${t.due_date.strftime('%d %B %Y').decode('utf-8', 'xmlcharrefreplace')}</td><td>${t.area.name}</td>
<td>${t.due_date.strftime('%d %B %Y')}</td><td>${t.area.name}</td>
<td><a href="http://jm2l.linux-azur.org/2017/Staff/tasks/${t.uid}">${t.name}</a> <td><a href="http://jm2l.linux-azur.org/2017/Staff/tasks/${t.uid}">${t.name}</a>
% endif % endif
% endfor % endfor
@@ -107,7 +107,7 @@ Voici ce qu'il y'a dans la liste des tâches qui te sont assignées:
% for t in sorted(Contact.task_assoc, key=lambda k:k.due_date): % for t in sorted(Contact.task_assoc, key=lambda k:k.due_date):
% if not t.closed: % if not t.closed:
<tr> <tr>
<td>${t.due_date.strftime('%d %B %Y').decode('utf-8', 'xmlcharrefreplace')}</td><td>${t.area.name}</td>
<td>${t.due_date.strftime('%d %B %Y')}</td><td>${t.area.name}</td>
<td><a href="http://jm2l.linux-azur.org/2017/Staff/tasks/${t.uid}">${t.name}</a> <td><a href="http://jm2l.linux-azur.org/2017/Staff/tasks/${t.uid}">${t.name}</a>
% endif % endif
% endfor % endfor
@@ -116,7 +116,7 @@ Voici ce qu'il y'a dans la liste des tâches qui te sont assignées:
Pour accéder à ton espace sur le site, il te suffit de cliquer sur le <a href="${request.route_url('bymail', hash=User.my_hash)}">lien suivant.</a> Pour accéder à ton espace sur le site, il te suffit de cliquer sur le <a href="${request.route_url('bymail', hash=User.my_hash)}">lien suivant.</a>
<br/><br/> <br/><br/>
% for t in filter(lambda k:k.uid==51, Contact.task_assoc): % for t in filter(lambda k:k.uid==51, Contact.task_assoc):
Nous avons fixé la prochaine réunion JM2L au ${t.due_date.strftime('%d %B').decode('utf-8', 'xmlcharrefreplace')} à 19h30.
Nous avons fixé la prochaine réunion JM2L au ${t.due_date.strftime('%d %B')} à 19h30.
% endfor % endfor
<p> <p>
Bon courage ! Bon courage !


+ 3
- 3
jm2l/templates/mail_plain.mako View File

@@ -65,7 +65,7 @@ Voici ce qu'il y'a dans la liste des tâches qui te sont assignées:


% for t in sorted(User.task_assoc, key=lambda k:k.due_date): % for t in sorted(User.task_assoc, key=lambda k:k.due_date):
% if not t.closed: % if not t.closed:
- Pour le ${t.due_date.strftime('%d %B %Y').decode('utf-8', 'xmlcharrefreplace')} - ${t.area.name} tâche ${t.uid}
- Pour le ${t.due_date.strftime('%d %B %Y')} - ${t.area.name} tâche ${t.uid}
=> ${t.name} => ${t.name}
% endif % endif
@@ -75,7 +75,7 @@ Et il y'a aussi des tâches communes !


% for t in sorted(Contact.task_assoc, key=lambda k:k.due_date): % for t in sorted(Contact.task_assoc, key=lambda k:k.due_date):
% if not t.closed and t.uid!=51: % if not t.closed and t.uid!=51:
- Pour le ${t.due_date.strftime('%d %B %Y').decode('utf-8', 'xmlcharrefreplace')} - ${t.area.name} tâche ${t.uid}
- Pour le ${t.due_date.strftime('%d %B %Y')} - ${t.area.name} tâche ${t.uid}
=> ${t.name} => ${t.name}
% endif % endif
@@ -85,7 +85,7 @@ Pour accéder à ton espace il te suffit de cliquer sur le lien suivant :
${request.route_url('bymail', hash=User.my_hash)}. ${request.route_url('bymail', hash=User.my_hash)}.


% for t in filter(lambda k:k.uid==51, Contact.task_assoc): % for t in filter(lambda k:k.uid==51, Contact.task_assoc):
Nous avons fixé la prochaine réunion JM2L au ${t.due_date.strftime('%d %B').decode('utf-8', 'xmlcharrefreplace')} à 19h30.
Nous avons fixé la prochaine réunion JM2L au ${t.due_date.strftime('%d %B')} à 19h30.
% endfor % endfor




+ 1
- 1
jm2l/templates/show_user.mako View File

@@ -52,7 +52,7 @@
</div> </div>
<br/> <br/>
<hr/> <hr/>
<p style="float:right;">Créé le ${DispUser.created.strftime('%d %b %Y').decode('utf-8')}</p>
<p style="float:right;">Créé le ${DispUser.created.strftime('%d %b %Y')}</p>


</div> </div>
</div> </div>

+ 2
- 2
jm2l/templates/view_event.mako View File

@@ -13,7 +13,7 @@
</div> </div>
<strong>${event.event_type}</strong>: <strong>${event.event_type}</strong>:
<div class="borderboxtime"> <div class="borderboxtime">
${event.start_time.strftime('%d %b %Y').decode('utf-8')} -
${event.start_time.strftime('%d %b %Y')} -
${event.start_time.strftime('%H:%M')} à ${event.end_time.strftime('%H:%M')} ${event.start_time.strftime('%H:%M')} à ${event.end_time.strftime('%H:%M')}
</div> </div>
% if event.for_year==CurrentYear and request.user and (request.user.Staff or request.user in event.intervenants): % if event.for_year==CurrentYear and request.user and (request.user.Staff or request.user in event.intervenants):
@@ -116,7 +116,7 @@
</p> </p>
% endfor % endfor
<div class="clearfix">&nbsp;</div> <div class="clearfix">&nbsp;</div>
<p style="float:right;">Créé le ${event.created.strftime('%d %b %Y').decode('utf-8')}</p>
<p style="float:right;">Créé le ${event.created.strftime('%d %b %Y')}</p>
<br/> <br/>
<hr/> <hr/>
</div> </div>


+ 1
- 1
jm2l/templates/view_tiers.mako View File

@@ -103,7 +103,7 @@ ${The_entity_type.entity_subtype}
</p> </p>
% endfor % endfor
<br/><br/> <br/><br/>
<p style="float:right;">Créé le ${entity.created.strftime('%d %b %Y').decode('utf-8')}</p>
<p style="float:right;">Créé le ${entity.created.strftime('%d %b %Y')}</p>
<br/> <br/>
<hr/> <hr/>




+ 1
- 1
jm2l/templates/view_user.mako View File

@@ -47,7 +47,7 @@
<h4>Ses interventions :</h4> <h4>Ses interventions :</h4>
${helpers.show_Interventions(DispUser.events)} ${helpers.show_Interventions(DispUser.events)}
% endif % endif
<p style="float:right;">Créé le ${DispUser.created.strftime('%d %b %Y').decode('utf-8')}</p>
<p style="float:right;">Créé le ${DispUser.created.strftime('%d %b %Y')}</p>


</div> </div>
</div> </div>

+ 4
- 1
jm2l/to_print.py View File

@@ -1,6 +1,9 @@
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
from pyramid.response import Response from pyramid.response import Response
import cStringIO as StringIO
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from pyramid.view import view_config from pyramid.view import view_config
from .models import DBSession, Event, Salles from .models import DBSession, Event, Salles
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas


+ 172
- 168
jm2l/upload.py View File

@@ -9,113 +9,114 @@ from os import path
import mimetypes import mimetypes
import magic import magic
import subprocess import subprocess
import cStringIO as StringIO
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
# Database access imports # Database access imports
from .models import User, Place, Tiers, Event, SallePhy from .models import User, Place, Tiers, Event, SallePhy
from .blenderthumbnailer import blend_extract_thumb, write_png from .blenderthumbnailer import blend_extract_thumb, write_png
from jm2l.const import CurrentYear from jm2l.const import CurrentYear
from slugify import slugify from slugify import slugify


MIN_FILE_SIZE = 1 # bytes
MAX_FILE_SIZE = 500000000 # bytes
MIN_FILE_SIZE = 1 # bytes
MAX_FILE_SIZE = 500000000 # bytes
IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)') IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)')
ACCEPTED_MIMES = ['application/pdf', ACCEPTED_MIMES = ['application/pdf',
'application/vnd.oasis.opendocument.text',
'application/vnd.oasis.opendocument.text-template',
'application/vnd.oasis.opendocument.graphics',
'application/vnd.oasis.opendocument.graphics-template',
'application/vnd.oasis.opendocument.presentation',
'application/vnd.oasis.opendocument.presentation-template',
'application/vnd.oasis.opendocument.spreadsheet',
'application/vnd.oasis.opendocument.spreadsheet-template',
'image/svg+xml',
'application/x-blender'
]

'application/vnd.oasis.opendocument.text',
'application/vnd.oasis.opendocument.text-template',
'application/vnd.oasis.opendocument.graphics',
'application/vnd.oasis.opendocument.graphics-template',
'application/vnd.oasis.opendocument.presentation',
'application/vnd.oasis.opendocument.presentation-template',
'application/vnd.oasis.opendocument.spreadsheet',
'application/vnd.oasis.opendocument.spreadsheet-template',
'image/svg+xml',
'application/x-blender'
]


ACCEPT_FILE_TYPES = IMAGE_TYPES ACCEPT_FILE_TYPES = IMAGE_TYPES
THUMBNAIL_SIZE = 80 THUMBNAIL_SIZE = 80
EXPIRATION_TIME = 300 # seconds
IMAGEPATH = [ 'images' ]
DOCPATH = [ 'document' ]
THUMBNAILPATH = [ 'images', 'thumbnails' ]
EXPIRATION_TIME = 300 # seconds
IMAGEPATH = ['images']
DOCPATH = ['document']
THUMBNAILPATH = ['images', 'thumbnails']
# change the following to POST if DELETE isn't supported by the webserver # change the following to POST if DELETE isn't supported by the webserver
DELETEMETHOD="DELETE"
DELETEMETHOD = "DELETE"


mimetypes.init() mimetypes.init()


class MediaPath():

class MediaPath():


def get_all(self, media_table, linked_id, MediaType=None): def get_all(self, media_table, linked_id, MediaType=None):
filelist = list() filelist = list()
curpath = self.get_mediapath(media_table, linked_id, None) curpath = self.get_mediapath(media_table, linked_id, None)
thumbpath = os.path.join( curpath, 'thumbnails')
thumbpath = os.path.join(curpath, 'thumbnails')
if not os.path.isdir(curpath) or not os.path.isdir(thumbpath): if not os.path.isdir(curpath) or not os.path.isdir(thumbpath):
return list() return list()
for f in os.listdir(curpath): for f in os.listdir(curpath):
filename, ext = os.path.splitext( f )
if os.path.isdir(os.path.join(curpath,f)):
filename, ext = os.path.splitext(f)
if os.path.isdir(os.path.join(curpath, f)):
continue continue
if f.endswith('.type'): if f.endswith('.type'):
continue continue
if f: if f:
ress_url = '/image/%s/%d/%s' % (media_table, linked_id, f.replace(" ", "%20")) ress_url = '/image/%s/%d/%s' % (media_table, linked_id, f.replace(" ", "%20"))
thumb_url = '/image/%s/%d/thumbnails/%s' % (media_table, linked_id, f.replace(" ","%20"))
thumb_url = '/image/%s/%d/thumbnails/%s' % (media_table, linked_id, f.replace(" ", "%20"))
if MediaType is None: if MediaType is None:
if os.path.exists(os.path.join(thumbpath, f +".jpg")):
filelist.append((ress_url, thumb_url +".jpg"))
if os.path.exists(os.path.join(thumbpath, f + ".jpg")):
filelist.append((ress_url, thumb_url + ".jpg"))
else: else:
filelist.append((ress_url, thumb_url)) filelist.append((ress_url, thumb_url))
elif MediaType=='Image' and len( os.path.splitext(filename)[1] )==0:
elif MediaType == 'Image' and len(os.path.splitext(filename)[1]) == 0:
filelist.append((ress_url, thumb_url)) filelist.append((ress_url, thumb_url))
elif MediaType=='Other' and len( os.path.splitext(filename)[1] ):
elif MediaType == 'Other' and len(os.path.splitext(filename)[1]):
filelist.append((ress_url, thumb_url)) filelist.append((ress_url, thumb_url))
return filelist return filelist



def get_list(self, media_table, linked_id, MediaType=None): def get_list(self, media_table, linked_id, MediaType=None):
filelist = list() filelist = list()
curpath = self.get_mediapath(media_table, linked_id, None) curpath = self.get_mediapath(media_table, linked_id, None)
if not os.path.isdir(curpath): if not os.path.isdir(curpath):
return list() return list()
for f in os.listdir(curpath): for f in os.listdir(curpath):
if os.path.isdir(os.path.join(curpath,f)):
if os.path.isdir(os.path.join(curpath, f)):
continue continue
if f.endswith('.type'): if f.endswith('.type'):
continue continue
if f: if f:
filename, ext = os.path.splitext( f )
tmpurl = '/image/%s/%d/%s' % (media_table, linked_id, f.replace(" ","%20"))
filename, ext = os.path.splitext(f)
tmpurl = '/image/%s/%d/%s' % (media_table, linked_id, f.replace(" ", "%20"))
if MediaType is None: if MediaType is None:
filelist.append(tmpurl) filelist.append(tmpurl)
elif MediaType=='Image' and ext.lower() in ['.gif','.jpg','.png','.svg','.jpeg']:
elif MediaType == 'Image' and ext.lower() in ['.gif', '.jpg', '.png', '.svg', '.jpeg']:
filelist.append(tmpurl) filelist.append(tmpurl)
elif MediaType=='Other' and ext.lower() not in ['.gif','.jpg','.png','.svg','.jpeg']:
elif MediaType == 'Other' and ext.lower() not in ['.gif', '.jpg', '.png', '.svg', '.jpeg']:
filelist.append(tmpurl) filelist.append(tmpurl)
return filelist return filelist
def get_thumb(self, media_table, linked_id, MediaType=None): def get_thumb(self, media_table, linked_id, MediaType=None):
filelist = list() filelist = list()
curpath = self.get_mediapath(media_table, linked_id, None) curpath = self.get_mediapath(media_table, linked_id, None)
curpath = os.path.join( curpath, 'thumbnails')
curpath = os.path.join(curpath, 'thumbnails')
if not os.path.isdir(curpath): if not os.path.isdir(curpath):
return list() return list()
for f in os.listdir(curpath): for f in os.listdir(curpath):
filename, ext = os.path.splitext( f )
if os.path.isdir(os.path.join(curpath,f)):
filename, ext = os.path.splitext(f)
if os.path.isdir(os.path.join(curpath, f)):
continue continue
if f.endswith('.type'): if f.endswith('.type'):
continue continue
if f: if f:
tmpurl = '/image/%s/%d/thumbnails/%s' % (media_table, linked_id, f.replace(" ","%20"))
tmpurl = '/image/%s/%d/thumbnails/%s' % (media_table, linked_id, f.replace(" ", "%20"))
if MediaType is None: if MediaType is None:
filelist.append(tmpurl) filelist.append(tmpurl)
elif MediaType=='Image' and len( os.path.splitext(filename)[1] )==0:
elif MediaType == 'Image' and len(os.path.splitext(filename)[1]) == 0:
filelist.append(tmpurl) filelist.append(tmpurl)
elif MediaType=='Other' and len( os.path.splitext(filename)[1] ):
elif MediaType == 'Other' and len(os.path.splitext(filename)[1]):
filelist.append(tmpurl) filelist.append(tmpurl)
return filelist
return filelist


def move_mediapath(self, media_table, from_id, to_id): def move_mediapath(self, media_table, from_id, to_id):
""" """
@@ -126,8 +127,8 @@ class MediaPath():
:return: Error if any :return: Error if any
""" """
if media_table in ['tiers', 'place', 'salle', 'users']: if media_table in ['tiers', 'place', 'salle', 'users']:
src = IMAGEPATH + [ media_table, from_id ]
dst = IMAGEPATH + [ media_table, to_id ]
src = IMAGEPATH + [media_table, from_id]
dst = IMAGEPATH + [media_table, to_id]
else: else:
raise RuntimeError("Sorry, Media '%s' not supported yet for move." % media_table) raise RuntimeError("Sorry, Media '%s' not supported yet for move." % media_table)


@@ -153,51 +154,51 @@ class MediaPath():
linked_id = str(linked_id) linked_id = str(linked_id)
if media_table in ['tiers', 'place', 'salle']: if media_table in ['tiers', 'place', 'salle']:
# Retrieve Slug # Retrieve Slug
if media_table=='tiers':
if media_table == 'tiers':
slug = Tiers.by_id(linked_id).slug slug = Tiers.by_id(linked_id).slug
if media_table=='place':
if media_table == 'place':
slug = Place.by_id(linked_id).slug or slugify(Place.by_id(linked_id).name) slug = Place.by_id(linked_id).slug or slugify(Place.by_id(linked_id).name)
if media_table=='salle':
if media_table == 'salle':
slug = SallePhy.by_id(linked_id).slug slug = SallePhy.by_id(linked_id).slug
p = IMAGEPATH + [ media_table, slug ]
elif media_table=='presse':
p = IMAGEPATH + [media_table, slug]
elif media_table == 'presse':
# Use Year in linked_id # Use Year in linked_id
p = IMAGEPATH + [ media_table, linked_id ]
elif media_table=='tasks':
p = IMAGEPATH + [media_table, linked_id]
elif media_table == 'tasks':
# Use Current Year # Use Current Year
p = IMAGEPATH + [ str(CurrentYear), media_table, linked_id ]
elif media_table=='poles':
p = IMAGEPATH + [str(CurrentYear), media_table, linked_id]
elif media_table == 'poles':
# Use Current Year # Use Current Year
p = IMAGEPATH + [ str(CurrentYear), media_table, linked_id ]
p = IMAGEPATH + [str(CurrentYear), media_table, linked_id]
elif media_table in ['RIB', 'Justif']: elif media_table in ['RIB', 'Justif']:
slug = User.by_id(linked_id).slug slug = User.by_id(linked_id).slug
p = IMAGEPATH + ['users', slug , media_table ]
p = IMAGEPATH + ['users', slug, media_table]
elif media_table in ['users', 'badge']: elif media_table in ['users', 'badge']:
user = User.by_id(linked_id) user = User.by_id(linked_id)
if not user: if not user:
raise HTTPNotFound() raise HTTPNotFound()
else: else:
slug = user.slug slug = user.slug
p = IMAGEPATH + [media_table, slug ]
elif media_table=='event':
p = IMAGEPATH + [media_table, slug]
elif media_table == 'event':
ev = Event.by_id(linked_id) ev = Event.by_id(linked_id)
slug = ev.slug slug = ev.slug
year = ev.for_year year = ev.for_year
p = IMAGEPATH + ['event', str(year), slug ]
p = IMAGEPATH + ['event', str(year), slug]


if name: if name:
p += [ name ]
p += [name]
TargetPath = os.path.join('jm2l/upload', *p) TargetPath = os.path.join('jm2l/upload', *p)
if not os.path.isdir(os.path.dirname(TargetPath)): if not os.path.isdir(os.path.dirname(TargetPath)):
try: try:
os.makedirs(os.path.dirname(TargetPath)) os.makedirs(os.path.dirname(TargetPath))
except OSError, e:
except OSError as e:
if e.errno != 17: if e.errno != 17:
raise e raise e
return os.path.join('jm2l/upload', *p)
return os.path.join('jm2l/upload', *p)


def ExtMimeIcon(self, mime): def ExtMimeIcon(self, mime):
if mime=='application/pdf':
if mime == 'application/pdf':
return "/img/PDF.png" return "/img/PDF.png"


def check_blend_file(self, fileobj): def check_blend_file(self, fileobj):
@@ -217,12 +218,12 @@ class MediaPath():
fileobj.seek(0) fileobj.seek(0)


# Check if the binary file is a blender file # Check if the binary file is a blender file
if ( mimetype == "application/octet-stream" or mimetype == "application/x-gzip" ) and self.check_blend_file(fileobj):
if (mimetype == "application/octet-stream" or mimetype == "application/x-gzip") and self.check_blend_file(
fileobj):
return "application/x-blender", True return "application/x-blender", True
else: else:
return mimetype, False return mimetype, False



def get_mimetype(self, name): def get_mimetype(self, name):
""" This function return the mime-type based on .type file """ """ This function return the mime-type based on .type file """
try: try:
@@ -232,6 +233,7 @@ class MediaPath():
except IOError: except IOError:
return None return None



@view_defaults(route_name='media_upload') @view_defaults(route_name='media_upload')
class MediaUpload(MediaPath): class MediaUpload(MediaPath):


@@ -256,17 +258,17 @@ class MediaUpload(MediaPath):
# Try to determine mime type from content uploaded # Try to determine mime type from content uploaded
found_mime = magic.from_buffer(filecontent.read(1024), mime=True) found_mime = magic.from_buffer(filecontent.read(1024), mime=True)
filecontent.seek(0) filecontent.seek(0)
# Do a special statement for specific detected mime type # Do a special statement for specific detected mime type
if found_mime in ["application/octet-stream", "application/x-gzip"]: if found_mime in ["application/octet-stream", "application/x-gzip"]:
# Lets see if it's a bender file # Lets see if it's a bender file
if self.check_blend_file(filecontent): if self.check_blend_file(filecontent):
found_mime = "application/x-blender" found_mime = "application/x-blender"
# MonKey Patch of content type # MonKey Patch of content type
result['type'] = found_mime
# Reject mime type that don't match
if found_mime!=result['type']:
result['type'] = found_mime
# Reject mime type that don't match
if found_mime != result['type']:
result['error'] = 'L\'extension du fichier ne correspond pas à son contenu - ' result['error'] = 'L\'extension du fichier ne correspond pas à son contenu - '
result['error'] += "( %s vs %s )" % (found_mime, result['type']) result['error'] += "( %s vs %s )" % (found_mime, result['type'])
return False return False
@@ -281,7 +283,7 @@ class MediaUpload(MediaPath):
result['error'] = 'le fichier est trop petit' result['error'] = 'le fichier est trop petit'
elif result['size'] > MAX_FILE_SIZE: elif result['size'] > MAX_FILE_SIZE:
result['error'] = 'le fichier est trop voluminueux' result['error'] = 'le fichier est trop voluminueux'
#elif not ACCEPT_FILE_TYPES.match(file['type']):
# elif not ACCEPT_FILE_TYPES.match(file['type']):
# file['error'] = u'les type de fichiers acceptés sont png, jpg et gif' # file['error'] = u'les type de fichiers acceptés sont png, jpg et gif'
else: else:
return True return True
@@ -289,80 +291,80 @@ class MediaUpload(MediaPath):
return False return False


def get_file_size(self, fileobj): def get_file_size(self, fileobj):
fileobj.seek(0, 2) # Seek to the end of the file
size = fileobj.tell() # Get the position of EOF
fileobj.seek(0) # Reset the file position to the beginning
fileobj.seek(0, 2) # Seek to the end of the file
size = fileobj.tell() # Get the position of EOF
fileobj.seek(0) # Reset the file position to the beginning
return size return size


def thumbnailurl(self,name):
return self.request.route_url('media_view',name='thumbnails',
media_table=self.media_table,
uid=self.linked_id) + '/' + name
def thumbnailurl(self, name):
return self.request.route_url('media_view', name='thumbnails',
media_table=self.media_table,
uid=self.linked_id) + '/' + name


def thumbnailpath(self,name):
def thumbnailpath(self, name):
origin = self.mediapath(name) origin = self.mediapath(name)
TargetPath = os.path.join( os.path.dirname(origin), 'thumbnails', name)
TargetPath = os.path.join(os.path.dirname(origin), 'thumbnails', name)
if not os.path.isdir(os.path.dirname(TargetPath)): if not os.path.isdir(os.path.dirname(TargetPath)):
os.makedirs(os.path.dirname(TargetPath))
os.makedirs(os.path.dirname(TargetPath))
return TargetPath return TargetPath


def createthumbnail(self, filename): def createthumbnail(self, filename):
image = Image.open( self.mediapath(filename) )
image = Image.open(self.mediapath(filename))
image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS) image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0)) timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
timage.paste( timage.paste(
image, image,
((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2)) ((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2))
TargetFileName = self.thumbnailpath(filename) TargetFileName = self.thumbnailpath(filename)
timage.save( TargetFileName )
return self.thumbnailurl( os.path.basename(TargetFileName) )
timage.save(TargetFileName)
return self.thumbnailurl(os.path.basename(TargetFileName))


def pdfthumbnail(self, filename): def pdfthumbnail(self, filename):
TargetFileName = self.thumbnailpath(filename) TargetFileName = self.thumbnailpath(filename)
Command = ["convert","./%s[0]" % self.mediapath(filename),"./%s_.jpg" % TargetFileName]
Command = ["convert", "./%s[0]" % self.mediapath(filename), "./%s_.jpg" % TargetFileName]
Result = subprocess.call(Command) Result = subprocess.call(Command)
if Result==0:
image = Image.open( TargetFileName+"_.jpg" )
pdf_indicator = Image.open( "jm2l/static/img/PDF_Thumb_Stamp.png" )
if Result == 0:
image = Image.open(TargetFileName + "_.jpg")
pdf_indicator = Image.open("jm2l/static/img/PDF_Thumb_Stamp.png")
image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS) image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0)) timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
# Add thumbnail # Add thumbnail
timage.paste( timage.paste(
image,
image,
((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2)) ((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2))
# Stamp with PDF file type # Stamp with PDF file type
timage.paste( timage.paste(
pdf_indicator, pdf_indicator,
(timage.size[0]-30, timage.size[1]-30),
(timage.size[0] - 30, timage.size[1] - 30),
pdf_indicator, pdf_indicator,
)
timage.convert('RGB').save( TargetFileName+".jpg", 'JPEG')
os.unlink(TargetFileName+"_.jpg")
return self.thumbnailurl( os.path.basename(TargetFileName+".jpg") )
)
timage.convert('RGB').save(TargetFileName + ".jpg", 'JPEG')
os.unlink(TargetFileName + "_.jpg")
return self.thumbnailurl(os.path.basename(TargetFileName + ".jpg"))
return self.ExtMimeIcon('application/pdf') return self.ExtMimeIcon('application/pdf')


def svgthumbnail(self, filename): def svgthumbnail(self, filename):
TargetFileName = self.thumbnailpath(filename) TargetFileName = self.thumbnailpath(filename)
Command = ["convert","./%s[0]" % self.mediapath(filename),"./%s_.jpg" % TargetFileName]
Command = ["convert", "./%s[0]" % self.mediapath(filename), "./%s_.jpg" % TargetFileName]
Result = subprocess.call(Command) Result = subprocess.call(Command)
if Result==0:
image = Image.open( TargetFileName+"_.jpg" )
pdf_indicator = Image.open( "jm2l/static/img/svg-icon.png" )
if Result == 0:
image = Image.open(TargetFileName + "_.jpg")
pdf_indicator = Image.open("jm2l/static/img/svg-icon.png")
image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS) image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0)) timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
# Add thumbnail # Add thumbnail
timage.paste( timage.paste(
image,
image,
((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2)) ((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2))
# Stamp with PDF file type # Stamp with PDF file type
timage.paste( timage.paste(
pdf_indicator, pdf_indicator,
(timage.size[0]-30, timage.size[1]-30),
(timage.size[0] - 30, timage.size[1] - 30),
pdf_indicator, pdf_indicator,
)
timage.convert('RGB').save( TargetFileName+".jpg", 'JPEG')
os.unlink(TargetFileName+"_.jpg")
return self.thumbnailurl( os.path.basename(TargetFileName+".jpg") )
)
timage.convert('RGB').save(TargetFileName + ".jpg", 'JPEG')
os.unlink(TargetFileName + "_.jpg")
return self.thumbnailurl(os.path.basename(TargetFileName + ".jpg"))
return self.ExtMimeIcon('image/svg+xml') return self.ExtMimeIcon('image/svg+xml')


def docthumbnail(self, filename): def docthumbnail(self, filename):
@@ -370,29 +372,30 @@ class MediaUpload(MediaPath):
# let's take the thumbnail generated inside the document # let's take the thumbnail generated inside the document
Command = ["unzip", "-p", self.mediapath(filename), "Thumbnails/thumbnail.png"] Command = ["unzip", "-p", self.mediapath(filename), "Thumbnails/thumbnail.png"]
ThumbBytes = subprocess.check_output(Command) ThumbBytes = subprocess.check_output(Command)
image = Image.open( StringIO.StringIO(ThumbBytes) )
image = Image.open(StringIO.StringIO(ThumbBytes))
image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS) image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
# Use the correct stamp # Use the correct stamp
f, ext = os.path.splitext( filename )
istamp = [ ('Writer','odt'),
('Impress','odp'),
('Calc','ods'),
('Draw','odg')]
stampfilename = filter(lambda (x,y): ext.endswith(y), istamp)
stamp = Image.open( "jm2l/static/img/%s-icon.png" % stampfilename[0][0])
f, ext = os.path.splitext(filename)
istamp = [('Writer', 'odt'),
('Impress', 'odp'),
('Calc', 'ods'),
('Draw', 'odg')]
stampfilename = filter(lambda x, y: ext.endswith(y), istamp)
stamp = Image.open("jm2l/static/img/%s-icon.png" % stampfilename[0][0])

timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0)) timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
# Add thumbnail # Add thumbnail
timage.paste( timage.paste(
image,
image,
((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2)) ((THUMBNAIL_SIZE - image.size[0]) / 2, (THUMBNAIL_SIZE - image.size[1]) / 2))
# Stamp with PDF file type # Stamp with PDF file type
timage.paste( timage.paste(
stamp, stamp,
(timage.size[0]-30, timage.size[1]-30),
(timage.size[0] - 30, timage.size[1] - 30),
stamp, stamp,
)
timage.convert('RGB').save( TargetFileName+".jpg", 'JPEG')
return self.thumbnailurl( os.path.basename(TargetFileName+".jpg") )
)
timage.convert('RGB').save(TargetFileName + ".jpg", 'JPEG')
return self.thumbnailurl(os.path.basename(TargetFileName + ".jpg"))


def blendthumbnail(self, filename): def blendthumbnail(self, filename):
blendfile = self.mediapath(filename) blendfile = self.mediapath(filename)
@@ -400,18 +403,18 @@ class MediaUpload(MediaPath):
if 0: if 0:
head = fileobj.read(12) head = fileobj.read(12)
fileobj.seek(0) fileobj.seek(0)
if head[:2] == b'\x1f\x8b': # gzip magic if head[:2] == b'\x1f\x8b': # gzip magic
import zlib import zlib
head = zlib.decompress(fileobj.read(), 31)[:12] head = zlib.decompress(fileobj.read(), 31)[:12]
fileobj.seek(0)
fileobj.seek(0)
buf, width, height = blend_extract_thumb(blendfile) buf, width, height = blend_extract_thumb(blendfile)
if buf: if buf:
png = write_png(buf, width, height) png = write_png(buf, width, height)
TargetFileName = self.thumbnailpath(filename) TargetFileName = self.thumbnailpath(filename)
image = Image.open(StringIO.StringIO(png)) image = Image.open(StringIO.StringIO(png))
blender_indicator = Image.open( "jm2l/static/img/Blender_Thumb_Stamp.png" )
blender_indicator = Image.open("jm2l/static/img/Blender_Thumb_Stamp.png")
image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS) image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0)) timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
# Add thumbnail # Add thumbnail
@@ -421,24 +424,24 @@ class MediaUpload(MediaPath):
# Stamp with Blender file type # Stamp with Blender file type
timage.paste( timage.paste(
blender_indicator, blender_indicator,
(timage.size[0]-30, timage.size[1]-30),
(timage.size[0] - 30, timage.size[1] - 30),
blender_indicator, blender_indicator,
)
timage.save( TargetFileName+".png")
return self.thumbnailurl( os.path.basename(TargetFileName+".png") )
)
timage.save(TargetFileName + ".png")
return self.thumbnailurl(os.path.basename(TargetFileName + ".png"))
return self.ExtMimeIcon('application/x-blender') return self.ExtMimeIcon('application/x-blender')


def fileinfo(self,name):
filename = self.mediapath(name)
def fileinfo(self, name):
filename = self.mediapath(name)
f, ext = os.path.splitext(name) f, ext = os.path.splitext(name)
if ext!='.type' and os.path.isfile(filename):
if ext != '.type' and os.path.isfile(filename):
info = {} info = {}
info['name'] = name info['name'] = name
info['size'] = os.path.getsize(filename) info['size'] = os.path.getsize(filename)
info['url'] = self.request.route_url('media_view', info['url'] = self.request.route_url('media_view',
name=name,
media_table=self.media_table,
uid=self.linked_id)
name=name,
media_table=self.media_table,
uid=self.linked_id)


mime = self.get_mimetype(name) mime = self.get_mimetype(name)
if IMAGE_TYPES.match(mime): if IMAGE_TYPES.match(mime):
@@ -448,8 +451,8 @@ class MediaUpload(MediaPath):
thumbext = ".jpg" thumbext = ".jpg"
if mime == "application/x-blender": if mime == "application/x-blender":
thumbext = ".png" thumbext = ".png"
if os.path.exists( thumb + thumbext ):
info['thumbnailUrl'] = self.thumbnailurl(name)+thumbext
if os.path.exists(thumb + thumbext):
info['thumbnailUrl'] = self.thumbnailurl(name) + thumbext
else: else:
info['thumbnailUrl'] = self.ExtMimeIcon(mime) info['thumbnailUrl'] = self.ExtMimeIcon(mime)
else: else:
@@ -457,10 +460,10 @@ class MediaUpload(MediaPath):
if not self.display_only: if not self.display_only:
info['deleteType'] = DELETEMETHOD info['deleteType'] = DELETEMETHOD
info['deleteUrl'] = self.request.route_url('media_upload', info['deleteUrl'] = self.request.route_url('media_upload',
sep='',
name='',
media_table=self.media_table,
uid=self.linked_id) + '/' + name
sep='',
name='',
media_table=self.media_table,
uid=self.linked_id) + '/' + name
if DELETEMETHOD != 'DELETE': if DELETEMETHOD != 'DELETE':
info['deleteUrl'] += '&_method=DELETE' info['deleteUrl'] += '&_method=DELETE'
return info return info
@@ -488,7 +491,7 @@ class MediaUpload(MediaPath):
n = self.fileinfo(f) n = self.fileinfo(f)
if n: if n:
filelist.append(n) filelist.append(n)
return { "files":filelist }
return {"files": filelist}


@view_config(request_method='DELETE', xhr=True, accept="application/json", renderer='json') @view_config(request_method='DELETE', xhr=True, accept="application/json", renderer='json')
def delete(self): def delete(self):
@@ -498,32 +501,32 @@ class MediaUpload(MediaPath):
except IOError: except IOError:
pass pass
except OSError: except OSError:
pass
pass
try: try:
os.remove(self.thumbnailpath(filename)) os.remove(self.thumbnailpath(filename))
except IOError: except IOError:
pass pass
except OSError:
except OSError:
pass pass
try: try:
os.remove(self.thumbnailpath(filename+".jpg"))
os.remove(self.thumbnailpath(filename + ".jpg"))
except IOError: except IOError:
pass pass
except OSError:
pass
except OSError:
pass
try: try:
os.remove(self.mediapath(filename)) os.remove(self.mediapath(filename))
except IOError: except IOError:
return False return False
return True return True
@view_config(request_method='POST', xhr=True, accept="application/json", renderer='json') @view_config(request_method='POST', xhr=True, accept="application/json", renderer='json')
def post(self): def post(self):
if self.request.matchdict.get('_method') == "DELETE": if self.request.matchdict.get('_method') == "DELETE":
return self.delete() return self.delete()
results = [] results = []
for name, fieldStorage in self.request.POST.items(): for name, fieldStorage in self.request.POST.items():
if isinstance(fieldStorage,unicode):
if isinstance(fieldStorage, unicode):
continue continue
result = {} result = {}
result['name'] = os.path.basename(fieldStorage.filename) result['name'] = os.path.basename(fieldStorage.filename)
@@ -531,63 +534,64 @@ class MediaUpload(MediaPath):
result['size'] = self.get_file_size(fieldStorage.file) result['size'] = self.get_file_size(fieldStorage.file)


if self.validate(result, fieldStorage.file): if self.validate(result, fieldStorage.file):
filename, file_extension = os.path.splitext( result['name'] )
local_filename = slugify( filename ) + file_extension
filename, file_extension = os.path.splitext(result['name'])
local_filename = slugify(filename) + file_extension
# Keep mime-type in .type file # Keep mime-type in .type file
with open( self.mediapath( local_filename ) + '.type', 'w') as f:
with open(self.mediapath(local_filename) + '.type', 'w') as f:
f.write(result['type']) f.write(result['type'])
# Store uploaded file # Store uploaded file
fieldStorage.file.seek(0) fieldStorage.file.seek(0)
with open( self.mediapath( local_filename ), 'wb') as f:
shutil.copyfileobj( fieldStorage.file , f)
with open(self.mediapath(local_filename), 'wb') as f:
shutil.copyfileobj(fieldStorage.file, f)


if re.match(IMAGE_TYPES, result['type']): if re.match(IMAGE_TYPES, result['type']):
result['thumbnailUrl'] = self.createthumbnail(local_filename) result['thumbnailUrl'] = self.createthumbnail(local_filename)
elif result['type']=='application/pdf':
elif result['type'] == 'application/pdf':
result['thumbnailUrl'] = self.pdfthumbnail(local_filename) result['thumbnailUrl'] = self.pdfthumbnail(local_filename)
elif result['type']=='image/svg+xml':
elif result['type'] == 'image/svg+xml':
result['thumbnailUrl'] = self.svgthumbnail(local_filename) result['thumbnailUrl'] = self.svgthumbnail(local_filename)
elif result['type'].startswith('application/vnd'): elif result['type'].startswith('application/vnd'):
result['thumbnailUrl'] = self.docthumbnail(local_filename) result['thumbnailUrl'] = self.docthumbnail(local_filename)
elif result['type']=='application/x-blender':
elif result['type'] == 'application/x-blender':
result['thumbnailUrl'] = self.blendthumbnail(local_filename) result['thumbnailUrl'] = self.blendthumbnail(local_filename)
else: else:
result['thumbnailUrl'] = self.ExtMimeIcon(result['type']) result['thumbnailUrl'] = self.ExtMimeIcon(result['type'])


result['deleteType'] = DELETEMETHOD result['deleteType'] = DELETEMETHOD
result['deleteUrl'] = self.request.route_url('media_upload', result['deleteUrl'] = self.request.route_url('media_upload',
sep='',
name='',
media_table=self.media_table,
uid=self.linked_id) + '/' + local_filename
sep='',
name='',
media_table=self.media_table,
uid=self.linked_id) + '/' + local_filename
result['url'] = self.request.route_url('media_view', result['url'] = self.request.route_url('media_view',
media_table=self.media_table,
uid=self.linked_id,
name=local_filename)
media_table=self.media_table,
uid=self.linked_id,
name=local_filename)
if DELETEMETHOD != 'DELETE': if DELETEMETHOD != 'DELETE':
result['deleteUrl'] += '&_method=DELETE' result['deleteUrl'] += '&_method=DELETE'
results.append(result) results.append(result)
return {"files":results}
return {"files": results}



@view_defaults(route_name='media_view') @view_defaults(route_name='media_view')
class MediaView(MediaPath): class MediaView(MediaPath):


def __init__(self,request):
def __init__(self, request):
self.request = request self.request = request
self.media_table = self.request.matchdict.get('media_table') self.media_table = self.request.matchdict.get('media_table')
self.linked_id = self.request.matchdict.get('uid') self.linked_id = self.request.matchdict.get('uid')


def mediapath(self,name):
def mediapath(self, name):
return self.get_mediapath(self.media_table, self.linked_id, name) return self.get_mediapath(self.media_table, self.linked_id, name)
@view_config(request_method='GET', http_cache = (EXPIRATION_TIME, {'public':True}))
@view_config(request_method='GET', http_cache=(EXPIRATION_TIME, {'public': True}))
def get(self): def get(self):
name = self.request.matchdict.get('name') name = self.request.matchdict.get('name')
self.request.response.content_type = self.get_mimetype(name) self.request.response.content_type = self.get_mimetype(name)


try: try:
self.request.response.body_file = open( self.mediapath(name), 'rb', 10000)
self.request.response.body_file = open(self.mediapath(name), 'rb', 10000)
except IOError: except IOError:
raise NotFound raise NotFound
return self.request.response return self.request.response


+ 798
- 729
jm2l/views.py
File diff suppressed because it is too large
View File


+ 3
- 2
setup.py View File

@@ -28,13 +28,14 @@ requires = [
'python-magic', 'python-magic',
'Pillow', 'Pillow',
'pyramid_exclog', 'pyramid_exclog',
'repoze.sendmail==4.1',
'repoze.sendmail',
'pyramid_mailer', 'pyramid_mailer',
'apscheduler', 'apscheduler',
'qrcode', 'qrcode',
'reportlab', 'reportlab',
'passlib', 'passlib',
'argon2_cffi'
'argon2_cffi',
'paginate'
] ]


setup(name='JM2L', setup(name='JM2L',


Loading…
Cancel
Save