@@ -38,7 +38,7 @@ If no error occurs, the webserver should be available on http://localhost:8080/ | |||
Enjoy ! | |||
sudo apt install virtualenv git python3-virtualenv | |||
sudo apt install virtualenv git python3-virtualenv imagemagick | |||
sudo mkdir -p /srv/jm2l | |||
cd /srv/jm2l/ | |||
@@ -15,7 +15,6 @@ pyramid.debug_notfound = false | |||
pyramid.debug_routematch = false | |||
pyramid.default_locale_name = en | |||
pyramid.includes = | |||
pyramid_mailer.testing | |||
pyramid_debugtoolbar | |||
pyramid_tm | |||
pyramid_mako | |||
@@ -30,31 +30,31 @@ def add_renderer_globals(event): | |||
event['CurrentYear'] = CurrentYear | |||
# @sched.scheduled_job('cron', day_of_week='sun', hour=22, minute=07) | |||
# @sched.scheduled_job('cron', day_of_week='sun', hour=22, minute=7) | |||
def mailer_tasks(config): | |||
# Send the Welcome Mail | |||
mailer = config.registry['mailer'] | |||
Contact = DBSession.query(User).filter(User.uid == 1).one() | |||
contact = DBSession.query(User).filter(User.uid == 1).one() | |||
request = Request.blank('/', base_url='http://jm2l.linux-azur.org') | |||
request.registry = config.registry | |||
for StaffUser in DBSession.query(User).filter(User.Staff == True): | |||
for staff_user in DBSession.query(User).filter(User.Staff is True): | |||
# Skip mail to contact | |||
if StaffUser == Contact: | |||
if staff_user == contact: | |||
continue | |||
# Skip those that have no task assigned | |||
if len(filter(lambda k: not k.closed, StaffUser.task_assoc)) == 0: | |||
if len(filter(lambda k: not k.closed, staff_user.task_assoc)) == 0: | |||
continue | |||
# Prepare Plain Text Message : | |||
Mail_template = Template(filename='jm2l/templates/mail_plain.mako') | |||
mail_plain = Mail_template.render(request=request, User=StaffUser, Contact=Contact, action="Tasks") | |||
mail_template = Template(filename='jm2l/templates/mail_plain.mako') | |||
mail_plain = mail_template.render(request=request, User=staff_user, Contact=contact, action="Tasks") | |||
# Prepare HTML Message : | |||
Mail_template = Template(filename='jm2l/templates/mail_html.mako') | |||
mail_html = Mail_template.render(request=request, User=StaffUser, Contact=Contact, action="Tasks") | |||
mail_template = Template(filename='jm2l/templates/mail_html.mako') | |||
mail_html = mail_template.render(request=request, User=staff_user, Contact=contact, action="Tasks") | |||
# Prepare Message | |||
message = Message(subject="[JM2L] Le mail de rappel pour les JM2L !", | |||
sender="contact@jm2l.linux-azur.org", | |||
recipients=[StaffUser.mail], | |||
recipients=[staff_user.mail], | |||
body=mail_plain, html=mail_html) | |||
message.add_bcc("spam@style-python.fr") | |||
@@ -81,8 +81,11 @@ def main(global_config, **settings): | |||
authentication_policy=authentication_policy, | |||
authorization_policy=authorization_policy | |||
) | |||
#config.include('pyramid_mailer') | |||
config.include('pyramid_mailer.debug') | |||
config.add_subscriber(add_renderer_globals, BeforeRender) | |||
config.registry['mailer'] = mailer_factory_from_settings(settings) | |||
print(settings) | |||
# config.registry['mailer'] = mailer_factory_from_settings(settings) | |||
config.registry['event_date'] = JM2L_Year.get_latest_jm2l_startdate() | |||
sched = BackgroundScheduler() | |||
sched.add_job(mailer_tasks, 'cron', day_of_week='fri', hour=18, args=[config]) | |||
@@ -154,7 +157,7 @@ def main(global_config, **settings): | |||
config.add_route('edit_event', r'/MesJM2L/{year:\d+}/{intervention:[\s\w]+}{sep:/*}{event_id:([\w-]+)?}') | |||
config.add_route('delete_event', r'/MesJM2L/{year:\d+}/{intervention:[\s\w]+}{sep:/*}{event_id:([\w-]+)?}/delete') | |||
## Entities | |||
# Entities | |||
config.add_route('entities', '/entities') # {sep:/*}{Nature:\w+?}') | |||
config.add_route('add_entity', '/entity') | |||
config.add_route('delete_entity', r'/entity/{entity_id:(\d+)}/delete') | |||
@@ -162,7 +165,7 @@ def main(global_config, **settings): | |||
config.add_route('edit_entity', r'/entity/{tiers_type:(\w+)}/{entity_id:([\w-]+)}/edit') | |||
config.add_route('edit_entity_cat', '/categorie/entity') | |||
## Users | |||
# Users | |||
config.add_route('pict_user', '/user_picture') | |||
config.add_route('show_user', r'/user/{user_slug:([\w-]+)?}') | |||
config.add_route('badge_user', r'/user/{user_slug:([\w-]+)?}/badge') | |||
@@ -6,6 +6,7 @@ try: | |||
from StringIO import StringIO | |||
except ImportError: | |||
from io import StringIO | |||
import io | |||
from pyramid.view import view_config | |||
from .models import DBSession, User | |||
from reportlab.pdfgen import canvas | |||
@@ -25,38 +26,38 @@ ICONSIZE = 10 * mm | |||
def JM2L_Logo(canvas, Offset=(0, 0)): | |||
OffX, OffY = Offset | |||
logoobject = canvas.beginText() | |||
logoobject.setFont('Logo', 32) | |||
logoobject.setFillColorRGB(.83, 0, .33) | |||
logoobject.setTextOrigin(OffX + 5, OffY + 17) | |||
logoobject.textLines("JM2L") | |||
canvas.drawText(logoobject) | |||
yearobject = canvas.beginText() | |||
yearobject.setFont("Helvetica-Bold", 10) | |||
yearobject.setFillColorRGB(1, 1, 1) | |||
yearobject.setTextRenderMode(0) | |||
yearobject.setTextOrigin(OffX + 12, OffY + 35) | |||
yearobject.setWordSpace(13) | |||
yearobject.textLines(" ".join(str(CurrentYear))) | |||
canvas.drawText(yearobject) | |||
def Tiers_Logo(canvas, DispUser, StartPos=None, Offset=(0, 0)): | |||
Border = 0 | |||
OffX, OffY = Offset | |||
if StartPos is None: | |||
StartPos = (30 * mm, 2) | |||
StartX, StartY = StartPos | |||
MaxX, MaxY = 34 * mm, 18 * mm | |||
off_x, off_y = Offset | |||
logo_object = canvas.beginText() | |||
logo_object.setFont('Logo', 32) | |||
logo_object.setFillColorRGB(.83, 0, .33) | |||
logo_object.setTextOrigin(off_x + 5, off_y + 17) | |||
logo_object.textLines("JM2L") | |||
canvas.drawText(logo_object) | |||
year_object = canvas.beginText() | |||
year_object.setFont("Helvetica-Bold", 10) | |||
year_object.setFillColorRGB(1, 1, 1) | |||
year_object.setTextRenderMode(0) | |||
year_object.setTextOrigin(off_x + 12, off_y + 35) | |||
year_object.setWordSpace(13) | |||
year_object.textLines(" ".join(str(CurrentYear))) | |||
canvas.drawText(year_object) | |||
def Tiers_Logo(canvas, DispUser, start_pos=None, Offset=(0, 0)): | |||
border = 0 | |||
off_x, off_y = Offset | |||
if start_pos is None: | |||
start_pos = (30 * mm, 2) | |||
start_x, start_y = start_pos | |||
max_x, max_y = 34 * mm, 18 * mm | |||
num = 0 | |||
canvas.setStrokeColorRGB(0.5, 0.5, 0.5) | |||
Logos = list() | |||
list_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]) | |||
list_logos.append(thumb.ThumbLinks[:3]) | |||
# list_logos = list(filter(lambda x: x.ThumbLinks, DispUser.tiers)[:3]) | |||
# Should We compute a better positionning for logos ? | |||
DicPos = {} | |||
DicPos[1] = {0: (1. / 2, 1. / 2)} | |||
@@ -75,25 +76,25 @@ def Tiers_Logo(canvas, DispUser, StartPos=None, Offset=(0, 0)): | |||
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) | |||
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.roundRect(start_x, start_y, max_x, max_y, radius=2, stroke=True) | |||
for tiers in list_logos: | |||
file_name = tiers.ThumbLinks.pop().split("/")[-1] | |||
image_path = "jm2l/upload/images/tiers/%s/%s" % (tiers.slug, file_name) | |||
pos_x = off_x + start_x + DicPos[len(list_logos)][num][0] * max_x - (ICONSIZE + border) / 2 | |||
pos_y = off_y + start_y + DicPos[len(list_logos)][num][1] * max_y - (ICONSIZE + border) / 2 | |||
canvas.setLineWidth(.1) | |||
if len(Logos) > 1: | |||
if len(list_logos) > 1: | |||
size = ICONSIZE | |||
else: | |||
size = ICONSIZE * 1.5 | |||
canvas.drawImage(ImagePath, | |||
PosX, PosY, size, size, | |||
canvas.drawImage(image_path, | |||
pos_x, pos_y, size, size, | |||
preserveAspectRatio=True, | |||
anchor='c', | |||
mask='auto' | |||
) | |||
# draw icon border | |||
# canvas.roundRect(PosX, PosY, ICONSIZE, ICONSIZE, radius=2, stroke=True) | |||
# canvas.roundRect(pos_x, pos_y, ICONSIZE, ICONSIZE, radius=2, stroke=True) | |||
num += 1 | |||
@@ -114,37 +115,37 @@ def QRCode(DispUser): | |||
def one_badge(c, DispUser, Offset=(0, 0)): | |||
# Logo on Top | |||
JM2L_Logo(c, Offset) | |||
OffX, OffY = Offset | |||
off_x, off_y = Offset | |||
c.rect(OffX - 3, OffY - 3, WIDTH + 6, HEIGHT + 6, fill=0, stroke=1) | |||
c.rect(off_x - 3, off_y - 3, WIDTH + 6, HEIGHT + 6, fill=0, stroke=1) | |||
if DispUser.Staff: | |||
# Staff | |||
c.setFillColorRGB(.83, 0, .33) | |||
c.rect(OffX - 3, OffY + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0) | |||
c.rect(off_x - 3, off_y + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0) | |||
c.setFillColorRGB(1, 1, 1) | |||
c.setFont('Liberation', 30) | |||
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT - 24, "STAFF") | |||
c.drawCentredString(off_x + WIDTH / 2, off_y + HEIGHT - 24, "STAFF") | |||
elif DispUser.is_Intervenant: | |||
# Intervenant | |||
c.setFillColorRGB(.21, .67, .78) | |||
c.rect(OffX - 3, OffY + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0) | |||
c.rect(off_x - 3, off_y + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0) | |||
c.setFillColorRGB(1, 1, 1) | |||
c.setFont('Liberation', 30) | |||
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT - 24, "Intervenant") | |||
c.drawCentredString(off_x + WIDTH / 2, off_y + HEIGHT - 24, "Intervenant") | |||
elif DispUser.is_crew: | |||
# Benevole | |||
c.setFillColorRGB(.18, .76, .23) | |||
c.rect(OffX - 3, OffY + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0) | |||
c.rect(off_x - 3, off_y + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0) | |||
c.setFillColorRGB(1, 1, 1) | |||
c.setFont('Liberation', 30) | |||
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT - 24, "Bénévole") | |||
c.drawCentredString(off_x + WIDTH / 2, off_y + HEIGHT - 24, "Bénévole") | |||
else: | |||
# Visiteur | |||
c.setFillColorRGB(.8, .8, .8) | |||
c.rect(OffX - 3, OffY + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0) | |||
c.rect(off_x - 3, off_y + HEIGHT - 30, WIDTH + 6, 33, fill=1, stroke=0) | |||
c.setFillColorRGB(1, 1, 1) | |||
c.setFont('Liberation', 30) | |||
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT - 24, "Visiteur") | |||
c.drawCentredString(off_x + WIDTH / 2, off_y + HEIGHT - 24, "Visiteur") | |||
c.restoreState() | |||
@@ -154,25 +155,25 @@ def one_badge(c, DispUser, Offset=(0, 0)): | |||
# Feed Name and SurName | |||
if DispUser.prenom and DispUser.nom and len(DispUser.prenom) + len(DispUser.nom) > 18: | |||
if DispUser.pseudo: | |||
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 + 0 * mm, "%s" % DispUser.prenom) | |||
c.drawCentredString(off_x + WIDTH / 2, off_y + 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(off_x + WIDTH / 2, off_y + HEIGHT / 2 - 8 * mm, "%s" % DispUser.nom) | |||
else: | |||
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 + 4 * mm, "%s" % DispUser.prenom) | |||
c.drawCentredString(off_x + WIDTH / 2, off_y + 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(off_x + WIDTH / 2, off_y + HEIGHT / 2 - 8 * mm, "%s" % DispUser.nom) | |||
else: | |||
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 + 0 * mm, "%s %s" % (DispUser.prenom, DispUser.nom)) | |||
c.drawCentredString(off_x + WIDTH / 2, off_y + HEIGHT / 2 + 0 * mm, "%s %s" % (DispUser.prenom, DispUser.nom)) | |||
if DispUser.pseudo: | |||
c.setFont("Helvetica-Oblique", 18) | |||
c.drawCentredString(OffX + WIDTH / 2, OffY + HEIGHT / 2 + 10 * mm, "%s" % DispUser.pseudo) | |||
c.drawCentredString(off_x + WIDTH / 2, off_y + HEIGHT / 2 + 10 * mm, "%s" % DispUser.pseudo) | |||
# Put QR code to user profile | |||
c.drawInlineImage(QRCode(DispUser), \ | |||
OffX + WIDTH - 20 * mm - 5, OffY + 5, \ | |||
20 * mm, 20 * mm, \ | |||
preserveAspectRatio=True, \ | |||
c.drawInlineImage(QRCode(DispUser), | |||
off_x + WIDTH - 20 * mm - 5, off_y + 5, | |||
20 * mm, 20 * mm, | |||
preserveAspectRatio=True, | |||
anchor='s') | |||
Tiers_Logo(c, DispUser, None, Offset) | |||
@@ -192,14 +193,14 @@ def badge_user(request): | |||
# Ok let's generate a PDF Badge | |||
# Register LiberationMono font | |||
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) | |||
ttf_file = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||
pdfmetrics.registerFont(TTFont("Liberation", ttf_file)) | |||
# Import font | |||
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) | |||
ttf_file_logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||
pdfmetrics.registerFont(TTFont("Logo", ttf_file_logo)) | |||
pdf = StringIO() | |||
out_img = StringIO() | |||
pdf = io.BytesIO() | |||
out_img = io.BytesIO() | |||
c = canvas.Canvas(pdf, pagesize=(WIDTH, HEIGHT)) | |||
c.translate(mm, mm) | |||
@@ -211,23 +212,21 @@ def badge_user(request): | |||
c.saveState() | |||
one_badge(c, DispUser) | |||
OutPDF = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.pdf') | |||
out_pdf = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.pdf') | |||
c.showPage() | |||
c._filename = OutPDF | |||
c.save() | |||
pdf.seek(0) | |||
if isoutpng: | |||
OutPNG = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.png') | |||
out_png = MediaPath().get_mediapath("badge", DispUser.uid, 'badge.png') | |||
# Let's generate a png file for website | |||
with open("./%s" % OutPDF, 'wb') as pdff: | |||
pdff.write(bytes(pdf.read(), 'utf8')) # .encode('utf8')) | |||
with open("./%s" % out_pdf, 'wb') as pdff: | |||
pdff.write(pdf.read()) | |||
Command = ["convert", "-density", "150x150", OutPDF, OutPNG] | |||
Command = ["convert", "-density", "150x150", out_pdf, out_png] | |||
subprocess.call(Command) | |||
with open("./%s" % OutPNG, 'r') as pngfile: | |||
out_img.write(bytes(pngfile.read(), 'utf8')) # bytes(pngfile.read(), "utf8")) | |||
with open("./%s" % out_png, 'rb') as pngfile: | |||
out_img.write(pngfile.read()) # pngfile.read(), "utf8")) | |||
out_img.seek(0) | |||
return Response(app_iter=out_img, content_type='image/png') | |||
@@ -246,13 +245,13 @@ def planche_badge(request): | |||
# .filter(User_Event.year_uid == year) | |||
# Register LiberationMono font | |||
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) | |||
ttf_file = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||
pdfmetrics.registerFont(TTFont("Liberation", ttf_file)) | |||
# Import font | |||
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) | |||
ttf_file_logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||
pdfmetrics.registerFont(TTFont("Logo", ttf_file_logo)) | |||
pdf = StringIO.StringIO() | |||
pdf = io.BytesIO() | |||
FULLWIDTH = 210 * mm | |||
FULLHEIGHT = 297 * mm | |||
@@ -264,11 +263,11 @@ def planche_badge(request): | |||
c.setCreator("linux-azur.org") | |||
c.setTitle("Badge") | |||
t = 0 | |||
ListUser = filter(lambda x: x.is_Intervenant or x.Staff or x.is_crew, Users) | |||
for num, DispUser in enumerate(ListUser): | |||
list_user = filter(lambda x: x.is_Intervenant or x.Staff or x.is_crew, Users) | |||
for num, disp_user in enumerate(list_user): | |||
c.saveState() | |||
Offsets = (((num - t) % 2) * (WIDTH + 40) + 40, ((num - t) / 2) * (HEIGHT + 25) + 40) | |||
one_badge(c, DispUser, Offsets) | |||
offsets = (((num - t) % 2) * (WIDTH + 40) + 40, int(((num - t) / 2)) * (HEIGHT + 25) + 40) | |||
one_badge(c, disp_user, offsets) | |||
if num % 8 == 7: | |||
t = num + 1 | |||
c.showPage() | |||
@@ -115,8 +115,8 @@ class _PyCaptcha_SineWarp(_PyCaptcha_WarpBase): | |||
@view_config(route_name='captcha') | |||
def DoCaptcha(request): | |||
ImgSize = (230, 100) | |||
work_img = Image.new('RGBA', ImgSize, (255, 255, 255, 0)) | |||
img_size = (230, 100) | |||
work_img = Image.new('RGBA', img_size, (255, 255, 255, 0)) | |||
Xmax, Ymax = work_img.size | |||
# Write something on it | |||
draw = ImageDraw.Draw(work_img) | |||
@@ -129,7 +129,7 @@ def DoCaptcha(request): | |||
# Choose a word for captcha | |||
text = random.choice(TabMots) | |||
Xt, Yt = font.getsize(text) | |||
OrX, OrY = (ImgSize[0] - Xt) / 2, (ImgSize[1] - Yt) / 2 | |||
OrX, OrY = (img_size[0] - Xt) / 2, (img_size[1] - Yt) / 2 | |||
draw.text((OrX, OrY), text, font=font, fill="#000000") | |||
# Apply a Blur | |||
# work_img=work_img.filter(ImageFilter.BLUR) | |||
@@ -140,7 +140,7 @@ def DoCaptcha(request): | |||
tx, ty = (random.uniform(0, 0.0003), random.uniform(0, 0.0003)) | |||
bx, by = (random.uniform(0.5, 0.8), random.uniform(0, 0.2)) | |||
# Apply perspective to Captcha | |||
work_img = work_img.transform(ImgSize, Image.PERSPECTIVE, (ax, bx, -25, by, ay, -10, tx, ty)) | |||
work_img = work_img.transform(img_size, Image.PERSPECTIVE, (ax, bx, -25, by, ay, -10, tx, ty)) | |||
# Apply SinWarp to Captcha | |||
tr = Captcha_Img(Xmax, Ymax) | |||
tr._image = work_img | |||
@@ -11,17 +11,20 @@ from wtforms.csrf.session import SessionCSRF | |||
from datetime import timedelta | |||
from jm2l.const import CurrentYear | |||
# What about an helper function | |||
#def strip_filter(x) | |||
# if x is no | |||
strip_filter = lambda x: x.strip() if x else None | |||
def strip_filter(x): | |||
# strip_filter = lambda x: x.strip() if x else None | |||
if x: | |||
return x.strip() | |||
return None | |||
# get random string password with letters, digits, and symbols | |||
def get_random_string(length): | |||
csrf_characters = string.ascii_letters + string.digits + string.punctuation | |||
csrf = ''.join(random.choice(password_characters) for i in range(length)) | |||
return csrf | |||
csrf = ''.join(random.choice(csrf_characters) for i in range(length)) | |||
return bytes(csrf, 'utf8') | |||
class MyBaseForm(Form): | |||
@@ -155,32 +158,38 @@ class ConfCreateForm(MyBaseForm): | |||
start_time = HiddenField() | |||
end_time = HiddenField() | |||
start_sel = SelectField(u'Début', coerce=int, | |||
description=u"C'est une heure indicative correspondant au mieux à vos préférences " + | |||
u"personnelles. Vous pouvez prendre un créneau horaire déjà réservé si vous avez des contraintes " | |||
u"particulières. L'équipe des JM2L mettra à disposition plus de salle si nécessaire. En cas de conflit," + | |||
u"l'organisation se réserve le droit de changer la salle et l'heure avec votre accord." | |||
) | |||
duration = SelectField(u'Durée', coerce=int, | |||
description=u"Précisez ici la durée de votre intervention" | |||
) | |||
salle_uid = SelectField(u'Salle', coerce=int, | |||
description=u"Choisissez ici la salle en fonction " | |||
u"du nombres de personnes potentiellement intéressé par votre intervention " + | |||
u"l'organisation se réserve le droit de changer la salle (avec votre accord)." | |||
) | |||
name = StringField(u'Le nom de votre ', | |||
[validators.DataRequired(u'Vous devez spécifier un nom pour votre intérvention'), | |||
validators.Length(min=1, max=80, message='entre 1 et 80 car')], | |||
filters=[strip_filter]) | |||
description = TextAreaField(u'Décrivez ici quelques détails à propos de votre intervention ', | |||
[validators.Optional(), validators.Length(max=1000000)], | |||
filters=[strip_filter] | |||
) | |||
start_sel = SelectField( | |||
u'Début', coerce=int, | |||
description=u"C'est une heure indicative correspondant au mieux à vos préférences " | |||
u"personnelles. Vous pouvez prendre un créneau horaire déjà réservé si vous avez des contraintes " | |||
u"particulières. L'équipe des JM2L mettra à disposition plus de salle si nécessaire. " | |||
u"En cas de conflit," | |||
u"l'organisation se réserve le droit de changer la salle et l'heure avec votre accord." | |||
) | |||
duration = SelectField( | |||
u'Durée', coerce=int, | |||
description=u"Précisez ici la durée de votre intervention") | |||
salle_uid = SelectField( | |||
u'Salle', coerce=int, | |||
description=u"Choisissez ici la salle en fonction du nombres de personnes potentiellement " | |||
u"intéressé par votre intervention l'organisation se réserve le droit de changer" | |||
u" la salle (avec votre accord)." | |||
) | |||
name = StringField( | |||
u'Le nom de votre ', | |||
[validators.DataRequired(u'Vous devez spécifier un nom pour votre intérvention'), | |||
validators.Length(min=1, max=80, message='entre 1 et 80 car')], | |||
filters=[strip_filter] | |||
) | |||
description = TextAreaField( | |||
u'Décrivez ici quelques détails à propos de votre intervention ', | |||
[validators.Optional(), validators.Length(max=1000000)], | |||
filters=[strip_filter] | |||
) | |||
class ConfUpdateForm(ConfCreateForm): | |||
@@ -223,9 +232,10 @@ class PlaceCreateForm(MyBaseForm): | |||
filters=[strip_filter]) | |||
name = StringField('Nom Complet', [validators.Length(min=1, max=80)], | |||
filters=[strip_filter]) | |||
gps_coord = StringField(u'Coordonnées GPS', [validators.Length(max=30), | |||
validators.Regexp("^[0-9]+\.?[0-9]+,[0-9]+\.?[0-9]+$", | |||
message=u"Le GPS devrait être sous la forme 43.6158372,7.0723401")], | |||
gps_coord = StringField(u'Coordonnées GPS', | |||
[validators.Length(max=30), | |||
validators.Regexp("^[0-9]+\.?[0-9]+,[0-9]+\.?[0-9]+$", | |||
message=u"Le GPS devrait être sous la forme 43.6158372,7.0723401")], | |||
filters=[strip_filter]) | |||
adresse = TextAreaField('Adresse', [validators.Length(max=100)], | |||
filters=[strip_filter]) | |||
@@ -600,13 +610,16 @@ class PropMForm(MyBaseForm): | |||
message=u"doit être sous la forme HH:MM")], | |||
filters=[strip_filter]) | |||
end_time = HiddenField() | |||
exch_categ = SelectField(u'Catégorie de matériel', coerce=int, | |||
description=u"Choisissez une catégorie de bien matériel" | |||
) | |||
description = TextAreaField(u'Ajoutez quelques mots autour du matériel que vous proposez', filters=[strip_filter], | |||
description=u"Décrivez ici quelques détails sur le matériel que vous souhaitez " | |||
+ u"proposer. N'hésitez pas à donner des détails." | |||
) | |||
exch_categ = SelectField( | |||
u'Catégorie de matériel', coerce=int, | |||
description=u"Choisissez une catégorie de bien matériel" | |||
) | |||
description = TextAreaField( | |||
u'Ajoutez quelques mots autour du matériel que vous proposez', | |||
filters=[strip_filter], | |||
description=u"Décrivez ici quelques détails sur le matériel " | |||
u"que vous souhaitez proposer. N'hésitez pas à donner des détails." | |||
) | |||
class UpdateAskCForm(AskCForm, UpdateExchangeForm): | |||
@@ -5,6 +5,11 @@ import itertools | |||
from jm2l.const import CurrentYear | |||
def get_current_year(): | |||
""" This function is intended to return the year of the next edition """ | |||
return CurrentYear | |||
class DummySejour(object): | |||
def __init__(self, event): | |||
@@ -29,78 +34,78 @@ class Sejour_helpers(DummySejour): | |||
# This function return the start of the event | |||
return self.CurrentYear | |||
def PossibleDate(self, typedate="arrival"): | |||
def PossibleDate(self, type_date="arrival"): | |||
arrival, departure = False, False | |||
TabResult = list() | |||
if typedate == "arrival": | |||
tab_result = list() | |||
if type_date == "arrival": | |||
# Let's say people should arrive until 2 day before | |||
arrival = True | |||
myDayRange = range(2, -1, -1) | |||
elif typedate == "departure": | |||
my_day_range = range(2, -1, -1) | |||
elif type_date == "departure": | |||
# Let's say people should go back home until 2 day after | |||
departure = True | |||
myDayRange = range(3) | |||
my_day_range = range(3) | |||
else: | |||
return TabResult | |||
return tab_result | |||
if self.Sejour: | |||
ArrDate = datetime.strftime(self.Sejour.arrival_time, "%d %B %Y") | |||
DepDate = datetime.strftime(self.Sejour.depart_time, "%d %B %Y") | |||
arr_date = datetime.strftime(self.Sejour.arrival_time, "%d %B %Y") | |||
dep_date = datetime.strftime(self.Sejour.depart_time, "%d %B %Y") | |||
else: | |||
ArrDate = datetime.strftime(self.CurrentEventYear.start_time, "%d %B %Y") | |||
DepDate = datetime.strftime(self.CurrentEventYear.end_time, "%d %B %Y") | |||
arr_date = datetime.strftime(self.CurrentEventYear.start_time, "%d %B %Y") | |||
dep_date = datetime.strftime(self.CurrentEventYear.end_time, "%d %B %Y") | |||
for oneday in myDayRange: | |||
for one_day in my_day_range: | |||
if arrival: | |||
TmpDay = self.CurrentEventYear.end_time - timedelta(days=oneday) | |||
tmp_day = self.CurrentEventYear.end_time - timedelta(days=one_day) | |||
elif departure: | |||
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") | |||
if arrival and ArrDate == DayString: | |||
TabResult.append((DayNum, DayName, 'selected="selected"')) | |||
elif departure and DepDate == DayString: | |||
TabResult.append((DayNum, DayName, 'selected="selected"')) | |||
tmp_day = self.CurrentEventYear.start_time + timedelta(days=one_day) | |||
day_name = datetime.strftime(tmp_day, "%A") | |||
day_num = datetime.strftime(tmp_day, "%d/%m/%y") | |||
day_string = datetime.strftime(tmp_day, "%d %B %Y") | |||
if arrival and arr_date == day_string: | |||
tab_result.append((day_num, day_name, 'selected="selected"')) | |||
elif departure and dep_date == day_string: | |||
tab_result.append((day_num, day_name, 'selected="selected"')) | |||
else: | |||
TabResult.append((DayNum, DayName, "")) | |||
return TabResult | |||
tab_result.append((day_num, day_name, "")) | |||
return tab_result | |||
def PossibleTime(self, typedate="arrival"): | |||
ArrTime, DepTime = "10:00", "19:00" | |||
TabResult = list() | |||
def PossibleTime(self, type_date="arrival"): | |||
arr_time, dep_time = "10:00", "19:00" | |||
tab_result = list() | |||
if self.Sejour: | |||
ArrTime = datetime.strftime(self.Sejour.arrival_time, "%H:%M") | |||
DepTime = datetime.strftime(self.Sejour.depart_time, "%H:%M") | |||
arr_time = datetime.strftime(self.Sejour.arrival_time, "%H:%M") | |||
dep_time = datetime.strftime(self.Sejour.depart_time, "%H:%M") | |||
for hour in range(24): | |||
for minutes in range(0, 60, 10): | |||
StrTime = "%.2d:%.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"')) | |||
str_time = "%.2d:%.2d" % (hour, minutes) | |||
disp_time = "%dh%.2d" % (hour, minutes) | |||
if type_date == "arrival" and str_time == arr_time: | |||
tab_result.append((str_time, disp_time, 'selected="selected"')) | |||
elif type_date == "departure" and str_time == dep_time: | |||
tab_result.append((str_time, disp_time, 'selected="selected"')) | |||
else: | |||
TabResult.append((StrTime, DispTime, "")) | |||
return TabResult | |||
tab_result.append((str_time, disp_time, "")) | |||
return tab_result | |||
def IsCheck(self, InputControl): | |||
ListControlA = ['Arrival', 'Departure'] | |||
ListControlB = ['PMR', 'Cov', 'Bras', 'Other'] | |||
if InputControl not in map(':'.join, itertools.product(ListControlA, ListControlB)): | |||
list_control_a = ['Arrival', 'Departure'] | |||
list_control_b = ['PMR', 'Cov', 'Bras', 'Other'] | |||
if InputControl not in map(':'.join, itertools.product(list_control_a, list_control_b)): | |||
return "" | |||
if self.Sejour: | |||
if InputControl.startswith('Arrival'): | |||
CtrlVal = 2 ** ListControlB.index(InputControl[8:]) | |||
if self.Sejour.arrival_check & CtrlVal == CtrlVal: | |||
ctrl_val = 2 ** list_control_b.index(InputControl[8:]) | |||
if self.Sejour.arrival_check & ctrl_val == ctrl_val: | |||
return "checked=\"checked\"" | |||
else: | |||
return "" | |||
elif InputControl.startswith('Departure'): | |||
CtrlVal = 2 ** ListControlB.index(InputControl[10:]) | |||
if self.Sejour.depart_check & CtrlVal == CtrlVal: | |||
ctrl_val = 2 ** list_control_b.index(InputControl[10:]) | |||
if self.Sejour.depart_check & ctrl_val == ctrl_val: | |||
return "checked=\"checked\"" | |||
else: | |||
return "" | |||
@@ -146,9 +151,9 @@ class Orga_helpers(DummySejour): | |||
def ChoosedList(self): | |||
""" Return choice validated by user """ | |||
ListOrga = [] | |||
list_orga = [] | |||
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 | |||
list_orga.append(self.Orga_tasks[num]) | |||
return list_orga |
@@ -1,9 +1,6 @@ | |||
# -*- coding: utf8 -*- | |||
import io | |||
from pyramid.response import Response | |||
try: | |||
from StringIO import StringIO | |||
except ImportError: | |||
from io import StringIO | |||
from pyramid.view import view_config | |||
from .models import DBSession, Event, Salles | |||
from reportlab.pdfgen import canvas | |||
@@ -14,174 +11,174 @@ from .upload import MediaPath | |||
from jm2l.const import CurrentYear | |||
# Create PDF container | |||
EXPIRATION_TIME = 300 # seconds | |||
EXPIRATION_TIME = 300 # seconds | |||
WIDTH = 210 * mm | |||
HEIGHT = 297 * mm | |||
ICONSIZE = 10 * mm | |||
def JM2L_large_Logo(canvas, Offset=(0,0)): | |||
def JM2L_large_Logo(canvas, Offset=(0, 0)): | |||
OffX, OffY = Offset | |||
canvas.setFont('Logo', 110) | |||
canvas.setFillColorRGB(.83,0,.33) | |||
canvas.drawCentredString(WIDTH/2-OffY, HEIGHT-100-OffX, "JM2L") | |||
canvas.setFillColorRGB(.83, 0, .33) | |||
canvas.drawCentredString(WIDTH / 2 - OffY, HEIGHT - 100 - OffX, "JM2L") | |||
canvas.setFont("Helvetica-Bold", 30) | |||
yearobject = canvas.beginText() | |||
yearobject.setFillColorRGB(1,1,1) | |||
yearobject.setTextRenderMode(0) | |||
yearobject.setTextOrigin(WIDTH/2-OffY-120, HEIGHT-36-OffX) | |||
yearobject.setWordSpace(48) | |||
yearobject.textLines("2 0 1 5") | |||
yearobject.setWordSpace(1) | |||
canvas.drawText(yearobject) | |||
year_object = canvas.beginText() | |||
year_object.setFillColorRGB(1, 1, 1) | |||
year_object.setTextRenderMode(0) | |||
year_object.setTextOrigin(WIDTH / 2 - OffY - 120, HEIGHT - 36 - OffX) | |||
year_object.setWordSpace(48) | |||
year_object.textLines("2 0 1 5") | |||
year_object.setWordSpace(1) | |||
canvas.drawText(year_object) | |||
def one_time_step(canvas, str, hour, max_size, offset): | |||
max_x, max_y = max_size | |||
off_x, off_y = offset | |||
step_y = max_y/9 | |||
half_step = step_y/2 | |||
canvas.drawCentredString(off_x-30, max_y-step_y*hour+off_y-3, str) | |||
hour_place = step_y*hour+off_y | |||
canvas.line(off_x-5, hour_place, off_x, hour_place) | |||
if hour<9: | |||
canvas.line(off_x-2, hour_place+half_step, off_x, hour_place+half_step) | |||
step_y = max_y / 9 | |||
half_step = step_y / 2 | |||
canvas.drawCentredString(off_x - 30, max_y - step_y * hour + off_y - 3, str) | |||
hour_place = step_y * hour + off_y | |||
canvas.line(off_x - 5, hour_place, off_x, hour_place) | |||
if hour < 9: | |||
canvas.line(off_x - 2, hour_place + half_step, off_x, hour_place + half_step) | |||
@view_config(route_name='stand_print', http_cache = (EXPIRATION_TIME, {'public':True})) | |||
@view_config(route_name='stand_print', http_cache=(EXPIRATION_TIME, {'public': True})) | |||
def stand_print(request): | |||
# Ok let's generate a print for place schedule | |||
# Register LiberationMono font | |||
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) | |||
# Import font | |||
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) | |||
pdf = StringIO.StringIO() | |||
c = canvas.Canvas( pdf, pagesize=(HEIGHT, WIDTH) ) | |||
ttf_file = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||
pdfmetrics.registerFont(TTFont("Liberation", ttf_file)) | |||
# Import font | |||
ttf_file_logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||
pdfmetrics.registerFont(TTFont("Logo", ttf_file_logo)) | |||
pdf = io.BytesIO() | |||
c = canvas.Canvas(pdf, pagesize=(HEIGHT, WIDTH)) | |||
c.translate(mm, mm) | |||
# Feed some metadata | |||
c.setCreator("linux-azur.org") | |||
c.setTitle("Affiches stand") | |||
c.saveState() | |||
year = int(request.matchdict.get('year', CurrentYear)) | |||
Events = DBSession.query(Event)\ | |||
.filter(Event.for_year == year)\ | |||
Events = DBSession.query(Event) \ | |||
.filter(Event.for_year == year) \ | |||
.filter(Event.event_type == "Stand") | |||
for ev in Events: | |||
c.setFont('Logo', 50) | |||
c.setFillColorRGB(.5,.5,.5) | |||
c.drawString(HEIGHT-150, 30, "JM2L") | |||
c.setFillColorRGB(.5, .5, .5) | |||
c.drawString(HEIGHT - 150, 30, "JM2L") | |||
c.setFont('Logo', 100) | |||
c.setFillColorRGB(0.5,0.5,0.5) | |||
c.drawCentredString(HEIGHT/2, WIDTH-90, "STAND", 0) | |||
c.setFillColorRGB(0,0,0) | |||
c.setFillColorRGB(0.5, 0.5, 0.5) | |||
c.drawCentredString(HEIGHT / 2, WIDTH - 90, "STAND", 0) | |||
c.setFillColorRGB(0, 0, 0) | |||
c.setFont('Helvetica', 42) | |||
c.drawCentredString(HEIGHT/2, WIDTH/2, ev.name, 0) | |||
c.drawCentredString(HEIGHT / 2, WIDTH / 2, ev.name, 0) | |||
c.showPage() | |||
c.save() | |||
pdf.seek(0) | |||
return Response(app_iter=pdf, content_type = 'application/pdf' ) | |||
return Response(app_iter=pdf, content_type='application/pdf') | |||
@view_config(route_name='place_print', http_cache = (EXPIRATION_TIME, {'public':True})) | |||
@view_config(route_name='place_print', http_cache=(EXPIRATION_TIME, {'public': True})) | |||
def place_print(request): | |||
# Ok let's generate a print for place schedule | |||
# Register LiberationMono font | |||
ttfFile = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||
pdfmetrics.registerFont(TTFont("Liberation", ttfFile)) | |||
# Import font | |||
ttfFile_Logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||
pdfmetrics.registerFont(TTFont("Logo", ttfFile_Logo)) | |||
pdf = StringIO.StringIO() | |||
c = canvas.Canvas( pdf, pagesize=(WIDTH, HEIGHT) ) | |||
ttf_file = "jm2l/static/fonts/LiberationMono-Regular.ttf" | |||
pdfmetrics.registerFont(TTFont("Liberation", ttf_file)) | |||
# Import font | |||
ttf_file_logo = "jm2l/static/fonts/PWTinselLetters.ttf" | |||
pdfmetrics.registerFont(TTFont("Logo", ttf_file_logo)) | |||
pdf = io.BytesIO() | |||
c = canvas.Canvas(pdf, pagesize=(WIDTH, HEIGHT)) | |||
c.translate(mm, mm) | |||
# Feed some metadata | |||
c.setCreator("linux-azur.org") | |||
c.setTitle("Planning Salle") | |||
c.saveState() | |||
year = int(request.matchdict.get('year', CurrentYear)) | |||
# Initialization | |||
# Compute days used by all events matching the specified input year | |||
place_used = DBSession.query(Event.salle_uid)\ | |||
.filter(Event.for_year == year)\ | |||
.filter(Event.event_type != 'Stand')\ | |||
.group_by(Event.salle_uid) | |||
place_used = DBSession.query(Event.salle_uid) \ | |||
.filter(Event.for_year == year) \ | |||
.filter(Event.event_type != 'Stand') \ | |||
.group_by(Event.salle_uid) | |||
for place in place_used: | |||
place_uid = place[0] | |||
place_uid = place[0] | |||
place_obj = Salles.by_id(place_uid) | |||
# Logo on Top | |||
JM2L_large_Logo(c) | |||
max_size = (WIDTH-110, HEIGHT-300) | |||
max_size = (WIDTH - 110, HEIGHT - 300) | |||
offset = (70, 90) | |||
c.setFillColorRGB(.5,.5,.5) | |||
c.setFillColorRGB(.5, .5, .5) | |||
c.setFont('Liberation', 40) | |||
c.drawCentredString(WIDTH/2, HEIGHT-190, place_obj.name, 1) | |||
c.drawCentredString(WIDTH / 2, HEIGHT - 190, place_obj.name, 1) | |||
c.setFont('Liberation', 35) | |||
c.drawCentredString(WIDTH/2, HEIGHT-145, place_obj.place_type, 0 ) | |||
c.setFont('Helvetica', 20) | |||
c.drawCentredString(WIDTH/2, 55, place_obj.phy.name, 0) | |||
c.drawCentredString(WIDTH / 2, HEIGHT - 145, place_obj.place_type, 0) | |||
c.setFont('Helvetica', 20) | |||
c.drawCentredString(WIDTH / 2, 55, place_obj.phy.name, 0) | |||
# Timetable container | |||
c.setLineWidth(.1) | |||
c.setLineCap(2) | |||
#c.setFillColorRGB(0,0,1) | |||
# c.setFillColorRGB(0,0,1) | |||
c.rect(offset[0], offset[1], max_size[0], max_size[1], fill=0, stroke=1) | |||
c.setLineWidth(.5) | |||
c.setLineWidth(.5) | |||
# create time mark | |||
c.setFillColorRGB(0,0,0) | |||
c.setFillColorRGB(0, 0, 0) | |||
c.setFont('Helvetica', 10) | |||
for i in range(0,10): | |||
one_time_step(c, "%.2dh00" % (i+10), i, max_size, offset) | |||
#c.setFont('Helvetica', 12) | |||
Events = DBSession.query(Event)\ | |||
.filter(Event.for_year == year)\ | |||
.filter(Event.salle_uid == place_uid)\ | |||
.order_by(Event.start_time) | |||
for i in range(0, 10): | |||
one_time_step(c, "%.2dh00" % (i + 10), i, max_size, offset) | |||
# c.setFont('Helvetica', 12) | |||
Events = DBSession.query(Event) \ | |||
.filter(Event.for_year == year) \ | |||
.filter(Event.salle_uid == place_uid) \ | |||
.order_by(Event.start_time) | |||
for ev in Events: | |||
place_time(c, ev, max_size, offset) | |||
#c.rect(70, 50, WIDTH-100, HEIGHT-250, fill=0, stroke=1) | |||
# c.rect(70, 50, WIDTH-100, HEIGHT-250, fill=0, stroke=1) | |||
c.showPage() | |||
c.save() | |||
pdf.seek(0) | |||
return Response(app_iter=pdf, content_type = 'application/pdf' ) | |||
return Response(app_iter=pdf, content_type='application/pdf') | |||
def place_time(c, ev, max_size, offset): | |||
max_x, max_y = max_size | |||
off_x, off_y = offset | |||
minute = max_y/(9*60) | |||
start_pos_y = ((int( ev.start_time.strftime('%H') )-10)*60 + int( ev.start_time.strftime('%M') )) * minute | |||
stop_pos_y = ((int( ev.end_time.strftime('%H') )-10)*60 + int( ev.end_time.strftime('%M') )) * minute | |||
c.setFillColorRGB(0.98,0.98,0.98) | |||
c.rect(offset[0], max_y + off_y - start_pos_y, max_size[0], start_pos_y-stop_pos_y, fill=1, stroke=1) | |||
c.setFillColorRGB(0,0,0) | |||
#c.drawString(off_x+5, max_y + off_y - 15 - start_pos_y, ev.start_time.strftime('%H:%M'), 0) | |||
c.setFont('Helvetica', 12) | |||
c.drawCentredString(WIDTH/2, max_y + off_y - 35 - start_pos_y, ev.name, 0) | |||
intervs = ', '.join( [x.slug for x in ev.intervenants] ) | |||
c.setFont('Helvetica', 10) | |||
c.drawCentredString(WIDTH/2, max_y + off_y - 55 - start_pos_y, intervs, 0) | |||
minute = max_y / (9 * 60) | |||
start_pos_y = ((int(ev.start_time.strftime('%H')) - 10) * 60 + int(ev.start_time.strftime('%M'))) * minute | |||
stop_pos_y = ((int(ev.end_time.strftime('%H')) - 10) * 60 + int(ev.end_time.strftime('%M'))) * minute | |||
c.setFillColorRGB(0.98, 0.98, 0.98) | |||
c.rect(offset[0], max_y + off_y - start_pos_y, max_size[0], start_pos_y - stop_pos_y, fill=1, stroke=1) | |||
c.setFillColorRGB(0, 0, 0) | |||
# c.drawString(off_x+5, max_y + off_y - 15 - start_pos_y, ev.start_time.strftime('%H:%M'), 0) | |||
c.setFont('Helvetica', 12) | |||
c.drawCentredString(WIDTH / 2, max_y + off_y - 35 - start_pos_y, ev.name, 0) | |||
intervs = ', '.join([x.slug for x in ev.intervenants]) | |||
c.setFont('Helvetica', 10) | |||
c.drawCentredString(WIDTH / 2, max_y + off_y - 55 - start_pos_y, intervs, 0) |
@@ -29,6 +29,7 @@ try: | |||
from StringIO import StringIO | |||
except ImportError: | |||
from io import StringIO | |||
import io | |||
import paginate | |||
import unicodedata | |||
import datetime | |||
@@ -1002,7 +1003,7 @@ def list_users_csv(request): | |||
.outerjoin(adalias) \ | |||
.order_by(User.slug) \ | |||
.all() | |||
FileHandle = StringIO.StringIO() | |||
FileHandle = io.BytesIO() | |||
fileWriter = csv.writer(FileHandle, delimiter=',', quotechar='"', quoting=csv.QUOTE_NONNUMERIC) | |||
fileWriter.writerow(["Identifiant_JM2L", "Nom", "Prenom", "Status_%s" % for_year]) | |||
for user, sejour in Data: | |||
@@ -1358,7 +1359,8 @@ def participer(request): | |||
NewUser = TmpUsr | |||
# Send the Welcome Mail | |||
mailer = request.registry['mailer'] | |||
# mailer = request.registry['mailer'] | |||
mailer = request.mailer | |||
# Prepare Plain Text Message : | |||
Mail_template = Template(filename='jm2l/templates/mail_plain.mako') | |||
mail_plain = Mail_template.render(request=request, User=NewUser, action="Welcome") | |||
@@ -11,6 +11,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: | |||
## Do not forget to run for lxml dependencies | |||
## apt-get install libxml2-dev libxslt1-dev | |||
requires = [ | |||
'pyramid', | |||
'pyramid_chameleon', | |||
@@ -36,9 +37,11 @@ requires = [ | |||
'passlib', | |||
'argon2_cffi', | |||
'paginate', | |||
'markupsafe' | |||
'markupsafe', | |||
'webhelpers2', | |||
'email_validator', | |||
'pyramid-scheduler' | |||
] | |||
setup(name='JM2L', | |||
version='0.1', | |||
description='JM2L', | |||