ソースを参照

Integrate piernov code, added badge, missing deletion handling

master
コミット
b5c9e83bd9
19個のファイルの変更745行の追加427行の削除
  1. +7
    -2
      jm2l/__init__.py
  2. +4
    -1
      jm2l/auth.py
  3. +83
    -114
      jm2l/badge.py
  4. +3
    -2
      jm2l/forms.py
  5. +53
    -4
      jm2l/helpers.py
  6. +19
    -2
      jm2l/models.py
  7. +74
    -93
      jm2l/templates/Interventions/Interventions.mako
  8. +20
    -0
      jm2l/templates/Logistique/Logistique.mako
  9. +2
    -135
      jm2l/templates/Participant/list.mako
  10. +48
    -0
      jm2l/templates/Participant/list_orga.mako
  11. +136
    -0
      jm2l/templates/Participant/list_users.mako
  12. +60
    -20
      jm2l/templates/edit_event.mako
  13. +4
    -1
      jm2l/templates/helpers.mako
  14. +1
    -1
      jm2l/templates/jm2l.mako
  15. +5
    -3
      jm2l/templates/layout.mako
  16. +1
    -0
      jm2l/templates/login.mako
  17. +12
    -16
      jm2l/templates/view_event.mako
  18. +7
    -0
      jm2l/upload.py
  19. +206
    -33
      jm2l/views.py

+ 7
- 2
jm2l/__init__.py ファイルの表示

@@ -12,7 +12,7 @@ from .security import EntryFactory, groupfinder
from pyramid_mailer import mailer_factory_from_settings
from pyramid_mailer.message import Attachment, Message
import locale
from .helpers import Sejour_helpers
from .helpers import Sejour_helpers, Orga_helpers
from apscheduler.schedulers.background import BackgroundScheduler
# Database access imports
from pyramid.request import Request
@@ -23,6 +23,7 @@ import logging

def add_renderer_globals(event):
event['mytrip'] = Sejour_helpers(event)
event['myorga'] = Orga_helpers(event)

#@sched.scheduled_job('cron', day_of_week='sun', hour=22, minute=07)
def mailer_tasks(config):
@@ -119,6 +120,7 @@ def main(global_config, **settings):
config.add_route('pict_salle', '/salle_picture/{salle_id:(\d+)}')

config.add_route('list_users', '/ListParticipant')
config.add_route('list_orga', '/ListOrga')

# HTML Routes - Public
config.add_route('home', '/{year:(\d+/)?}')
@@ -133,8 +135,10 @@ def main(global_config, **settings):
## Events
config.add_route('event', '/event/{year:\d+}/{event_id:([\w-]+)?}')
config.add_route('link_event_user', '/MesJM2L/{year:\d+}/{intervention:[\s\w]+}/link_user')
config.add_route('link_event_tiers', '/MesJM2L/{year:\d+}/{intervention:[\s\w]+}/link_tiers')
config.add_route('link_event_tiers', '/MesJM2L/{year:\d+}/{intervention:[\s\w]+}/link_tiers')
config.add_route('delete_link', '/MesJM2L/{year:\d+}/{intervention:[\s\w]+}/delete_link')
config.add_route('edit_event', '/MesJM2L/{year:\d+}/{intervention:[\s\w]+}{sep:/*}{event_id:([\w-]+)?}')
config.add_route('delete_event', '/MesJM2L/{year:\d+}/{intervention:[\s\w]+}{sep:/*}{event_id:([\w-]+)?}/delete')
## Entities
config.add_route('entities', '/entities') #{sep:/*}{Nature:\w+?}')
@@ -156,6 +160,7 @@ def main(global_config, **settings):
config.add_route('jm2l', '/MesJM2L')
config.add_route('miam', '/MonMiam')
config.add_route('sejour', '/MonSejour')
config.add_route('orga', '/MonOrga')
config.add_route('modal', '/{year:\d+}/modal/{modtype:\w+}/{id:(\d+)}')
# Handle exchanges


+ 4
- 1
jm2l/auth.py ファイルの表示

@@ -11,7 +11,7 @@ import re

@view_config(route_name='auth', match_param="action=login", renderer="jm2l:templates/login.mako")
def login(request):
return {}
return {"comefrom":request.GET.get('from', "")}

@view_config(route_name='auth', match_param="action=forgot", renderer="jm2l:templates/login.mako")
def forgot(request):
@@ -75,6 +75,9 @@ def sign_in_out(request):
user.last_logged=datetime.datetime.now()
DBSession.merge(user)
headers = remember(request, user.uid)
if request.POST.get('redirect'):
return HTTPFound(location=request.POST.get('redirect'),
headers=headers)
return HTTPFound(location=request.route_url('jm2l'),
headers=headers)
else:


+ 83
- 114
jm2l/badge.py ファイルの表示

@@ -4,17 +4,19 @@ from pyramid.response import Response
import cStringIO as StringIO
from pyramid.view import view_config
from .models import User

from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.units import mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.units import mm
import qrcode
import subprocess
from .upload import MediaPath

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

def Ribbon35(DispUser, canvas):
canvas.saveState()
@@ -140,34 +142,48 @@ def JM2L_Logo(canvas, Position="Up"):
yearobject.textLines("2 0 1 5")
canvas.drawText(yearobject)

def Tiers_Logo(canvas, DispUser, LWidth=4, StartPos=None):
Border = 1
def Tiers_Logo(canvas, DispUser, StartPos=None):
Border = 0
if StartPos is None:
StartPos = ( WIDTH -70, 0 )
StartPos = ( 30 * mm, 2 )
StartX, StartY = StartPos
MaxX, MaxY = 34*mm, 18*mm
num = 0
canvas.setStrokeColorRGB(0.5,0.5,0.5)
Logos = filter(lambda x:x.ThumbLinks, DispUser.tiers)
Logos = filter(lambda x:x.ThumbLinks, DispUser.tiers)[:3]
# Should We compute a better positionning for logos ?
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) }
# draw overall border
# canvas.roundRect(StartX, StartY, MaxX, MaxY, radius=2, stroke=True)
for tiers in Logos:
FileName = tiers.ThumbLinks.pop().split("/")[-1]
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName)
# Should We compute a better positionning for logos ?
#PosX = StartX + (( (2.0*num+1) / 2*len(Logos) )*(ICONSIZE+Border) - ((ICONSIZE+Border)/2))
PosX = StartX + (num % LWidth) * (ICONSIZE+Border)
if len(Logos)<=LWidth:
# Middle of line
PosY = StartY + 0.5 * (ICONSIZE+Border)
else:
# in two or more lines
PosY = StartY + (num / LWidth) * (ICONSIZE+Border)
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName)
PosX = StartX + DicPos[len(Logos)][num][0] * MaxX - (ICONSIZE+Border)/2
PosY = StartY + DicPos[len(Logos)][num][1] * MaxY - (ICONSIZE+Border)/2
canvas.setLineWidth(.1)
canvas.drawImage(ImagePath,
PosX, PosY, ICONSIZE, ICONSIZE,\
preserveAspectRatio=True,
anchor='c',
mask=[0,3,0,3,0,3]
mask='auto'
)
canvas.roundRect(PosX, PosY, ICONSIZE, ICONSIZE, radius=2, stroke=True)
# draw icon border
# canvas.roundRect(PosX, PosY, ICONSIZE, ICONSIZE, radius=2, stroke=True)
num+=1

def QRCode(DispUser):
@@ -183,8 +199,10 @@ def QRCode(DispUser):

return qr.make_image()

@view_config(route_name='badge_user')

@view_config(route_name='badge_user', http_cache = (EXPIRATION_TIME, {'public':True}))
def badge_user(request):
isoutpng = request.params.get('png')
user_slug = request.matchdict.get('user_slug', None)
if user_slug is None or len(user_slug)==0:
raise HTTPNotFound(u"Cet utilisateur n'a pas été reconnu")
@@ -200,6 +218,7 @@ def badge_user(request):
pdfmetrics.registerFont(TTFont("Liberation", ttfFile))

pdf = StringIO.StringIO()
out_img = StringIO.StringIO()
c = canvas.Canvas( pdf, pagesize=(WIDTH, HEIGHT) )
c.translate(mm, mm)
@@ -215,24 +234,24 @@ def badge_user(request):
if DispUser.Staff:
# Staff
c.setFillColorRGB(.83,0,.33)
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1)
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1, stroke=0)
c.setFillColorRGB(1,1,1)
c.setFont('Liberation', 30)
c.drawCentredString(WIDTH/2, HEIGHT-26, "STAFF")
elif DispUser.is_Intervenant:
elif DispUser.is_Intervenant:
# Intervenant
c.setFillColorRGB(.3,.3,1)
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1)
c.setFillColorRGB(.21,.67,.78)
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1, stroke=0)
c.setFillColorRGB(1,1,1)
c.setFont('Liberation', 30)
c.drawCentredString(WIDTH/2, HEIGHT-26, "Intervenant")
c.drawCentredString(WIDTH/2, HEIGHT-26, "Intervenant")
else:
# Visiteur
c.setFillColorRGB(.8,.8,.8)
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1)
c.setFillColorRGB(.8,.8,.8)
c.rect(-3, HEIGHT-30, WIDTH, HEIGHT, fill=1, stroke=0)
c.setFillColorRGB(1,1,1)
c.setFont('Liberation', 30)
c.drawCentredString(WIDTH/2, HEIGHT-26, "Visiteur")
c.drawCentredString(WIDTH/2, HEIGHT-26, "Visiteur")

c.restoreState()
@@ -242,19 +261,19 @@ def badge_user(request):
# Feed Name and SurName
if len(DispUser.prenom) + len(DispUser.nom)>18:
if DispUser.pseudo:
c.drawCentredString(WIDTH/2, HEIGHT/2 + 4 * mm , "%s" % DispUser.prenom )
c.setFont('Courier', 17)
c.drawCentredString(WIDTH/2, HEIGHT/2 - 2 * mm , "%s" % DispUser.nom )
c.drawCentredString(WIDTH/2, HEIGHT/2 + 0 * mm , "%s" % DispUser.prenom )
c.setFont('Courier', 17)
c.drawCentredString(WIDTH/2, HEIGHT/2 - 8 * mm , "%s" % DispUser.nom )
else:
c.drawCentredString(WIDTH/2, HEIGHT/2 + 2 * mm , "%s" % DispUser.prenom )
c.setFont('Courier', 17)
c.drawCentredString(WIDTH/2, HEIGHT/2 - 6 * mm , "%s" % DispUser.nom )
c.drawCentredString(WIDTH/2, HEIGHT/2 + 4 * mm , "%s" % DispUser.prenom )
c.setFont('Courier', 17)
c.drawCentredString(WIDTH/2, HEIGHT/2 - 8 * mm , "%s" % DispUser.nom )
else:
c.drawCentredString(WIDTH/2, HEIGHT/2 + 0 * mm , "%s %s" % (DispUser.prenom, DispUser.nom) )

if DispUser.pseudo:
c.setFont("Helvetica-Oblique", 14)
c.drawCentredString(WIDTH/2, HEIGHT/2 - 8 * mm , "%s" % DispUser.pseudo )
c.setFont("Helvetica-Oblique", 18)
c.drawCentredString(WIDTH/2, HEIGHT/2 + 10 * mm , "%s" % DispUser.pseudo )
# Put QR code to user profile
c.drawInlineImage(QRCode(DispUser), \
@@ -263,12 +282,28 @@ def badge_user(request):
preserveAspectRatio=True, \
anchor='s')
Tiers_Logo(c, DispUser, 4, ( 30 * mm, 2 ))
Tiers_Logo(c, DispUser)
c.showPage()
c.save()
pdf.seek(0)
return Response(app_iter=pdf, content_type = 'application/pdf' )
pdf.seek(0)
if isoutpng:
OutPDF = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.pdf')
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]
subprocess.call(Command)
with open( OutPNG, 'rb') as pngfile:
out_img.write(pngfile.read())
out_img.seek(0)
return Response(app_iter=out_img, content_type = 'image/png' )
else:
return Response(app_iter=pdf, content_type = 'application/pdf' )

@view_config(route_name='badge_user1')
def badge_user1(request):
@@ -312,27 +347,7 @@ def badge_user1(request):
yearobject.textLines("2 0 1 5")
c.drawText(yearobject)
num = 0
for tiers in DispUser.tiers:
if tiers.ThumbLinks:
FileName = tiers.ThumbLinks.pop().split("/")[-1]
num+=1
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName)
if num<6:
c.drawImage(ImagePath,
WIDTH -5 - num * (ICONSIZE+5), 5, ICONSIZE, ICONSIZE,\
preserveAspectRatio=True,
anchor='c',
mask=[0,3,0,3,0,3])
else:
c.drawImage(ImagePath,
WIDTH -5 - (num-5) * (ICONSIZE+5), 5 + ICONSIZE, ICONSIZE, ICONSIZE,\
preserveAspectRatio=True,
anchor='c',
mask=[0,3,0,3,0,3])

Tiers_Logo(c, DispUser)
if 1:
Ribbon90(DispUser, c)
@@ -454,32 +469,11 @@ def badge_user2(request):
yearobject.setWordSpace(21)
yearobject.textLines("2 0 1 5")
c.drawText(yearobject)
num = 0
for tiers in DispUser.tiers:
if tiers.ThumbLinks:
FileName = tiers.ThumbLinks.pop().split("/")[-1]
num+=1
ImagePath = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, FileName)
if num<6:
c.drawImage(ImagePath,
WIDTH -5 - num * (ICONSIZE+5), 5, ICONSIZE, ICONSIZE,\
preserveAspectRatio=True,
anchor='c',
mask=[0,3,0,3,0,3])
else:
c.drawImage(ImagePath,
WIDTH -5 - (num-5) * (ICONSIZE+5), 5 + ICONSIZE, ICONSIZE, ICONSIZE,\
preserveAspectRatio=True,
anchor='c',
mask=[0,3,0,3,0,3])

if 1:
c.rotate(35)
offset_u=0
c.rotate(-35)
c.translate(-100, -70)
offset_u=0
if DispUser.Staff:
# Staff
c.setFillColorRGB(1,.2,.2)
@@ -489,7 +483,7 @@ def badge_user2(request):
c.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+5, "STAFF")
elif DispUser.is_Intervenant:
# Intervenant
c.setFillColorRGB(.3,.3,1)
c.setFillColorRGB(.21,.3,1)
c.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1)
c.setFillColorRGB(1,1,1)
c.setFont('Liberation', 15)
@@ -501,32 +495,6 @@ def badge_user2(request):
c.setFillColorRGB(0,0,0)
c.setFont('Liberation', 12)
c.drawCentredString(WIDTH/2-10, HEIGHT/2-offset_u+7, "Visiteur")

if 0:
c.rotate(90)
offset_u=111
if DispUser.Staff:
# Staff
c.setFillColorRGB(1,.2,.2)
c.rect(-5, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1)
c.setFillColorRGB(1,1,1)
c.setFont('Liberation', 30)
c.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+5, "STAFF")
elif DispUser.is_Intervenant:
# Intervenant
c.setFillColorRGB(.3,.3,1)
c.rect(0, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1)
c.setFillColorRGB(1,1,1)
c.setFont('Liberation', 15)
c.drawCentredString(WIDTH/2-15, HEIGHT/2-offset_u+10, "Intervenant")
else:
# Visiteur
c.setFillColorRGB(.8,.8,.8)
c.rect(-5, HEIGHT/2-offset_u, WIDTH, 10*mm, fill=1)
c.setFillColorRGB(0,0,0)
c.setFont('Liberation', 19)
c.drawCentredString(WIDTH/2, HEIGHT/2-offset_u+7, "Visiteur")

c.restoreState()
@@ -551,7 +519,8 @@ def badge_user2(request):
c.setFont("Helvetica-Oblique", 14)
c.drawCentredString(WIDTH/2, HEIGHT/2 - 8 * mm , "\"%s\"" % DispUser.pseudo )
#c.restoreState()
# Put Tiers logos
Tiers_Logo(c, DispUser)
# Put QR code to user profile
c.drawInlineImage(img, WIDTH - 20 * mm - 7, 0, 20 * mm, 20 * mm, preserveAspectRatio=True, anchor='s')


+ 3
- 2
jm2l/forms.py ファイルの表示

@@ -156,8 +156,9 @@ class ConfUpdateForm(ConfCreateForm):
class SalleForm(MyBaseForm):
year_uid = SelectField(u'Année', coerce=int)
phy_salle_id = SelectField('Salle Physique', coerce=int)
place_type = SelectField('Type', choices=[('Conference','Conference'),
('Stand','Stand'), ('Ateliers','Ateliers'), ('Autres','Autres') ])
place_type = SelectField('Type', choices=[('Conference',u'Conférence'),
('Stand','Stand'), ('Atelier','Atelier'), ('Table ronde','Table ronde'),
('MAO','MAO'), ('Repas','Repas / Snack'), ('Autres','Autres') ])
name = TextField('Nom de la salle', [validators.Length(min=1, max=40)],
filters=[strip_filter])
description = TextAreaField('Description',


+ 53
- 4
jm2l/helpers.py ファイルの表示

@@ -1,8 +1,9 @@
# -*- coding: utf8 -*-
from .models import DBSession, JM2L_Year, Sejour
from datetime import timedelta, datetime
import itertools

class Sejour_helpers:
class DummySejour(object):
def __init__(self, event):
self.Me = event['request'].user
@@ -13,8 +14,13 @@ class Sejour_helpers:
.filter(Sejour.user_id==self.Me.uid)\
.filter(Sejour.for_year==self.CurrentEventYear.year_uid)\
.first()
if self.Sejour and self.Sejour.arrival_time is None:
self.Sejour = None

class Sejour_helpers(DummySejour):

def __init__(self, event):
super(Sejour_helpers, self).__init__(event)
if self.Sejour and self.Sejour.arrival_time is None:
self.Sejour = None
def StartEvent(self):
# This function return the start of the event
@@ -98,4 +104,47 @@ class Sejour_helpers:
else:
return ""
else:
return ""
return ""

class Orga_helpers(DummySejour):

def __init__(self, event):
super(Orga_helpers, self).__init__(event)
self.Orga_tasks = [
u"Participer au fléchage de l'événement.",
u"Faire les courses.",
u"Participer à l'accueil des visiteurs.",
u"Accompagner, Guider les visiteurs.",
u"Accompagner, Guider les intervenants.",
u"Déplacer le matériel / le mobilier",
u"Monter / Démonter les stands",
u"Participer à la préparation / à vider les poubelles",
u"Participer à la préparation / à vider les salles de conférence",
u"Participer à la prise de son/vidéo des conférenciers.",
u"Participer à poser, à repartir, à enlever les ralonges éléctriques.",
u"Participer à poser, à repartir, à enlever les câbles réseau / le Wifi.",
u"Remplir les frigos, s'assurer qu'ils soient toujours pleins. (Boissons)",
u"Participer à la distribution des repas.",
u"Faire du café et s'assurer de sa disponibilité.",
u"Participer à la publication / au montage des vidéos des conférenciers.",
u"Autres"
]

def IsChecked(self, nb):
nb = 2**nb
if self.Sejour:
if self.Sejour.orga_part & nb == nb:
return "checked=\"checked\""
else:
return ""
else:
return ""
def ChoosedList(self):
""" Return choice validated by user """
ListOrga = []
for num in range(0,len(self.Orga_tasks)):
curs = 2**num
if self.Sejour.orga_part & curs == curs:
ListOrga.append(self.Orga_tasks[num])
return ListOrga

+ 19
- 2
jm2l/models.py ファイルの表示

@@ -175,6 +175,13 @@ class User(Base):
return DBSession.query(Event).join(User_Event) \
.filter(User_Event.user_uid==self.uid) \
.filter(Event.for_year==year).count()
def year_events(self, EventType='All', year=2015):
if EventType=='All':
return filter(lambda e: e.for_year==year, self.events)
else:
return filter(lambda e: e.for_year==year and e.event_type==EventType, self.events)

@property
def my_hash(self):
@@ -308,6 +315,7 @@ class Role_Tiers(Base):
tiers_uid = Column(Integer, ForeignKey('tiers.uid'))
tiers = relationship(Tiers, backref=backref("roles_assoc") )
tiers_role = Column(Enum('Exposant', 'Sponsor', 'Donateur'))
event_uid = Column(Integer, default=None) # Optionnal link with Event

class User_Tiers(Base):
""" Créer le lien entre la personne et le tiers en fonction de l'année"""
@@ -364,7 +372,7 @@ class Salles(Base):
phy_salle_id = Column(Integer, ForeignKey('phy_salle.uid'))
year_uid = Column(Integer, ForeignKey('jm2l_year.year_uid'), default=CurrentYear)
name = Column(Unicode(40))
place_type = Column(Enum('Conference', 'Stand', 'Ateliers', 'Autres'))
place_type = Column(Enum('Conference', 'Stand', 'Atelier', 'Table ronde', 'MAO', 'Repas', 'Autres'))
description = Column(UnicodeText) # Description du matériel disponible
created = Column(DateTime, default=datetime.datetime.now)
last_change = Column(DateTime, default=datetime.datetime.now)
@@ -557,6 +565,8 @@ class Sejour(Base):
repas = Column(Integer)
repas_allerg = Column(Unicode(100))
repas_contr = Column(Unicode(100))
orga_part = Column(Integer, default=0)
#travel_detail = Column(UnicodeText)
created = Column(DateTime, default=datetime.datetime.now)
last_change = Column(DateTime, default=datetime.datetime.now)
@@ -572,7 +582,7 @@ class Event(Base):
for_year = Column(Integer, ForeignKey('jm2l_year.year_uid')) # link JM2L_Year
name = Column(Unicode(100), nullable=False)
slug = Column(Unicode(100))
event_type = Column(Enum('Stand', 'Table ronde', 'Atelier', 'Concert', 'Conference', 'Repas'))
event_type = Column(Enum('Conference', 'Stand', 'Atelier', 'Table ronde', 'MAO', 'Repas', 'Autres'))
start_time = Column(DateTime, default=datetime.datetime.now)
end_time = Column(DateTime, default=datetime.datetime.now)
description = Column(UnicodeText)
@@ -601,6 +611,13 @@ class Event(Base):
return DBSession.query(cls)\
.filter(cls.slug == slug).first()

def get_linked_tiers(self):
ListLink = DBSession.query(Role_Tiers.tiers_uid) \
.filter(Role_Tiers.year_uid==self.for_year) \
.filter(Role_Tiers.tiers_role=="Exposant") \
.filter(Role_Tiers.event_uid==self.uid)
return DBSession.query(Tiers).filter( Tiers.uid.in_( ListLink ) )

@property
def video(self):
return DBSession.query(Media) \


+ 74
- 93
jm2l/templates/Interventions/Interventions.mako ファイルの表示

@@ -7,22 +7,64 @@
<th>Section</th>
<th>Statut</th>
</tr>
</thead>
</thead>
<tbody>
<tr>
<td>Conférences</td> <td style="text-align:center">Aucune</td>
<td>Conférences</td> <td style="text-align:center">
% if len( request.user.year_events('Conference') ):
% for evt in request.user.year_events('Conference'):
% endfor
<a href="/MesJM2L/${evt.for_year}/Conference/${evt.slug}">${evt.name} à ${evt.start_time.strftime('%Hh%M')}</a>
% else:
Aucune
% endif
</td>
</tr>
<tr>
<td>Stands</td> <td style="text-align:center">Aucune</td>
<td>Stands</td> <td style="text-align:center">
% if len( request.user.year_events('Stand') ):
% for evt in request.user.year_events('Stand'):
<a href="/MesJM2L/${evt.for_year}/Stand/${evt.slug}">${evt.name}</a>
% endfor
% else:
Aucune
% endif
</td>
</tr>
<tr>
<td>Ateliers</td> <td style="text-align:center">Aucune</td>
<td>Ateliers</td> <td style="text-align:center">
% if len( request.user.year_events('Atelier') ):
% for evt in request.user.year_events('Atelier'):
<a href="/MesJM2L/${evt.for_year}/Atelier/${evt.slug}">${evt.name} à ${evt.start_time.strftime('%Hh%M')}</a>
% endfor
% else:
Aucune
% endif
</td>
</tr>
<tr>
<td>Tables Ronde</td> <td style="text-align:center">Aucune</td>
<td>Tables Ronde</td> <td style="text-align:center">
% if len( request.user.year_events('Table ronde') ):
% for evt in request.user.year_events('Table ronde'):
<a href="/MesJM2L/${evt.for_year}/Table_ronde/${evt.slug}">${evt.name} à ${evt.start_time.strftime('%Hh%M')}</a>
% endfor
% else:
Aucune
% endif
</td>
</tr>
<tr>
<td>Organisation</td> <td style="text-align:center">Aucune</td>
<td>Organisation</td> <td style="text-align:left">
% if myorga.Sejour and myorga.Sejour.orga_part:
<ul>
% for orga_task in myorga.ChoosedList():
<li>${orga_task}</li>
% endfor
</ul>
% else:
<center>Aucune</center>
% endif
</td>
</tr>
</tbody>
</table>
@@ -62,6 +104,7 @@ elif Type=='T':
CurEventType = "Table ronde"
CurLink = "Ma_Table_Ronde"
%>
% if Type!='O':
<fieldset>
<legend class="lowshadow">Vos ${CurTitles} programmés pour 2015</legend>
<%
@@ -71,6 +114,7 @@ NothingTitle = u"Vous n'avez pas sollicité d'intervention %s." % CurEvent
%>
${helpers.show_Interventions(Selection, "Sujet", NothingTitle )}
</fieldset>
% endif

% if Type=='C':
<p>
@@ -118,109 +162,46 @@ NothingTitle = u"Vous n'avez pas sollicité d'intervention %s." % CurEvent
% endif

% if Type=='O':
<form id="OrgaForm" action="/MonOrga" method="POST">

<fieldset>
<legend>Participer à l'organisation</legend>
<strong>Une autre façon de participer !</strong>
<p>
Comme vous vous en doutez, la meilleure organisation qui existe,
c'est celle où chacun apporte sa contribution.
</p>
</p>
<p>
Dans ce genre d'évenement nous avons besoin de bras et de bonnes volontés.
Vous pouvez nous aider en vous inscrivant en tant que "bénévole du jour" sur un
certains nombre de missions :
</p>
<p>
Et oui, il existe plein de façon de participer aux JM2L, choisissez :
</p>
</fieldset>
<ul>
<li>
<label class="checkbox">
<input type="checkbox"> Participer au fléchage de l'événement.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Faire les courses.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Participer à l'accueil des visiteurs.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Accompagner, Guider les visiteurs.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Accompagner, Guider les intervenants.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Déplacer le matériel / le mobilier
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Monter / Démonter les stands
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Participer à la préparation / à vider les poubelles
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Participer à la préparation / à vider les salles de conférence
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Participer à la prise de son/vidéo des conférenciers.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Participer à poser, à repartir, à enlever les ralonges éléctriques.
</label>
</li>
% for num, item in enumerate(myorga.Orga_tasks):
<li>
<label class="checkbox">
<input type="checkbox"> Participer à poser, à repartir, à enlever les câbles réseau / le Wifi.
<input id="O${num}" ${myorga.IsChecked(num)|n} name="O${num}" type="checkbox"> ${item}
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Remplir les frigos, s'assurer qu'ils soient toujours pleins. (Boissons)
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Participer à la distribution des repas.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Faire du café et s'assurer de sa disponibilité.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Participer à la publication / au montage des vidéos des conférenciers.
</label>
</li>
<li>
<label class="checkbox">
<input type="checkbox"> Autres
</label>
</li>
<ul>
% endfor
</ul>
<p>
Avant l'évenement, et en fonction des cases cochés, les coordinateurs metterons à jour le planning et vous receverez les instructions par mail.
</p>
<div class="span2 offset5">
<input class="btn btn-primary" type="submit" value="Enregistrer !" />
</div>
</fieldset>
</form>
% endif
<a href="/MesJM2L/2015/${CurEventType.replace(' ','_')}">Je souhaite ajouter ${CurTitle} pour les JM2L 2015 !</a>
% if Type!='O':
<a href="/MesJM2L/2015/${CurEventType.replace(' ','_')}">Je souhaite ajouter ${CurTitle} pour les JM2L 2015 !</a>

<fieldset>
<legend class="lowshadow">Historique</legend>
<%


+ 20
- 0
jm2l/templates/Logistique/Logistique.mako ファイルの表示

@@ -303,6 +303,26 @@ ListWrap = ["Co-voiturage",u"Hébergement","Matos"]

<fieldset>
<legend>Mon Badge</legend>
<p>
Rendez-vous à l'accueil pour retirer votre badge !
<br>
Pour que tout se passe pour le mieux dans l'organisation de l'événement, nous vous invitons à :
<ul>
<li>Renseigner et valider vos horaires d'arrivée / de départ dans la section "Mon Séjour".</li>
<li>Procéder à la réservation de vos repas dans la section "Miam" !</li>
<li>Préparer et réserver vos transports au plus vite dans la section "Mes Frais".</li>
</ul>
</p>
<div class="center">
<a href="/user/${request.user.slug}/badge"> Télécharger mon badge (PDF)</a>
<br>
</div>
<br>
<div align="center">
<a href="/user/${request.user.slug}/badge">
<img style="border:solid black 1px;" src="/user/${request.user.slug}/badge?png=true" alt="Badge de ${request.user.slug}" />
</a>
</div>
</fieldset>
</%def>
## -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


+ 2
- 135
jm2l/templates/Participant/list.mako ファイルの表示

@@ -111,139 +111,6 @@ $('table').each(function(){
});

</script>
<%
import datetime
import itertools
now = datetime.datetime.now()
%>

<form class="filterform" action="#">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th style="width:7em;text-align:center;"></th>
<th style="width:5em;text-align:center;">Visite</th>
<th style="width:7em;text-align:center;">Activité</th>
<th style="text-align:center;">Arrivée prévue</th>
<th style="width:5em;text-align:center;">${DicRepas['Ven']} Repas Vendredi</th>
<th style="width:5em;text-align:center;">${DicRepas['Midi']} Repas Midi </th>
<th style="width:5em;text-align:center;">${DicRepas['Soir']} Repas Soir</th>
<th style="text-align:center;">Départ prévu</th>
<th style="text-align:center;">Notes</th>
</tr>
</thead>
<tbody id="list">
% for u, s in Users:
<tr>
<td style="text-align:center;">
<span class="data">${u.slug}</span>
<a href="/MesJM2L?user=${u.uid}">${u.nom} ${u.prenom}</a><br />
<span style="align:center">
${u.vote_logo}
<a href="mailto:${u.mail}">
<i class="icon-envelope"></i>
</a>
% if u.Staff==1:
<a href="javascript:alert('${u.nom}, ${u.prenom}\nStaff JM2L');">
<i class="icon-star"></i>
</a>
% endif
% if u.Staff==0:
<a href="javascript:alert('${u.nom}, ${u.prenom}\nIntervenant');">
<i class="icon-user"></i>
</a>
% endif
% if u.active==0:
<a href="javascript:alert('${u.nom}, ${u.prenom}\nInactive');">
<i class="icon-ban-circle"></i>
</a>
% endif
% if u.phone:
<a href="javascript:alert('${u.nom}, ${u.prenom}\n${u.phone}');">
<i class="icon-headphones"></i>
</a>
% endif
<a href="/user/${u.slug}/badge">
<i class="icon-qrcode"></i>
</a>
</span>
</td>
<td style="text-align:center;">
<span class="data">${(now - u.last_logged).days}</span>${(now - u.last_logged).days} j
</td>
<td style="text-align:center;">
% if u.events:
<span class="data">${len(u.events)}</span>
<select style="width:7em;">
<option><strong>${len(u.events)} Intérv.</strong></option>
% for y, g in itertools.groupby(sorted(u.events, key=lambda k:k.for_year, reverse=True), key=lambda k:k.for_year):
<OPTGROUP LABEL="${y}">
% for event in g:
${event.for_year}
<OPTION onclick="location='/event/${event.for_year}/${event.slug}';">${event.event_type} - ${event.name}</OPTION>
% endfor
</OPTGROUP>
% endfor
</select>
%else:
<span class="data"></span>
<i> - </i>
% endif
</td>
<td style="text-align:center;">
% if s and s.arrival_time:
<span class="data">${s.arrival_time.strftime('%m/%d/%Y %H:%M:%S')}</span>
${s.arrival_time.strftime('%a %d <strong>%H:%M</strong>') | n}<br/>
${s.arrival_place}
% if s.arrival_text:
- NB: <strong>${s.arrival_text}</strong>
% endif
%else:
<span class="data"></span>
<i>Pas d'informations</i>
% endif
</td>
% if s and s.repas:
% for i, d in enumerate(['Ven Soir', 'Sam midi', 'Sam soir']):
<td style="text-align:center">
% if (s.repas & 2**i):
<span class="data">Oui</span>Oui
% else:
<span class="data">Non</span>Non
% endif
</td>
% endfor
%else:
<td style="text-align:center;" colspan="3">
<i>Pas d'informations</i>
</td>
% endif
</td>
<td style="text-align:center;">
% if s and s.depart_time:
<span class="data">${s.depart_time.strftime('%m/%d/%Y %H:%M:%S')}</span>
${s.depart_time.strftime('%a %d <strong>%H:%M</strong>') | n}<br/>
${s.depart_place}
% if s.depart_text:
- NB: <strong>${s.arrival_text}</strong>
% endif
%else:
<span class="data"></span>
<i>Pas d'informations</i>
% endif
</td>
<td>
% if s:
% if s.repas_allerg:
<u>Allergies</u> : ${s.repas_allerg}<br/>
% endif
% if s.repas_contr:
<u>Contraintes</u> : ${s.repas_contr}<br/>
% endif
% endif
</td>
</tr>
% endfor
</tbody>
</table>
</form>
${next.body()}


+ 48
- 0
jm2l/templates/Participant/list_orga.mako ファイルの表示

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
<%inherit file="jm2l:templates/Participant/list.mako"/>

<%
import unicodedata
%>

<%def name="cssAddOn()">
${parent.cssAddOn()}
<style>
input[type=text]{width:10em;}
</style>
</%def>

<form class="filterform" action="#">
<div style="overflow:auto">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th style="width:10em;text-align:center;"></th>
% for u, s in Users:
% if s and s.orga_part and s.orga_part > 0:
<th style="text-align:center;"><a href="/MesJM2L?user=${u.uid}">${u.nom} ${u.prenom} ${s.orga_part}</a></th>
% endif
% endfor
</tr>
</thead>
<tbody id="list">
% for i, title in enumerate(myorga.Orga_tasks):
<tr>
<td style="text-align:center;"><span class="data">${unicodedata.normalize('NFKD', title).encode('ASCII', 'ignore').lower()}</span>${title}</td>
% for u, s in Users:
% if s and s.orga_part and s.orga_part > 0:
<td style="text-align:center;">
% if s and s.orga_part:
% if (s.orga_part & 2**i):
Oui
% endif
% endif
</td>
% endif
% endfor
</tr>
% endfor
</tbody>
</table>
</div>
</form>

+ 136
- 0
jm2l/templates/Participant/list_users.mako ファイルの表示

@@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
<%inherit file="jm2l:templates/Participant/list.mako"/>

<%
import datetime
import itertools
now = datetime.datetime.now()
%>

<form class="filterform" action="#">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th style="width:7em;text-align:center;"></th>
<th style="width:5em;text-align:center;">Visite</th>
<th style="width:7em;text-align:center;">Activité</th>
<th style="text-align:center;">Arrivée prévue</th>
<th style="width:5em;text-align:center;">${DicRepas['Ven']} Repas Vendredi</th>
<th style="width:5em;text-align:center;">${DicRepas['Midi']} Repas Midi </th>
<th style="width:5em;text-align:center;">${DicRepas['Soir']} Repas Soir</th>
<th style="text-align:center;">Départ prévu</th>
<th style="text-align:center;">Notes</th>
</tr>
</thead>
<tbody id="list">
% for u, s in Users:
<tr>
<td style="text-align:center;">
<span class="data">${u.slug}</span>
<a href="/MesJM2L?user=${u.uid}">${u.nom} ${u.prenom}</a><br />
<span style="align:center">
${u.vote_logo}
<a href="mailto:${u.mail}">
<i class="icon-envelope"></i>
</a>
% if u.Staff==1:
<a href="javascript:alert('${u.nom}, ${u.prenom}\nStaff JM2L');">
<i class="icon-star"></i>
</a>
% endif
% if u.Staff==0:
<a href="javascript:alert('${u.nom}, ${u.prenom}\nIntervenant');">
<i class="icon-user"></i>
</a>
% endif
% if u.active==0:
<a href="javascript:alert('${u.nom}, ${u.prenom}\nInactive');">
<i class="icon-ban-circle"></i>
</a>
% endif
% if u.phone:
<a href="javascript:alert('${u.nom}, ${u.prenom}\n${u.phone}');">
<i class="icon-headphones"></i>
</a>
% endif
</span>
</td>
<td style="text-align:center;">
<span class="data">${(now - u.last_logged).days}</span>${(now - u.last_logged).days} j
</td>
<td style="text-align:center;">
% if u.events:
<span class="data">${len(u.events)}</span>
<select style="width:7em;">
<option><strong>${len(u.events)} Intérv.</strong></option>
% for y, g in itertools.groupby(sorted(u.events, key=lambda k:k.for_year, reverse=True), key=lambda k:k.for_year):
<OPTGROUP LABEL="${y}">
% for event in g:
${event.for_year}
<OPTION onclick="location='/event/${event.for_year}/${event.slug}';">${event.event_type} - ${event.name}</OPTION>
% endfor
</OPTGROUP>
% endfor
</select>
%else:
<span class="data"></span>
<i> - </i>
% endif
</td>
<td style="text-align:center;">
% if s and s.arrival_time:
<span class="data">${s.arrival_time.strftime('%m/%d/%Y %H:%M:%S')}</span>
${s.arrival_time.strftime('%a %d <strong>%H:%M</strong>') | n}<br/>
${s.arrival_place}
% if s.arrival_text:
- NB: <strong>${s.arrival_text}</strong>
% endif
%else:
<span class="data"></span>
<i>Pas d'informations</i>
% endif
</td>
% if s and s.repas:
% for i, d in enumerate(['Ven Soir', 'Sam midi', 'Sam soir']):
<td style="text-align:center">
% if (s.repas & 2**i):
<span class="data">Oui</span>Oui
% else:
<span class="data">Non</span>Non
% endif
</td>
% endfor
%else:
<td style="text-align:center;" colspan="3">
<i>Pas d'informations</i>
</td>
% endif
</td>
<td style="text-align:center;">
% if s and s.depart_time:
<span class="data">${s.depart_time.strftime('%m/%d/%Y %H:%M:%S')}</span>
${s.depart_time.strftime('%a %d <strong>%H:%M</strong>') | n}<br/>
${s.depart_place}
% if s.depart_text:
- NB: <strong>${s.arrival_text}</strong>
% endif
%else:
<span class="data"></span>
<i>Pas d'informations</i>
% endif
</td>
<td>
% if s:
% if s.repas_allerg:
<u>Allergies</u> : ${s.repas_allerg}<br/>
% endif
% if s.repas_contr:
<u>Contraintes</u> : ${s.repas_contr}<br/>
% endif
% endif
</td>
</tr>
% endfor
</tbody>
</table>
</form>

+ 60
- 20
jm2l/templates/edit_event.mako ファイルの表示

@@ -21,9 +21,13 @@
<div class="span10 offset1">

<div id="SalleCarousel">
${helpers.show_salles( Salles, form.salle_uid.data or form.salle_uid.choices[0][0] )}
${helpers.show_salles( Salles, form.salle_uid.data or form.salle_uid.choices and form.salle_uid.choices[0][0] )}
</div>

% if event.for_year==2015 and request.user and (request.user.Staff or request.user in event.intervenants):
<a class="btn btn-danger pull-right" type="button" href="${event.uid}/delete">
<i class="icon-remove icon-white"></i> Supprimer
</a>
%endif
% if 'uid' in form._fields:
<div class="borderboxtime">
${event.start_time.strftime('%d %b %Y').decode('utf-8')} -
@@ -53,7 +57,7 @@
% endif
</ul>
</div>
<a href="/event/${event.for_year}/${event.slug}" class="pull-right">Voir la version publiée de cet évenement</a>
<a href="/event/${event.for_year}/${event.slug}" class="pull-right">Voir la version publiée de cet évenement</a>
<br clear="both">
%endif
<fieldset>
@@ -105,12 +109,20 @@ DicForm = {
</p>
% endif

<%def name="callback_Del_Summary(Entity)"> \
<a class="btn btn-danger btn-mini pull-right" type="button" href="delete_link?tid=${Entity.uid}&uid=${event.uid}">
<i class="icon-remove icon-white"></i>
</a>
</%def>

% if 'uid' in form._fields:

<fieldset>
<legend>Indiquez l'entité dont vous faites la promotion :</legend>
<legend id="Tiers">Indiquez l'entité dont vous faites la promotion :</legend>
<p>
<form action="/MesJM2L/${form.for_year.data}/${form.event_type.data}/link_tiers" method="POST">
${helpers.show_SummaryEntities( event.get_linked_tiers(), callback_Del_Summary )}

<form action="/MesJM2L/${form.for_year.data}/${form.event_type.data.replace(' ', '_')}/link_tiers" method="POST">
${formAddT.event_uid}
<input type="hidden" id="tiers" name="tiers" style="width:20em;"
class="form-control select2-offscreen" tabindex="-1">
@@ -120,27 +132,55 @@ DicForm = {
</button>
</form>
NB : Notez que les entités séléctionnées apparaissent dans les exposants.
</p>

</p>
</fieldset>


<fieldset>
<legend>Ajouter vos co-intervenants</legend>
<p>
Vous avez la possibilité d'être plusieurs pour un même évenement.<br>
Chacun des intervenants doit être inscrit sur le site.
<form action="/MesJM2L/${form.for_year.data}/${form.event_type.data}/link_user" method="POST">
${formAdd.event_uid}
<input type="hidden" id="intervenant" name="intervenant" style="width:20em;"
class="form-control select2-offscreen" tabindex="-1">
</input>
<button type="submit" class="btn btn-primary" />
<i class="icon-plus icon-white"></i> Ajouter cet intervenant
</button>
</form>
NB : Notez que les intervenants d'un même évenement ont tous les droits de modification.
</p>
<table>
<tr>
<td>
<p>
Vous avez la possibilité d'être plusieurs pour un même évenement.<br>
Chacun des intervenants doit être inscrit sur le site.
<form action="/MesJM2L/${form.for_year.data}/${form.event_type.data.replace(' ', '_')}/link_user" method="POST">
${formAdd.event_uid}
<input type="hidden" id="intervenant" name="intervenant" style="width:20em;"
class="form-control select2-offscreen" tabindex="-1">
</input>
<button type="submit" class="btn btn-primary" />
<i class="icon-plus icon-white"></i> Ajouter cet intervenant
</button>
</form>
NB : Notez que les intervenants d'un même évenement ont tous les droits de modification.
</p>
</td>
<td>
<u>Les Intrevenants:</u>
<ul>
% if event.intervenants.count()==0:
<i><b>Aucun</b></i>
% else:
% for num, iterv in enumerate(event.intervenants):
<li style="height:2em">
<strong><a href="/user/${iterv.slug}">${iterv.prenom} ${iterv.nom}</a></strong>.
% if iterv.pseudo:
(${iterv.pseudo})
%endif
% if iterv!=request.user:
<a class="btn btn-danger btn-mini" type="button" href="delete_interv?uid=${iterv.uid}&eid=${event.uid}">
<i class="icon-remove icon-white"></i>
</a>
%endif
</li>
% endfor
% endif
</ul>
</td>
</tr>
</table>

</fieldset>
<div class="clearfix">&nbsp;</div>


+ 4
- 1
jm2l/templates/helpers.mako ファイルの表示

@@ -407,12 +407,15 @@ TabJs = {'select':[], 'desc':[]}
## -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
## Wrapper pour les badges des entités utilisateur
## -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
<%def name="show_SummaryEntities(ListEntities)"> \
<%def name="show_SummaryEntities(ListEntities, callback=None)"> \
<ul class="thumbnails">
% for tiers in ListEntities:
<% Entity = tiers.get_entity_type %>
<li class="span3 tiers">
<div class="media">
% if callback:
${callback(tiers)}
% endif
<a class="pull-left" href="/entity/${Entity.entity_type}/${tiers.slug}">
% if tiers.ThumbLinks:
<img class="media-object" src="${tiers.ThumbLinks.pop()}" alt="${tiers.slug}" />


+ 1
- 1
jm2l/templates/jm2l.mako ファイルの表示

@@ -59,7 +59,7 @@
<div class="row-fluid">
<div class="span10 offset1">
<div class="tabbable" id="main_tab">
<ul class="nav nav-tabs" 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><a href="#Sejour" id="Map_Sejour" data-toggle="tab">Mon S&eacute;jour</a></li>
<li><a href="#Logistique" id="Map_Logistique" data-toggle="tab">Logistique</a></li>


+ 5
- 3
jm2l/templates/layout.mako ファイルの表示

@@ -108,7 +108,7 @@ ${helpers.uploader_js()}
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
Archives&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu">
<ul class="dropdown-menu pull-right" style="min-width:0">
% for tmpyear in range(2015, 2005, -1):
% if tmpyear!=2014:
<li><a href="/year/${tmpyear}">${tmpyear}</a></li>
@@ -126,13 +126,15 @@ ${helpers.uploader_js()}
% endif
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<ul class="dropdown-menu pull-right">
% if request.user:
% if request.user.Staff:
<li><a href="/Staff">Partie Staff</a></li>
<li><a href="/ListParticipant">Gérer les intervenants</a></li>
<li><a href="/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="/ListOrga">Participations à l'orga</a></li>
<li role="separator" class="divider"></li>
% endif
<li><a href="/sign/out">Me déconnecter</a></li>
% else:


+ 1
- 0
jm2l/templates/login.mako ファイルの表示

@@ -14,6 +14,7 @@
<hr>
<input name="username" type="text" class="input-block-level" placeholder="Nom">
<input name="password" type="password" class="input-block-level" placeholder="Mot de passe">
<input name="redirect" type="hidden" value="${comefrom}">
<div class="center">
<button class="btn btn-large btn-primary btn-block" type="submit">S'identifier</button>
</div>


+ 12
- 16
jm2l/templates/view_event.mako ファイルの表示

@@ -8,24 +8,18 @@
</%def>
<div class="row-fluid">
<div class="span10 offset1">
<div id="SalleCarousel">
${helpers.show_salles( Salles, event.Salle.salle_id )}
</div>
<div id="SalleCarousel">
${helpers.show_salles( Salles, event.Salle.salle_id )}
</div>
<strong>${event.event_type}</strong>:
<div class="borderboxtime">
${event.start_time.strftime('%d %b %Y').decode('utf-8')} -
${event.start_time.strftime('%H:%M')} à ${event.end_time.strftime('%H:%M')}
##%if event.Salle:
## - <strong>Salle</strong>: ${event.Salle.name}
##%endif
</div>
##%if event.event_uid:
## <a href="http://jm2l.linux-azur.org/node/${event.event_uid}">Link</a> -
##%endif
<div class="borderboxtime">
${event.start_time.strftime('%d %b %Y').decode('utf-8')} -
${event.start_time.strftime('%H:%M')} à ${event.end_time.strftime('%H:%M')}
</div>
% if event.for_year==2015 and request.user and (request.user.Staff or request.user in event.intervenants):
<a href="/MesJM2L/${event.for_year}/${event.event_type}/${event.slug}">Modifier</a>
<a href="/MesJM2L/${event.for_year}/${event.event_type.replace(' ', '_')}/${event.slug}">Modifier</a>
% elif request.user and request.user.Staff:
<a href="/MesJM2L/${event.for_year}/${event.event_type}/${event.slug}">Editer</a>
<a href="/MesJM2L/${event.for_year}/${event.event_type.replace(' ', '_')}/${event.slug}">Editer</a>
% endif
<h3 style="line-height:30px;">${event.name}</h3>
% if event.description :
@@ -35,10 +29,12 @@ ${event.start_time.strftime('%H:%M')} à ${event.end_time.strftime('%H:%M')}
% else:
<p>Cette évenement n'a pas de description.</p>
% endif
<div style="margin-top:10px;">
${helpers.show_SummaryEntities( event.get_linked_tiers() )}
</div>
</div>
</div>
<hr/>

% if event.presentation.count() and event.video.count():
<div class="row-fluid">
<div id="pres" class="span6">


+ 7
- 0
jm2l/upload.py ファイルの表示

@@ -120,6 +120,13 @@ class MediaPath():
else:
slug = user.slug
p = IMAGEPATH + ['users'] + [ slug ]
elif media_table=='badge':
user = User.by_id(linked_id)
if not user:
raise HTTPNotFound()
else:
slug = user.slug
p = IMAGEPATH + ['badge'] + [ slug ]
elif media_table=='event':
ev = Event.by_id(linked_id)
slug = ev.slug


+ 206
- 33
jm2l/views.py ファイルの表示

@@ -5,11 +5,14 @@ from pyramid.renderers import render_to_response
from pyramid.view import notfound_view_config, forbidden_view_config
from pyramid.view import view_config
from mako.template import Template
from .upload import IMAGEPATH
# Import Web Forms
from .forms import *
# Database access imports
from .models import *
from .helpers import Orga_helpers
from sqlalchemy import func, or_
from os import path, makedirs
# Usefull tools
from slugify import slugify
from icalendar import Calendar
@@ -20,7 +23,8 @@ from pyramid_mailer.message import Message
import webhelpers.paginate as paginate
import unicodedata
import datetime
import re
import re
import shutil

CurrentYear = 2015

@@ -390,10 +394,10 @@ def tasks(request):
raise HTTPForbidden(u'Vous n\'avez pas l\'autorité suffisante pour effectuer cette action.')
task_id = request.matchdict.get('task_id')
# Convert the pole_id GET parameter to int or 0
try:
if request.params.get('pole_id') and request.params.get('pole_id').isdigit():
pole_id = int(request.params.get('pole_id'))
except (ValueError, TypeError):
pole_id = 0
else:
pole_id = None

# Get areas from db
Areas = DBSession.query(TasksArea.uid, TasksArea.name)\
@@ -404,15 +408,18 @@ def tasks(request):
.order_by('nom').all()

if task_id:
Task = Tasks.by_id(int(task_id))
if not Task:
TmpTask = Tasks.by_id(int(task_id))
if not TmpTask:
raise HTTPNotFound()
form = EditStaffTasks(request.POST, Task, meta={'csrf_context': request.session})
form = EditStaffTasks(request.POST, TmpTask, meta={'csrf_context': request.session})
else:
Task = Tasks()
TmpTask = Tasks()
# Check if the supplied pole_id is in the Areas' range
Task.area_uid = pole_id if 1 < pole_id <= len(Areas) else 1
form = StaffTasks(request.POST, Task, meta={'csrf_context': request.session})
form = StaffTasks(request.POST, TmpTask, meta={'csrf_context': request.session})

Area = TasksArea.by_id(pole_id)
if Area:
form.area_uid.data = Area.uid

# Put some areas on form
form.area_uid.choices = Areas
@@ -421,16 +428,18 @@ def tasks(request):
for u in Users]

form.due_date.type = "date"
if request.method == 'POST' and form.validate():
form.populate_obj(Task)
Task.closed = False
if request.method=='POST' and form.validate():
form.populate_obj(TmpTask)
TmpTask.closed = False
if 'uid' in form._fields.keys():
DBSession.merge(Task)
DBSession.merge(TmpTask)
else:
DBSession.add(Task)
DBSession.add(TmpTask)
DBSession.flush()
return HTTPFound(location=request.route_url('list_task')+"#"+slugify(Task.area.name))
return {'form':form, 'area':slugify(Task.area.name)}
return HTTPFound(location=request.route_url('list_task')+"#"+slugify(TmpTask.area.name))
return {'form':form, 'area':TmpTask.area and slugify(TmpTask.area.name) or ''}

@view_config(route_name='handle_pole', renderer='jm2l:templates/Staff/pole.mako')
def tasks_area(request):
@@ -724,6 +733,47 @@ def sejour(request):
return HTTPFound(location='/MesJM2L#Sejour')

@view_config(route_name='orga')
def orga(request):
if request.user is None:
# Don't answer to users that aren't logged
raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
if request.method == 'POST':
FicheSejour = Sejour.by_user(request.user.uid)
UpdateOrga=False
if FicheSejour:
Update=True
if FicheSejour.orga_part:
UpdateOrga=True
else:
FicheSejour = Sejour()
FicheSejour.created = datetime.datetime.now()
Update=False
FicheSejour.user_id = request.user.uid
FicheSejour.last_change = datetime.datetime.now()
FicheSejour.for_year = CurrentYear

OrgaPart=0
for item in request.params:
try:
nb = int(item[1:])
except:
continue
OrgaPart += 2**nb

FicheSejour.orga_part = OrgaPart
if UpdateOrga:
request.session.flash(('info',u'Vos modifications de participation à l\'organisation ont été pris en compte.'))
else:
request.session.flash(('info',u'\\o/ Votre participation à l\'organisation est enregistrée !'))

if Update:
DBSession.merge(FicheSejour)
else:
DBSession.add(FicheSejour)

return HTTPFound(location='/MesJM2L#Organisation')

@view_config(route_name='vote_logo')
def vote_logo(request):
if request.user is None:
@@ -743,7 +793,7 @@ def vote_logo(request):
return HTTPFound(location=come)
raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')

@view_config(route_name='list_users', renderer="jm2l:templates/Participant/list.mako")
@view_config(route_name='list_users', renderer="jm2l:templates/Participant/list_users.mako")
def list_users(request):
if request.user is None:
# Don't answer to users that aren't logged
@@ -761,6 +811,16 @@ def list_users(request):
if (r[0] & 4 == 4): DicRepas["Soir"]+=1
return { 'Users':Data, 'UserEvent' : User_Event, "DicRepas":DicRepas }

@view_config(route_name='list_orga', renderer="jm2l:templates/Participant/list_orga.mako")
def list_orga(request):
if request.user is None:
# Don't answer to users that aren't logged
raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
if not request.user.Staff:
raise HTTPForbidden(u'Vous n\'avez pas l\'autorité suffisante pour effectuer cette action.')
Data = DBSession.query(User, Sejour).outerjoin(Sejour).all()
return { 'Users':Data }

@view_config(route_name='jm2l', renderer="jm2l:templates/jm2l.mako")
def jm2l_page(request):
if request.user is None:
@@ -1143,17 +1203,90 @@ def link_event_tiers(request):
else:
TargetTiers = Exist

if len(DBSession.query(Role_Tiers)\

Matching = DBSession.query(Role_Tiers)\
.filter(Role_Tiers.year_uid==year)\
.filter(Role_Tiers.tiers_role=="Exposant")\
.filter(Role_Tiers.tiers_uid==TargetTiers.uid)\
.all())==0:
tev = Role_Tiers(year_uid=year, tiers_role="Exposant", tiers_uid=TargetTiers.uid)
.filter(Role_Tiers.event_uid==TargetEvent.uid)\
.all()

if len(Matching)==0:
tev = Role_Tiers(year_uid=year, tiers_role="Exposant", tiers_uid=TargetTiers.uid, event_uid=TargetEvent.uid)
DBSession.add(tev)
return HTTPFound(location=request.route_url('edit_event', sep='/',
year=str(year), intervention=intervention, event_id=str(TargetEvent.uid)))
year=str(year), intervention=intervention, event_id=str(TargetEvent.uid), _anchor="Tiers"))

@view_config(route_name='delete_link')
def delete_link_event_tiers(request):
""" Create user if not exist, add it to current event """
if request.user is None:
# Don't answer to users that aren't logged
raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
year = int(request.matchdict.get('year', -1))
intervention = request.matchdict.get('intervention', None)
TargetEvent = Event.by_id( request.params.get('uid') )
Exist = Tiers.by_id( request.params.get('tid') )
if not Exist:
request.session.flash(('error',u"Une erreur s'est produite lors de l'ajout de votre entité !"))
return HTTPFound(location=request.route_url('edit_event', sep='/',
year=str(year), intervention=intervention, event_id=str(TargetEvent.uid)))
else:
TargetTiers = Exist

Matching = DBSession.query(Role_Tiers)\
.filter(Role_Tiers.year_uid==year)\
.filter(Role_Tiers.tiers_role=="Exposant")\
.filter(Role_Tiers.tiers_uid==TargetTiers.uid)\
.filter(Role_Tiers.event_uid==TargetEvent.uid)\
.all()

if len(Matching)==0:
request.session.flash(('error',u"Une erreur s'est produite lors de la suppression de votre entité !"))
else:
for item in Matching:
DBSession.delete(item)
return HTTPFound(location=request.route_url('edit_event', sep='/',
year=str(year), intervention=intervention, event_id=str(TargetEvent.uid), _anchor="Tiers"))

@view_config(route_name='delete_event')
def delete_event(request):
if request.user is None:
# Don't answer to users that aren't logged
raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
year = int(request.matchdict.get('year', -1))
event_id = request.matchdict.get('event_id')
intervention = request.matchdict.get('intervention', None)
# Check intervention
if not intervention in ['Stand', 'Table_ronde', 'Atelier', 'Conference', 'Concert']:
raise HTTPNotFound(u"Ce type d'évenement n'est pas reconnu")
# We should remove all links before to remove the event
if event_id.isdigit():
TheEvent = Event.by_id(event_id)
if TheEvent is None:
raise HTTPNotFound(u"Cette réference n'existe pas")
else:
TheEvent = Event.by_slug(event_id, year)
if TheEvent is None:
raise HTTPNotFound(u"Cette réference n'existe pas")
# Remove Roles
Roles_Link = DBSession.query(Role_Tiers).filter(Role_Tiers.event_uid==TheEvent.uid).all()
for tmp in Roles_Link:
DBSession.delete(tmp)
# Remove Intervenant
User_Link = DBSession.query(User_Event).filter(User_Event.event_uid==TheEvent.uid).all()
for tmp in User_Link:
DBSession.delete(tmp)
# Remove attachment if any
SRCPath = path.join('jm2l/upload', *(IMAGEPATH + ['event'] + [ str(year) ] + [ TheEvent.slug ]) )
shutil.rmtree(SRCPath, ignore_errors=True)
# Remove Event
DBSession.delete(TheEvent)
return HTTPFound(location=request.route_url('jm2l', _anchor="Interventions"))

@view_config(route_name='edit_event', renderer="jm2l:templates/edit_event.mako")
def edit_event(request):
@@ -1167,7 +1300,7 @@ def edit_event(request):
if intervention=='Conference':
IntervLabel = u'conférence'
# Check intervention
if not intervention in ['Stand', 'Table ronde', 'Atelier', 'Conference', 'Concert']:
if not intervention in ['Stand', 'Table_ronde', 'Atelier', 'Conference', 'Concert']:
raise HTTPNotFound(u"Ce type d'évenement n'est pas reconnu")
TheYear = DBSession.query(JM2L_Year)\
.filter(JM2L_Year.year_uid==year)\
@@ -1256,7 +1389,7 @@ def edit_event(request):
form.duration.choices.append( (duration,u'Conférence (%d min)' % duration) )
if not form._fields.has_key("uid"):
form.duration.data=60
SalleDispo = SalleDispo.filter(Salles.place_type=='Conference')
SalleDispo = SalleDispo.filter(Salles.place_type.in_(['Conference', 'MAO']))
elif intervention=="Stand":
form.duration.choices =[
(8*60, u'Toute la journée'),
@@ -1268,19 +1401,19 @@ def edit_event(request):
[60, 90, 120, 150, 180, 210, 240] )
if not duration in map(lambda (d,y): d, form.duration.choices):
form.duration.choices.append( (duration,u'Atelier (%dh%.2d)' % (duration/60, duration%60) ) )
SalleDispo = SalleDispo.filter(Salles.place_type=='Ateliers')
elif intervention=="Table ronde":
SalleDispo = SalleDispo.filter(Salles.place_type.in_(['Ateliers', 'MAO']))
elif intervention=="Table_ronde":
form.duration.choices = map( lambda d:(d, u'Table ronde (%dh%.2d)' % (d/60, d%60) ), \
[60, 90, 120, 150] )
if not duration in map(lambda (d,y): d, form.duration.choices):
form.duration.choices.append( (duration,u'Table ronde (%dh%.2d)' % (duration/60, duration%60) ) )
SalleDispo = SalleDispo.filter(Salles.place_type=='Conference')
SalleDispo = SalleDispo.filter(Salles.place_type=='Table ronde')
elif intervention=="Concert":
form.duration.choices = map( lambda d:(d, u'Concert (%dh%.2d)' % (d/60, d%60) ), \
[60, 90, 120, 150, 180, 210, 240] )
if not duration in map(lambda (d,y): d, form.duration.choices):
form.duration.choices.append( (duration,u'Concert (%dh%.2d)' % (duration/60, duration%60) ) )
SalleDispo = SalleDispo.filter(Salles.place_type=='Stand')
SalleDispo = SalleDispo.filter(Salles.place_type.in_(['Stand', 'MAO']))
else:
raise HTTPForbidden(u"Pas encore disponible.")
@@ -1301,16 +1434,35 @@ def edit_event(request):
uev.user_uid = request.user.uid
TheEvent.interventions.append( uev )
DBSession.flush()
request.session.flash(('sucess',u'Votre intervention a été créee !'))
request.session.flash(('sucess',u'Votre intervention a été créee ! Vous pouvez la compléter à tout moment.'))
return HTTPFound(location=request.route_url('edit_event', sep='/',
year=str(year), intervention=intervention, event_id=str(TheEvent.slug)))
else:
if slugify(TheEvent.name)!=TheEvent.slug:
# We should move some file as slug have been changed
# First we ensure there is no related event that already exist with that slug
CheckEvent = Event.by_slug( slugify(TheEvent.name), year)
if CheckEvent:
request.session.flash(('warning',u'Choisissez un autre titre pour votre évenement, il est en conflit avec un autre.'))
return {'event':TheEvent, 'form':form, 'formAdd':formAdd, 'formAddT':formAddT, 'Salles':Salles }
else:
SRCPath = path.join('jm2l/upload', *(IMAGEPATH + ['event'] + [ str(year) ] + [ TheEvent.slug ]) )
TheEvent.slug=slugify(TheEvent.name)
DSTPath = path.join('jm2l/upload', *(IMAGEPATH + ['event'] + [ str(year) ] + [ TheEvent.slug ]) )
if not path.isdir(path.dirname(DSTPath)):
makedirs(path.dirname(DSTPath))
# Then we should move event attachments to the new slug (if any)
if path.exists(SRCPath):
shutil.move(SRCPath, DSTPath)
DBSession.merge(TheEvent)
request.session.flash(('sucess',u'Votre intervention a été mis à jour !'))
return HTTPFound(location=request.route_url('edit_event', sep='/',
year=str(year), intervention=intervention, event_id=str(TheEvent.slug)))
MainTab = {'programme':'','presse':'', 'plan':'', 'participer':'',
'event':TheEvent, 'form':form, 'formAdd':formAdd, 'formAddT':formAddT,
'Salles':Salles,
'logged_in':request.authenticated_userid }
'Salles':Salles }
return MainTab

@@ -1451,13 +1603,34 @@ def edit_tiers(request):

form.populate_obj(TheTiers)
# Handle Remove of accents
TheTiers.slug = slugify(form.name.data)
OriginalSlug = TheTiers.slug
if not form._fields.has_key('uid'):
TheTiers.slug = slugify(form.name.data)
TheTiers.creator_id = request.user.uid
DBSession.add(TheTiers)
DBSession.flush()
return HTTPFound(location=request.route_url('edit_entity', sep='/',
entity_id=str(TheTiers.slug), tiers_type=TheTiers.get_entity_type.entity_type))
else:
if OriginalSlug!=slugify(form.name.data):
# We should move some file as slug have been changed
# First we ensure there is no related event that already exist with that slug
CheckTiers = Tiers.by_slug( slugify(form.name.data) )
if CheckTiers:
request.session.flash(('warning',u'Attention, Choisissez un autre titre pour votre entitée, elle est en conflit avec une autre.'))
DBSession.rollback()
return HTTPFound(location=request.route_url('edit_entity', sep='/',
entity_id=str(OriginalSlug), tiers_type=TheTiers.get_entity_type.entity_type))
else:
TheTiers.slug = slugify(form.name.data)
SRCPath = path.join('jm2l/upload', *(IMAGEPATH + ['tiers'] + [ OriginalSlug ]) )
DSTPath = path.join('jm2l/upload', *(IMAGEPATH + ['tiers'] + [ TheTiers.slug ]) )
if not path.isdir(path.dirname(DSTPath)):
makedirs(path.dirname(DSTPath))
# Then we should move event attachments to the new slug (if any)
if path.exists(SRCPath):
shutil.move(SRCPath, DSTPath)

DBSession.merge(TheTiers)
return HTTPFound(location=request.route_url('show_entity', entity_id=TheTiers.slug,
tiers_type=TheTiers.get_entity_type.slug_entity_type))
@@ -1586,7 +1759,7 @@ def link_role_entity(request):
def forbidden(reason, request):
if 'ident' in reason.detail:
request.session.flash(('info', reason.detail))
return HTTPFound(location='/sign/login' )
return HTTPFound(location='/sign/login?from='+request.environ['PATH_INFO'] )
else:
request.response.status = 403
return render_to_response('jm2l:templates/Errors/403.mako', { "reason":reason },


読み込み中…
キャンセル
保存