Le repo des sources pour le site web des JM2L
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

2032 lines
88 KiB

  1. # -*- coding: utf8 -*-
  2. from pyramid.httpexceptions import HTTPFound, HTTPNotFound, HTTPForbidden
  3. from pyramid.httpexceptions import HTTPBadRequest, HTTPUnauthorized
  4. from pyramid.renderers import render_to_response
  5. from pyramid.view import notfound_view_config, forbidden_view_config
  6. from pyramid.view import view_config
  7. from pyramid.response import Response
  8. from mako.template import Template
  9. from pyramid_mailer.message import Message
  10. from .upload import IMAGEPATH, MediaPath
  11. # Import Web Forms
  12. from .forms import *
  13. # Database access imports
  14. from .models import *
  15. from .helpers import Orga_helpers
  16. from sqlalchemy import func, or_, text, and_
  17. from sqlalchemy.orm import aliased
  18. from os import path, makedirs, listdir
  19. # Usefull tools
  20. from slugify import slugify
  21. from icalendar import Calendar
  22. from pytz import timezone
  23. from icalendar import Event as Evt
  24. from pyramid_mailer.message import Message
  25. from security import check_staff, check_logged
  26. # Then, standard libs
  27. import csv
  28. import cStringIO as StringIO
  29. import webhelpers.paginate as paginate
  30. import unicodedata
  31. import datetime
  32. import re
  33. import shutil
  34. import glob
  35. from jm2l.const import CurrentYear
  36. from passlib.hash import argon2
  37. ## =-=- Here, We keep some usefull function -=-=
  38. def remove_accents(input_str):
  39. """ This function is intended to remove all accent from input unicode string """
  40. nkfd_form = unicodedata.normalize('NFKD', input_str)
  41. only_ascii = nkfd_form.encode('ASCII', 'ignore')
  42. return only_ascii
  43. def embeed_video(mime_type, link):
  44. Container = "<video controls='controls' preload='metadata' style='width:60%'>"
  45. Container += "<source type='%s' " % mime_type
  46. Container += "src='%s' />" % link
  47. Container += "</video>"
  48. return Container
  49. @view_config(route_name='Live', renderer="jm2l:templates/Live.mako")
  50. def Live(request):
  51. year = int(request.matchdict.get('year', CurrentYear))
  52. tz = timezone('Europe/Paris')
  53. curtime = datetime.datetime.now().replace(tzinfo=tz, day=28) #, hour=10, minute=00 )
  54. Events = DBSession.query(Event)\
  55. .filter(Event.for_year == year)\
  56. .filter(Event.event_type != 'Stand')\
  57. .filter(and_(Event.start_time <= curtime, Event.end_time >= curtime) )\
  58. .order_by(Event.start_time)
  59. return {'year': year, "DisplayYear": year, 'events':Events, "logged_in":request.authenticated_userid }
  60. ## =-=- Here, We handle ICal requests -=-=
  61. @view_config(route_name='progr_iCal', renderer="string")
  62. def ICal_Progamme_Request(request):
  63. year = int(request.matchdict.get('year', CurrentYear))
  64. # Initialization
  65. # Compute days used by all events matching the specified input year
  66. Events = DBSession.query(Event)\
  67. .filter(Event.for_year == year)\
  68. .filter(Event.event_type != 'Stand')\
  69. .order_by(Event.start_time)
  70. cal = Calendar()
  71. cal.add('prodid', '-//Programme %d//jm2l.linux-azur.org//' % year)
  72. cal.add('version', '2.0')
  73. tz = timezone('Europe/Paris')
  74. for ev in Events:
  75. if ev.event_type:
  76. event = Evt()
  77. event['uid'] = "%d/%d" % ( year, ev.uid )
  78. event.add('summary', ev.name )
  79. event.add('dtstart', ev.start_time.replace(tzinfo=tz) )
  80. event.add('dtend', ev.end_time.replace(tzinfo=tz) )
  81. event.add('created', ev.last_change.replace(tzinfo=tz) )
  82. event.add('description', "http://www.linux-azur.org/event/%s/%s" % (ev.for_year, ev.slug) )
  83. event.add('url', "http://www.linux-azur.org/event/%s/%s" % (ev.for_year, ev.slug) )
  84. event.add('priority', 5)
  85. for interv in ev.intervenants:
  86. event.add('organizer',
  87. interv.slug,
  88. parameters={
  89. "CN": "%s %s" % (interv.prenom, interv.nom),
  90. "DIR": "https://jm2l.linux-azur.org/user/%s" % interv.slug
  91. }
  92. )
  93. event.add('location', ev.Salle.name)
  94. event.add('categories', ev.event_type)
  95. cal.add_component(event)
  96. request.response.content_type = "text/calendar"
  97. return cal.to_ical()
  98. ## =-=- Here, We handle ICal requests -=-=
  99. @view_config(route_name='progr_dyn_iCal', renderer="string")
  100. def ICal_Progamme_Dyn_Request(request):
  101. year = int(request.matchdict.get('year', CurrentYear))
  102. # Initialization
  103. # Compute days used by all events matching the specified input year
  104. Events = DBSession.query(Event)\
  105. .filter(Event.for_year == year)\
  106. .filter(Event.event_type != 'Stand')\
  107. .order_by(Event.start_time)
  108. cal = Calendar()
  109. cal.add('prodid', '-//Programme %d//jm2l.linux-azur.org//' % year)
  110. cal.add('version', '2.0')
  111. today = datetime.datetime.now()
  112. tz = timezone('Europe/Paris')
  113. event = Evt()
  114. event['uid'] = "%d/%d" % ( year, 100 )
  115. event.add('summary', "Les stands JM2L %s" % year )
  116. event.add('dtstart', datetime.datetime.now().replace(tzinfo=tz, hour=10, minute=00 ) )
  117. event.add('dtend', datetime.datetime.now().replace(tzinfo=tz, hour=18, minute=00 ) )
  118. event.add('created', datetime.datetime.now().replace(tzinfo=tz, hour=10, minute=00 ) )
  119. event.add('description', "https://www.youtube.com/watch?v=91X65eEKxvU&t=6s" )
  120. event.add('location', "http://jm2l.linux-azur.org/img/325.gif" )
  121. event.add('url', "http://jm2l.linux-azur.org/%s/le-programme" % CurrentYear)
  122. event.add('priority', 5)
  123. cal.add_component(event)
  124. TabCorr = {
  125. 135:"Koneko",
  126. 131:"cam-jm2l-a",
  127. 132:"cam-jm2l-b",
  128. 115:"cam-jm2l-e",
  129. 114:"cam-jm2l-f",
  130. 116:"cam-jm2l-g",
  131. 139:"cam-jm2l-y"
  132. }
  133. for i, ev in enumerate(Events):
  134. if ev.event_type:
  135. event = Evt()
  136. event['uid'] = "%d/%d" % ( year, ev.uid )
  137. event.add('summary', ev.name )
  138. event.add('dtstart', ev.start_time.replace(tzinfo=tz, day=today.day, month = today.month, hour=(ev.start_time.hour)%24 ) )
  139. event.add('dtend', ev.end_time.replace(tzinfo=tz, day=today.day, month = today.month, hour=(ev.end_time.hour)%24 ) )
  140. event.add('created', ev.last_change.replace(tzinfo=tz) )
  141. event.add('description', "http://jm2l.linux-azur.org:8080/%s.webm" % TabCorr.get(ev.Salle.phy_salle_id, ev.Salle.phy_salle_id) )
  142. event.add('location', "http://jm2l.linux-azur.org/img/%d.gif" % ev.Salle.phy_salle_id )
  143. event.add('url', "http://www.linux-azur.org/event/%s/%s" % (ev.for_year, ev.slug) )
  144. event.add('priority', 5)
  145. cal.add_component(event)
  146. request.response.content_type = "text/calendar"
  147. return cal.to_ical()
  148. ## =-=- Here, We handle Json requests -=-=
  149. @view_config(route_name='users_json', renderer="json")
  150. def JSON_User_Request(request):
  151. """ Build a JSON answer with active users and pagination handling """
  152. # Check arguments consitency
  153. pageSize = request.params.get('pageSize',"8")
  154. current_page = request.params.get('pageNum',"1")
  155. UserQuery = request.params.get('searchTerm', u"")
  156. # Don't answer to users that aren't logged
  157. if not request.user:
  158. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  159. # Check consistancy of parameters
  160. if pageSize.isdigit() and current_page.isdigit():
  161. current_page = int(current_page)
  162. pageSize = int(pageSize)
  163. else:
  164. return HTTPBadRequest('pageSize and pageNum accept only digits.')
  165. # Query database
  166. Users = DBSession.query(User.uid, User.nom, User.prenom)\
  167. .filter(User.slug.contains( remove_accents(UserQuery) ))
  168. page_url = paginate.PageURL_WebOb(request)
  169. records = paginate.Page(Users, current_page, url=page_url, items_per_page=pageSize)
  170. ListMatchUser = map( lambda u:{"id": u.uid, "text":"%s %s" % ( u.prenom, u.nom )}, records )
  171. return { "Results": ListMatchUser, "Total":records.item_count,
  172. "logged_in":request.authenticated_userid }
  173. @view_config(route_name='tiers_json', renderer="json")
  174. def JSON_Tiers_Request(request):
  175. """ Build a JSON answer with active users and pagination handling """
  176. # Check arguments consitency
  177. pageSize = request.params.get('pageSize',"8")
  178. current_page = request.params.get('pageNum',"1")
  179. TiersQuery = request.params.get('searchTerm', u"")
  180. # Don't answer to users that aren't logged
  181. if not request.user:
  182. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  183. # Check consistancy of parameters
  184. if pageSize.isdigit() and current_page.isdigit():
  185. current_page = int(current_page)
  186. pageSize = int(pageSize)
  187. else:
  188. return HTTPBadRequest('pageSize and pageNum accept only digits.')
  189. # Query database
  190. JTiers = DBSession.query(Tiers.uid, Tiers.name)\
  191. .filter(Tiers.slug.contains( remove_accents(TiersQuery) ))
  192. page_url = paginate.PageURL_WebOb(request)
  193. records = paginate.Page(JTiers, current_page, url=page_url, items_per_page=pageSize)
  194. ListMatchTiers = map( lambda t:{"id": t.uid, "text": t.name }, records )
  195. return { "Results": ListMatchTiers, "Total":records.item_count,
  196. "logged_in":request.authenticated_userid }
  197. @view_config(route_name='progr_json', renderer="json")
  198. def JSON_Progamme_Request(request):
  199. year = int(request.matchdict.get('year', CurrentYear))
  200. # Initialization
  201. DicResult = dict()
  202. # Query database
  203. # Compute days used by all events matching the specified input year
  204. Days = DBSession.query( func.strftime('%d', Event.start_time).label('day') )\
  205. .filter(Event.for_year == year)\
  206. .filter(Event.event_type != None)\
  207. .group_by(func.strftime('%d', Event.start_time)).all()
  208. for Day in Days:
  209. Events = DBSession.query(Event)\
  210. .filter(Event.for_year == year)\
  211. .filter(Event.event_type != 'Stand')\
  212. .filter(text("strftime('%d', start_time) = :dow")).params(dow=Day.day)\
  213. .order_by(Event.start_time)
  214. ListEv = []
  215. for ev in Events:
  216. if ev.event_type:
  217. ListEv.append( {
  218. "uid":"%d/%d" % ( year, ev.uid ),
  219. "desc":ev.name,
  220. "startDate":ev.start_time.strftime('%Y-%m-%dT%H:%M:%S+01:00'),
  221. "endDate":ev.end_time.strftime('%Y-%m-%dT%H:%M:%S+01:00'),
  222. "placeName":ev.Salle and (ev.Salle.name or "unk") ,
  223. "status":ev.event_type
  224. } )
  225. DicResult[Day.day] = ListEv
  226. return { 'all':DicResult }
  227. @view_config(route_name='timeline_json', renderer="json")
  228. def JSON_TimeLine_Request(request):
  229. year = int(request.matchdict.get('year', CurrentYear))
  230. # Initialization
  231. DicResult = dict()
  232. # Query database
  233. # Compute days used by all events matching the specified input year
  234. Days = DBSession.query( func.strftime('%d', Event.start_time).label('day') )\
  235. .filter(Event.for_year == year)\
  236. .filter(Event.event_type != None)\
  237. .group_by(func.strftime('%d', Event.start_time)).all()
  238. ListEv = []
  239. for Day in Days:
  240. Events = DBSession.query(Event)\
  241. .filter(Event.for_year == year)\
  242. .filter(Event.event_type != 'Stand')\
  243. .filter(text("strftime('%d', start_time) = :dow")).params(dow=Day.day)\
  244. .order_by(Event.start_time)
  245. #ListEv = []
  246. for ev in Events:
  247. if ev.event_type:
  248. CurMedia = ev.video.first() or ""
  249. if CurMedia:
  250. Container = "<video controls='controls' preload='metadata' style='width:60%'>"
  251. Container += "<source type='%s' " % CurMedia.mime_type
  252. Container += "src='%s' />" % CurMedia.get_path
  253. Container += "</video>"
  254. else:
  255. Container = ""
  256. ListEv.append( {
  257. #"uid":"%d/%d" % ( year, ev.uid ),
  258. "headline":'<a href="/event/%s/%s">%s</a>' % (ev.for_year, ev.slug, ev.name),
  259. "startDate":ev.start_time.strftime('%Y,%m,%d,%H,%M'),
  260. "endDate":ev.end_time.strftime('%Y,%m,%d,%H,%M'),
  261. "text": ev.Salle and (ev.Salle.name or "unk"),
  262. #"text":ev.description[:100],
  263. "tags":ev.Salle and (ev.Salle.salle_id or "unk") ,
  264. #"status":ev.event_type,
  265. "asset": {
  266. "media": Container,
  267. "credit": ",".join(["%s %s" % (i.prenom, i.nom) for i in ev.intervenants]),
  268. "caption":"" }
  269. } )
  270. if year==2018:
  271. DicResult = {
  272. "lang":"fr",
  273. "headline":"JM2L 2018",
  274. "type":"default",
  275. "startDate":"2018,12,15,10",
  276. "text":"<i><span class='c1'>11ème Édition</span></i>",
  277. "asset":
  278. {
  279. "media":embeed_video("video/ogg","/resources/2017/Video/JM2L2017_Reportage_FR3.ogv"),
  280. "credit":"Reportage France 3",
  281. "caption":"JM2L 2018"
  282. }
  283. }
  284. elif year==2017:
  285. DicResult = {
  286. "lang":"fr",
  287. "headline":"JM2L 2017",
  288. "type":"default",
  289. "startDate":"2017,11,25,10",
  290. "text":"<i><span class='c1'>10ème Édition</span></i>",
  291. "asset":
  292. {
  293. #"media":"https://www.youtube.com/watch?v=91X65eEKxvU&t=6s",
  294. "media":embeed_video("video/ogg","/resources/2017/Video/JM2L2017_Reportage_PleinSud.ogv"),
  295. "credit":"Reportage France 3",
  296. "caption":"JM2L 2017"
  297. }
  298. }
  299. elif year==2015:
  300. DicResult = {
  301. "lang":"fr",
  302. "headline":"JM2L 2015",
  303. "type":"default",
  304. "startDate":"2015,11,28,10",
  305. "text":"<i><span class='c1'>9ème Édition</span></i>",
  306. "asset":
  307. {
  308. #"media":"https://www.youtube.com/watch?v=91X65eEKxvU&t=6s",
  309. "media":embeed_video("video/ogg","/resources/2015/Video/jm2l_france3-2015.ogv"),
  310. "credit":"Reportage France 3",
  311. "caption":"JM2L 2015"
  312. }
  313. }
  314. elif year==2011:
  315. DicResult = {
  316. "lang":"fr",
  317. "headline":"JM2L 2011",
  318. "type":"default",
  319. "startDate":"2011,11,25,10",
  320. "text":"<i><span class='c1'>6ème Édition</span></i>",
  321. "asset":
  322. {
  323. "media":"https://www.youtube.com/embed/rcaNeXuAEhs",
  324. "credit":"JM2L",
  325. "caption":"Reportage FR3"
  326. },
  327. }
  328. elif year==2010:
  329. DicResult = {
  330. "lang":"fr",
  331. "headline":"JM2L 2010",
  332. "type":"default",
  333. "text":"<i><span class='c1'>5ème Édition</span></i>",
  334. "asset":
  335. {
  336. "media":embeed_video("video/ogg","/resources/2010/Video/reportages/JM2L2010-PleinSudTV.ogv"),
  337. "credit":"<a href='http://pleinsudtv.com/index.php/casa/affichage-en-vignettes/93-casa-culture/582-les-jm-du-logiciel-libre-2010'>Le reportage Plein-sud TV</a>",
  338. "caption":"JM2L",
  339. }
  340. }
  341. elif year==2007:
  342. DicResult = {
  343. "lang":"fr",
  344. "headline":"JM2L 2007",
  345. "type":"default",
  346. "text":"<i><span class='c1'>2ème Édition</span></i>",
  347. "asset":
  348. {
  349. "media":embeed_video("video/ogg","/resources/2007/Video/20071110-linux.ogv"),
  350. "credit":"<a href='http://pleinsudtv.com/index.php/casa/affichage-en-vignettes/93-casa-culture/245-logiciel-libre-linux-jm2l'>Le reportage Plein-sud TV</a>",
  351. "caption":"JM2L 2007",
  352. }
  353. }
  354. else:
  355. DicResult = {
  356. "lang":"fr",
  357. "headline":"JM2L %d" % year,
  358. "type":"default",
  359. "asset":
  360. {
  361. "media":"",
  362. "credit":"JM2L",
  363. "caption":""
  364. }
  365. }
  366. DicResult["date"] = ListEv
  367. return { 'timeline':DicResult }
  368. ## =-=- Here, We handle HTTP requests - Public Part -=-=
  369. @view_config(route_name='home', renderer="jm2l:templates/NewIndex.mako")
  370. def index_page(request):
  371. year = request.matchdict.get('year')
  372. if year:
  373. year=int(year[:-1])
  374. content = DBSession.query(JM2L_Year).filter(JM2L_Year.year_uid==year).first()
  375. if content:
  376. content = content.description
  377. else:
  378. content = ""
  379. if 2004<year<=CurrentYear:
  380. if year==2006:
  381. return {'year': year, 'content':content, 'edition':u"1<sup>ère</sup>" }
  382. elif year==2015:
  383. return {'year': year, 'content':content, 'edition':u"9<sup>ème</sup>" }
  384. elif year==2017:
  385. return {'year': year, 'content':content, 'edition':u"10<sup>ème</sup>" }
  386. elif year==2018:
  387. return {'year': year, 'content':content, 'edition':u"11<sup>ème</sup>" }
  388. else:
  389. edition = year - 2005
  390. return {'year': year, 'content':content, 'edition':u"%d<sup>ème</sup>" % edition }
  391. else:
  392. raise HTTPNotFound()
  393. else:
  394. content = DBSession.query(JM2L_Year).filter(JM2L_Year.year_uid==CurrentYear).first().description
  395. TargetDir = "jm2l/static/img/%s/Photos" % (year or CurrentYear)
  396. TargetUrl = "/static/img/%s/Photos/" % (year or CurrentYear)
  397. if path.isdir(TargetDir):
  398. ListPhotos = map(lambda x: TargetUrl + x, listdir(TargetDir))
  399. else:
  400. ListPhotos = []
  401. return {'year': CurrentYear, 'content':content, 'edition':u"11<sup>ème</sup>", 'ListPhotos': ListPhotos}
  402. @view_config(route_name='edit_index', renderer="jm2l:templates/Staff/EditIndex.mako")
  403. def edit_index(request):
  404. check_staff(request)
  405. year = int(request.matchdict.get('year', CurrentYear))
  406. content = DBSession.query(JM2L_Year).filter(JM2L_Year.year_uid==year).first()
  407. form = IndexForm(request.POST, content, meta={'csrf_context': request.session})
  408. if request.method == 'POST' and form.validate():
  409. form.populate_obj(content)
  410. return HTTPFound(location=request.route_url('home', year="%d/" % year))
  411. MainTab = {'home':'active', "logged_in":request.authenticated_userid,
  412. 'form':form, 'DisplayYear':year}
  413. return MainTab
  414. @view_config(route_name='programme', renderer="jm2l:templates/Public/Programme.mako")
  415. def programme(request):
  416. year = int(request.matchdict.get('year'))
  417. if 2006 > year:
  418. return HTTPBadRequest('The first JM2L event was in 2006.')
  419. # Query database about selected Year.
  420. Events = DBSession.query(Event)\
  421. .filter(Event.for_year == year)\
  422. .order_by(Event.start_time)
  423. Days = DBSession.query(func.strftime('%d-%m-%Y', Event.start_time))\
  424. .filter(Event.for_year == year)\
  425. .filter(Event.event_type != None)\
  426. .group_by(func.strftime('%d', Event.start_time)).all()
  427. ListDay = []
  428. for day in Days:
  429. RefDay = datetime.datetime.strptime(day[0],'%d-%m-%Y')
  430. ListDay.append( ( RefDay.strftime('%A %d %b %Y').decode('utf-8'),
  431. RefDay.strftime('%d') ) )
  432. MainTab = {'programme':'active','DisplayYear':year, \
  433. 'Events':Events, 'Event':Event, 'Days':ListDay, "logged_in":request.authenticated_userid }
  434. return MainTab
  435. @view_config(route_name='presse', renderer="jm2l:templates/Public/Presse.mako")
  436. def static_presse(request):
  437. year = int(request.matchdict.get('year', None))
  438. content = DBSession.query(JM2L_Year).filter(JM2L_Year.year_uid==year).first()
  439. MainTab = {'presse':'active', "logged_in":request.authenticated_userid, 'content':content, 'DisplayYear':year}
  440. return MainTab
  441. @view_config(route_name='edit_presse', renderer="jm2l:templates/Staff/EditPresse.mako")
  442. def edit_presse(request):
  443. check_staff(request)
  444. year = int(request.matchdict.get('year', None))
  445. content = DBSession.query(JM2L_Year).filter(JM2L_Year.year_uid==year).first()
  446. form = DossPresse(request.POST, content, meta={'csrf_context': request.session})
  447. if request.method == 'POST' and form.validate():
  448. form.populate_obj(content)
  449. MainTab = {'presse':'active', "logged_in":request.authenticated_userid, 'form':form, 'DisplayYear':year}
  450. return MainTab
  451. @view_config(route_name='plan', renderer="jm2l:templates/Public/Plan.mako")
  452. def static_plan(request):
  453. session = request.session
  454. session['year'] = CurrentYear
  455. MainTab = {'plan':'active', "logged_in":request.authenticated_userid }
  456. return MainTab
  457. ## =-=- Here, We handle HTTP requests - Staff Logged Part -=-=
  458. @view_config(route_name='list_task', renderer='jm2l:templates/Staff/list.mako')
  459. def list_view(request):
  460. check_staff(request)
  461. year = int(request.matchdict.get('year', CurrentYear))
  462. DicTask = {}
  463. taskgroup = DBSession.query( TasksArea ).filter( TasksArea.year_uid == year).all()
  464. for grp in taskgroup:
  465. tasks = DBSession.query( Tasks )\
  466. .filter( Tasks.area_uid==grp.uid ) \
  467. .filter( Tasks.year_uid == year) \
  468. .order_by(Tasks.closed, Tasks.due_date).all()
  469. DicTask[grp] = tasks
  470. return {'tasks': DicTask, 'year':year }
  471. @view_config(route_name='list_expenses', renderer='jm2l:templates/Staff/compta.mako')
  472. def expenses(request):
  473. check_staff(request)
  474. year = int(request.matchdict.get('year', CurrentYear))
  475. output = []
  476. dic_out = {}
  477. for name in glob.glob('jm2l/upload/images/users/*/RIB/*'):
  478. tab_path = name.split('/')
  479. u = User.by_slug(tab_path[4])
  480. if u is None:
  481. continue
  482. if tab_path[6]=='thumbnails' or tab_path[6].endswith('.type'):
  483. continue
  484. if dic_out.get(tab_path[4]) is None:
  485. dic_out[tab_path[4]] = {}
  486. if dic_out[tab_path[4]].get('RIB') is None:
  487. dic_out[tab_path[4]]['RIB'] = {'files':[], 'thumb':[]}
  488. u = User.by_slug(tab_path[4])
  489. if u is None:
  490. dic_out[tab_path[4]]['User'] = {
  491. 'uid':'# NOT FOUND',
  492. 'nom':name,
  493. 'prenom':'# NOT FOUND',
  494. 'slug':'# NOT FOUND'
  495. }
  496. else:
  497. dic_out[tab_path[4]]['User'] = {
  498. 'uid':u.uid,
  499. 'nom':u.nom,
  500. 'prenom':u.prenom,
  501. 'slug':u.slug
  502. }
  503. dic_out[tab_path[4]]['RIB']['files'].append( name )
  504. dic_out[tab_path[4]]['RIB']['thumb'].append( name + ".jpg" )
  505. for name in glob.glob('jm2l/upload/images/users/*/Justif/*'):
  506. tab_path = name.split('/')
  507. u = User.by_slug(tab_path[4])
  508. if u is None:
  509. continue
  510. if tab_path[6]=='thumbnails' or tab_path[6].endswith('.type'):
  511. continue
  512. if dic_out.get(tab_path[4]) is None:
  513. dic_out[tab_path[4]] = {}
  514. if dic_out[tab_path[4]].get('Justif') is None:
  515. dic_out[tab_path[4]]['Justif'] = {'files':[], 'thumb':[]}
  516. u = User.by_slug(tab_path[4])
  517. if u is None:
  518. dic_out[tab_path[4]]['User'] = {
  519. 'uid':'# NOT FOUND',
  520. 'nom':name,
  521. 'prenom':'# NOT FOUND',
  522. 'slug':'# NOT FOUND'
  523. }
  524. else:
  525. dic_out[tab_path[4]]['User'] = {
  526. 'uid':u.uid,
  527. 'nom':u.nom,
  528. 'prenom':u.prenom,
  529. 'slug':u.slug
  530. }
  531. dic_out[tab_path[4]]['Justif']['files'].append( name )
  532. dic_out[tab_path[4]]['Justif']['thumb'].append( name + ".jpg" )
  533. return dict(found=dic_out, year=year )
  534. @view_config(route_name='handle_task', renderer='jm2l:templates/Staff/tasks.mako')
  535. def tasks(request):
  536. check_staff(request)
  537. year = int(request.matchdict.get('year', CurrentYear))
  538. task_id = request.matchdict.get('task_id')
  539. # Convert the pole_id GET parameter to int or 0
  540. if request.params.get('pole_id') and request.params.get('pole_id').isdigit():
  541. pole_id = int(request.params.get('pole_id'))
  542. else:
  543. pole_id = None
  544. # Get areas from db
  545. Areas = DBSession.query(TasksArea.uid, TasksArea.name) \
  546. .filter(TasksArea.year_uid == year) \
  547. .order_by('name').all()
  548. # Get users from db
  549. Users = DBSession.query(User)\
  550. .filter(User.Staff==1)\
  551. .order_by('nom').all()
  552. if task_id:
  553. TmpTask = Tasks.by_id(int(task_id))
  554. if not TmpTask:
  555. raise HTTPNotFound()
  556. form = EditStaffTasks(request.POST, TmpTask, meta={'csrf_context': request.session})
  557. else:
  558. TmpTask = Tasks()
  559. # Check if the supplied pole_id is in the Areas' range
  560. form = StaffTasks(request.POST, TmpTask, meta={'csrf_context': request.session})
  561. Area = TasksArea.by_id(pole_id)
  562. if Area:
  563. form.area_uid.data = Area.uid
  564. # Put some areas on form
  565. form.area_uid.choices = Areas
  566. # Put some users on form
  567. form.closed_by.choices = [(u.uid, "%s %s" % (u.nom, u.prenom))
  568. for u in Users]
  569. form.due_date.type = "date"
  570. if request.method=='POST' and form.validate():
  571. form.populate_obj(TmpTask)
  572. TmpTask.closed = False
  573. if not TmpTask.year_uid:
  574. TmpTask.year_uid = year;
  575. if 'uid' in form._fields.keys():
  576. DBSession.merge(TmpTask)
  577. else:
  578. DBSession.add(TmpTask)
  579. DBSession.flush()
  580. return HTTPFound(location=request.route_url('list_task', year=year)+"#"+slugify(TmpTask.area.name))
  581. return {'form':form, 'area':TmpTask.area and slugify(TmpTask.area.name) or '', 'year':year}
  582. @view_config(route_name='handle_pole', renderer='jm2l:templates/Staff/pole.mako')
  583. def tasks_area(request):
  584. check_staff(request)
  585. year = int(request.matchdict.get('year', CurrentYear))
  586. pole_id = request.matchdict.get('pole_id')
  587. if pole_id:
  588. Pole = TasksArea.by_id(int(pole_id))
  589. if not Pole:
  590. raise HTTPNotFound()
  591. form = EditStaffArea(request.POST, Pole, meta={'csrf_context': request.session})
  592. else:
  593. Pole = TasksArea()
  594. form = StaffArea(request.POST, Pole, meta={'csrf_context': request.session})
  595. if request.method == 'POST' and form.validate():
  596. form.populate_obj(Pole)
  597. if not Pole.year_uid:
  598. Pole.year_uid = year;
  599. if 'uid' in form._fields.keys():
  600. DBSession.merge(Pole)
  601. else:
  602. DBSession.add(Pole)
  603. return HTTPFound(location=request.route_url('list_task', year=year)+"#"+slugify(Pole.name))
  604. return {'form':form, 'year':year }
  605. @view_config(route_name='action_task')
  606. def action_task(request):
  607. check_staff(request)
  608. year = int(request.matchdict.get('year', CurrentYear))
  609. action = request.matchdict.get('action')
  610. task_id = request.matchdict.get('task_id')
  611. Task = Tasks.by_id(int(task_id))
  612. if action=='close':
  613. Task.closed = True
  614. request.session.flash(('info', u'La tâche a été fermé, Félicitations !'))
  615. DBSession.merge(Task)
  616. if action=='open':
  617. Task.closed = False
  618. request.session.flash(('info', u'La tâche a été ré-ouverte !'))
  619. DBSession.merge(Task)
  620. if action=='delete':
  621. request.session.flash(('info', u'La tâche a été supprimée !'))
  622. DBSession.delete(Task)
  623. return HTTPFound(location=request.route_url('list_task', year=year)+"#"+slugify(Task.area.name))
  624. @view_config(route_name='action_task_area')
  625. def action_task_area(request):
  626. check_staff(request)
  627. year = int(request.matchdict.get('year', CurrentYear))
  628. action = request.matchdict.get('action')
  629. pole_id = request.matchdict.get('pole_id')
  630. Pole = TasksArea.by_id(int(pole_id))
  631. if not Pole:
  632. raise HTTPNotFound()
  633. if action=='delete':
  634. request.session.flash(('info', u'Le pôle a été supprimé !'))
  635. DBSession.delete(Pole)
  636. return HTTPFound(location=request.route_url('list_task', year=year))
  637. @view_config(route_name='list_salles', renderer='jm2l:templates/Salles/list.mako')
  638. def list_salles(request):
  639. check_staff(request)
  640. DicSalle = {}
  641. years = DBSession.query( JM2L_Year ).all()
  642. for year in years:
  643. salles = DBSession.query( Salles )\
  644. .filter( Salles.year_uid==year.year_uid )\
  645. .order_by(Salles.name).all()
  646. DicSalle[year] = salles
  647. return {'DicSalle': DicSalle }
  648. @view_config(route_name='list_salles_phy', renderer='jm2l:templates/Salles/list_phy.mako')
  649. def list_salles_phy(request):
  650. check_staff(request)
  651. salles_phy = DBSession.query( SallePhy )\
  652. .order_by(SallePhy.name).all()
  653. return {'DicSallePhy': salles_phy }
  654. @view_config(route_name='handle_salle', renderer='jm2l:templates/Salles/salle.mako')
  655. def handle_salle(request):
  656. check_staff(request)
  657. salle_id = request.matchdict.get('salle_id')
  658. if salle_id:
  659. Salle = Salles.by_id(int(salle_id))
  660. if not Salle:
  661. raise HTTPNotFound()
  662. form = EditSalleForm(request.POST, Salle, meta={'csrf_context': request.session})
  663. else:
  664. Salle = Salles()
  665. form = SalleForm(request.POST, Salle, meta={'csrf_context': request.session})
  666. form.year_uid.choices = map(tuple, DBSession.query(JM2L_Year.year_uid, JM2L_Year.year_uid).order_by(sa.desc(JM2L_Year.year_uid)).all())
  667. form.phy_salle_id.choices = map(tuple, DBSession.query(SallePhy.uid, SallePhy.name).all())
  668. if request.method == 'POST' and form.validate():
  669. form.populate_obj(Salle)
  670. if 'uid' in form._fields.keys():
  671. DBSession.merge(Salle)
  672. else:
  673. DBSession.add(Salle)
  674. return HTTPFound(location=request.route_url('list_salles'))
  675. return {'form':form }
  676. @view_config(route_name='handle_salle_phy', renderer='jm2l:templates/Salles/salle_phy.mako')
  677. def handle_salle_phy(request):
  678. check_staff(request)
  679. salle_id = request.matchdict.get('salle_id')
  680. if salle_id:
  681. Salle = SallePhy.by_id(int(salle_id))
  682. orig_slug = Salle.slug
  683. if not Salle:
  684. raise HTTPNotFound()
  685. form = EditSallePhyForm(request.POST, Salle, meta={'csrf_context': request.session})
  686. else:
  687. Salle = SallePhy()
  688. orig_slug = None
  689. form = SallePhyForm(request.POST, Salle, meta={'csrf_context': request.session})
  690. if request.method == 'POST' and form.validate():
  691. form.populate_obj(Salle)
  692. dest_slug = slugify(Salle.name)
  693. # Do a check in order to move attached files if any
  694. if orig_slug and orig_slug!=dest_slug:
  695. try:
  696. mp = MediaPath().move_mediapath('salle', orig_slug, dest_slug)
  697. except RuntimeError, err:
  698. request.session.flash(('error', u"Le nom de cette salle est déjà utilisé : " + err.message))
  699. return {'form': form}
  700. Salle.slug = slugify(Salle.name)
  701. if 'uid' in form._fields.keys():
  702. DBSession.merge(Salle)
  703. else:
  704. DBSession.add(Salle)
  705. return HTTPFound(location=request.route_url('list_salles'))
  706. return {'form':form }
  707. @view_config(route_name='action_salle')
  708. def action_salle(request):
  709. check_staff(request)
  710. action = request.matchdict.get('action')
  711. salle_id = request.matchdict.get('salle_id')
  712. Salle = Salles.by_id(int(salle_id))
  713. if not Salle:
  714. raise HTTPNotFound()
  715. if action=='delete':
  716. request.session.flash(('info', u'La Salle a été supprimée !'))
  717. DBSession.delete(Salle)
  718. return HTTPFound(location=request.route_url('list_salles'))
  719. ## =-=- Here, We handle HTTP requests - User Logged Part -=-=
  720. @view_config(route_name='exchange', renderer="jm2l:templates/Logistique/Logistique.mako")
  721. def exchange(request):
  722. modtype = request.matchdict.get('modtype', None)
  723. action = request.matchdict.get('action', None)
  724. uid = int(request.matchdict.get('id', -1))
  725. Exch = Exchange.by_id(uid)
  726. if not Exch:
  727. MainTab = {
  728. 'Exchanges':Exchange,
  729. 'Type':modtype[-1:],
  730. 'reload':True,
  731. 'logged_in':request.authenticated_userid
  732. }
  733. return MainTab
  734. if action in ['delete', 'accept', 'refuse', 'deal']:
  735. if action=='delete': # delete exchange
  736. DBSession.delete(Exch)
  737. elif action=='accept': # accept exchange
  738. Exch.exch_done=True
  739. DBSession.merge(Exch)
  740. elif action=='refuse': # refuse exchange
  741. Exch.exch_done=False
  742. if Exch.exch_state=="Ask":
  743. Exch.provider_id = None
  744. elif Exch.exch_state=="Proposal":
  745. Exch.asker_id = None
  746. DBSession.merge(Exch)
  747. elif action=='deal':
  748. # ask to deal the exchange
  749. if Exch.exch_state=="Ask":
  750. Exch.provider_id = request.user.uid
  751. elif Exch.exch_state=="Proposal":
  752. Exch.asker_id = request.user.uid
  753. # Return javascript to parent page
  754. response = render_to_response('jm2l:templates/modals_js.mako',
  755. {'modtype':modtype, 'action':action},
  756. request=request)
  757. response.content_type = 'text/javascript'
  758. return response
  759. else:
  760. MainTab = {
  761. 'Exchanges':Exchange,
  762. 'Type':modtype[-1:],
  763. 'reload':True,
  764. 'logged_in':request.authenticated_userid
  765. }
  766. return MainTab
  767. @view_config(route_name='miam')
  768. def miam(request):
  769. check_logged(request)
  770. miam_form = MiamForm(request.POST, request.user, meta={'csrf_context': request.session})
  771. if request.method == 'POST' and miam_form.validate():
  772. FicheSejour = Sejour.by_user(request.user.uid, CurrentYear)
  773. if FicheSejour:
  774. Update=True
  775. else:
  776. FicheSejour = Sejour()
  777. FicheSejour.created = datetime.datetime.now()
  778. Update=False
  779. Repas=0
  780. for num, item in enumerate(['RepasVendredi', 'RepasSamediMidi', 'RepasSamediSoir']):
  781. if request.params.get(item)==u"1":
  782. Repas += 2**num
  783. FicheSejour.repas = Repas
  784. FicheSejour.repas_allerg = request.params.get('Allergies')
  785. FicheSejour.repas_contr = request.params.get('Contraintes')
  786. FicheSejour.user_id = request.user.uid
  787. FicheSejour.for_year = CurrentYear
  788. if Update:
  789. DBSession.merge(FicheSejour)
  790. else:
  791. DBSession.add(FicheSejour)
  792. request.session.flash(('info',u'Votre fiche a été mise à jour avec succès'))
  793. else:
  794. request.session.flash(('error',u'Un problème est survenu'))
  795. return HTTPFound(location='/MesJM2L#Miam')
  796. @view_config(route_name='sejour')
  797. def sejour(request):
  798. check_logged(request)
  799. if request.method == 'POST':
  800. FicheSejour = Sejour.by_user(request.user.uid, CurrentYear)
  801. if FicheSejour:
  802. Update=True
  803. else:
  804. FicheSejour = Sejour()
  805. FicheSejour.created = datetime.datetime.now()
  806. Update=False
  807. FicheSejour.user_id = request.user.uid
  808. FicheSejour.last_change = datetime.datetime.now()
  809. FicheSejour.for_year = CurrentYear
  810. # Arrival
  811. ArrDate = datetime.datetime.strptime(request.params.get('Arrival:Day'),"%d/%m/%y")
  812. ArrTime = datetime.datetime.strptime(request.params.get('Arrival:Hour'),"%H:%M")
  813. FicheSejour.arrival_time = datetime.datetime.combine(ArrDate.date(), ArrTime.time())
  814. ArrivalCheck=0
  815. for num, item in enumerate(['Arrival:PMR', 'Arrival:Cov', 'Arrival:Bras', 'Arrival:Other']):
  816. if request.params.get(item):
  817. ArrivalCheck += 2**num
  818. FicheSejour.arrival_check = ArrivalCheck
  819. FicheSejour.arrival_text = request.params.get('Arrival:Comment')
  820. FicheSejour.arrival_place = request.params.get('Arrival:Place')
  821. # Departure
  822. DepDate = datetime.datetime.strptime(request.params.get('Departure:Day'),"%d/%m/%y")
  823. DepTime = datetime.datetime.strptime(request.params.get('Departure:Hour'),"%H:%M")
  824. FicheSejour.depart_time = datetime.datetime.combine(DepDate.date(), DepTime.time())
  825. DepartCheck=0
  826. for num, item in enumerate(['Departure:PMR', 'Departure:Cov', 'Departure:Bras', 'Departure:Other']):
  827. if request.params.get(item):
  828. DepartCheck += 2**num
  829. FicheSejour.depart_check = DepartCheck
  830. FicheSejour.depart_text = request.params.get('Departure:Comment')
  831. FicheSejour.depart_place = request.params.get('Departure:Place')
  832. if Update:
  833. DBSession.merge(FicheSejour)
  834. request.session.flash(('info',u'Vos modifications de séjour ont été pris en compte.'))
  835. else:
  836. DBSession.add(FicheSejour)
  837. request.session.flash(('info',u'\\o/ Votre séjour est enregistré ! Complétez la partie Logistique.'))
  838. return HTTPFound(location='/MesJM2L#Sejour')
  839. @view_config(route_name='orga')
  840. def orga(request):
  841. check_logged(request)
  842. if request.method == 'POST':
  843. FicheSejour = Sejour.by_user(request.user.uid, CurrentYear)
  844. UpdateOrga=False
  845. if FicheSejour:
  846. Update=True
  847. if FicheSejour.orga_part:
  848. UpdateOrga=True
  849. else:
  850. FicheSejour = Sejour()
  851. FicheSejour.created = datetime.datetime.now()
  852. Update=False
  853. FicheSejour.user_id = request.user.uid
  854. FicheSejour.last_change = datetime.datetime.now()
  855. FicheSejour.for_year = CurrentYear
  856. OrgaPart=0
  857. for item in request.params:
  858. try:
  859. nb = int(item[1:])
  860. except:
  861. continue
  862. OrgaPart += 2**nb
  863. FicheSejour.orga_part = OrgaPart
  864. if UpdateOrga:
  865. request.session.flash(('info',u'Vos modifications de participation à l\'organisation ont été pris en compte.'))
  866. else:
  867. request.session.flash(('info',u'\\o/ Votre participation à l\'organisation est enregistrée !'))
  868. if Update:
  869. DBSession.merge(FicheSejour)
  870. else:
  871. DBSession.add(FicheSejour)
  872. return HTTPFound(location='/MesJM2L#Organisation')
  873. @view_config(route_name='vote_logo')
  874. def vote_logo(request):
  875. if request.user is None:
  876. # Don't answer to users that aren't logged
  877. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  878. else:
  879. vote = int(request.matchdict.get('num', -1))
  880. come = request.params.get('come_from')
  881. if vote:
  882. request.user.vote_logo=vote
  883. DBSession.merge(request.user)
  884. request.session.flash(('info',u'Votre vote à été pris en compte.'))
  885. return HTTPFound('/')
  886. else:
  887. request.session.flash(('warning',u"Votre vote n'a été pris en compte."))
  888. if come:
  889. return HTTPFound(location=come)
  890. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  891. @view_config(route_name='list_users_csv', renderer="string")
  892. def list_users_csv(request):
  893. check_staff(request)
  894. for_year = int(request.matchdict.get('year', CurrentYear))
  895. stmt = (DBSession.query(Sejour).filter(Sejour.for_year==for_year)).subquery()
  896. # stmt_event = (DBSession.query(User_Event).filter()
  897. adalias = aliased(Sejour, stmt)
  898. Data = DBSession.query(User, adalias ) \
  899. .outerjoin(adalias)\
  900. .order_by(User.slug)\
  901. .all()
  902. FileHandle = StringIO.StringIO()
  903. fileWriter = csv.writer(FileHandle, delimiter=',', quotechar='"', quoting=csv.QUOTE_NONNUMERIC)
  904. fileWriter.writerow(["Identifiant_JM2L", "Nom", "Prenom", "Status_%s" % for_year ])
  905. for user, sejour in Data:
  906. tab_identifier = [user.slug, user.nom.encode('utf-8'), user.prenom.encode('utf-8'), '']
  907. if user.is_IntervenantOnYear(CurrentYear):
  908. tab_identifier[3] = "Intervenant"
  909. elif user.Staff:
  910. tab_identifier[3] = "Staff"
  911. elif sejour and sejour.orga_part:
  912. tab_identifier[3] = "Bénévole"
  913. elif sejour:
  914. tab_identifier[3] = "Visiteur"
  915. else:
  916. continue
  917. fileWriter.writerow(tab_identifier)
  918. return Response(app_iter=FileHandle.getvalue(), content_type="text/csv")
  919. @view_config(route_name='list_users', renderer="jm2l:templates/Participant/list_users.mako")
  920. def list_users(request):
  921. check_staff(request)
  922. for_year = int(request.matchdict.get('year', CurrentYear))
  923. stmt = (DBSession.query(Sejour).filter(Sejour.for_year==for_year)).subquery()
  924. adalias = aliased(Sejour, stmt)
  925. Data = DBSession.query(User, adalias ) \
  926. .outerjoin(adalias)\
  927. .all()
  928. Repas = DBSession.query(Sejour.repas) \
  929. .filter(Sejour.for_year == for_year ) \
  930. .all()
  931. DicRepas = {"Ven":0, "Midi":0, "Soir":0}
  932. for r in Repas:
  933. if r[0] is None:
  934. continue
  935. if (r[0] & 1 == 1): DicRepas["Ven"]+=1
  936. if (r[0] & 2 == 2): DicRepas["Midi"]+=1
  937. if (r[0] & 4 == 4): DicRepas["Soir"]+=1
  938. return { 'Users':Data, 'UserEvent' : User_Event, "DicRepas":DicRepas, "for_year": for_year }
  939. @view_config(route_name='list_orga', renderer="jm2l:templates/Participant/list_orga.mako")
  940. def list_orga(request):
  941. check_staff(request)
  942. for_year = int(request.matchdict.get('year', CurrentYear))
  943. Data = DBSession.query(User, Sejour)\
  944. .outerjoin(Sejour)\
  945. .filter(Sejour.for_year == for_year)\
  946. .all()
  947. return { 'Users':Data }
  948. @view_config(route_name='drop_sejour')
  949. def drop_sejour(request):
  950. if request.user is None:
  951. # Don't answer to users that aren't logged
  952. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  953. # Feed FicheSejour if any
  954. FicheSejour = Sejour.by_user(request.user.uid, CurrentYear)
  955. if FicheSejour:
  956. DBSession.delete(FicheSejour)
  957. request.session.flash(('warning', u'Votre participation %s a été supprimé avec succès.' % CurrentYear ))
  958. else:
  959. raise HTTPNotFound()
  960. return HTTPFound(location='/MesJM2L#Sejour')
  961. @view_config(route_name='jm2l', renderer="jm2l:templates/jm2l.mako")
  962. def jm2l_page(request):
  963. if request.user is None:
  964. # Don't answer to users that aren't logged
  965. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  966. UserNum = request.params.get('user')
  967. if UserNum:
  968. profil = User.by_id(int(UserNum))
  969. if not profil:
  970. raise HTTPNotFound()
  971. if not request.user.Staff:
  972. raise HTTPForbidden(u'Vous n\'avez pas l\'autorité suffisante pour effectuer cette action.')
  973. else:
  974. profil = request.user
  975. FicheSejour = Sejour()
  976. FicheSejour.created = datetime.datetime.now()
  977. # Build Form
  978. profil_form = ProfilForm(request.POST, profil, meta={'csrf_context': request.session})
  979. miam_form = MiamForm(request.POST, profil, meta={'csrf_context': request.session})
  980. # Feed FicheSejour if any
  981. FicheSejour = Sejour.by_user(profil.uid, CurrentYear)
  982. if FicheSejour:
  983. if FicheSejour.repas is not None:
  984. for num, item in enumerate(['RepasVendredi', 'RepasSamediMidi', 'RepasSamediSoir']):
  985. if FicheSejour.repas & 2**num:
  986. miam_form._fields[item].data = "1"
  987. else:
  988. miam_form._fields[item].data = "0"
  989. miam_form._fields['Allergies'].data = FicheSejour.repas_allerg
  990. miam_form._fields['Contraintes'].data = FicheSejour.repas_contr
  991. if request.method == 'POST' and profil_form.validate():
  992. ToDelete = list()
  993. # First, we remove entries no more present
  994. for obj in profil_form.tiersship.object_data:
  995. MatchEntry = filter( lambda x: x.object_data and x.object_data._sa_instance_state == obj._sa_instance_state,
  996. profil_form.tiersship.entries )
  997. if not MatchEntry:
  998. ToDelete.append(obj)
  999. # Then, it's time to consider new entries
  1000. for entry in profil_form.tiersship.entries:
  1001. if entry.object_data is None:
  1002. TmpUser = User_Tiers()
  1003. entry.object_data = TmpUser
  1004. profil.tiersship.append(TmpUser)
  1005. profil_form.tiersship.object_data = profil.tiersship
  1006. profil_form.populate_obj(profil)
  1007. # We should remove it as it's not in original data
  1008. for obj in ToDelete:
  1009. #profil.tiersship.remove(obj)
  1010. DBSession.delete(obj)
  1011. profil.last_change = datetime.datetime.utcnow()
  1012. profil.slug = slugify(remove_accents('%s %s' % (profil.prenom, profil.nom)).lower().strip())
  1013. DBSession.merge(profil)
  1014. request.session.flash(('info',u'Votre fiche a été mise à jour avec succès'))
  1015. MainTab = {'participer':'active',
  1016. 'Places':Place.get_list(False),
  1017. 'DBTiers':Tiers,
  1018. 'DBTiersOpt':TiersOpt,
  1019. 'Exchanges':Exchange,
  1020. 'profil_form':profil_form,
  1021. 'miam_form':miam_form,
  1022. 'uprofil':profil,
  1023. 'logged_in':request.authenticated_userid
  1024. }
  1025. return MainTab
  1026. @view_config(route_name='modal', renderer="jm2l:templates/modals.mako")
  1027. def Modal(request):
  1028. year = int(request.matchdict.get('year', None))
  1029. modtype = request.matchdict.get('modtype', None)
  1030. uid = int(request.matchdict.get('id', -1))
  1031. session = request.session
  1032. if modtype=='Password':
  1033. form = UserPasswordForm(request.POST, request.user, meta={'csrf_context': request.session})
  1034. if request.method == 'POST' and form.validate():
  1035. response = render_to_response('jm2l:templates/modals_js.mako',
  1036. {'modtype':modtype},
  1037. request=request)
  1038. request.user.password = argon2.using(rounds=4).hash(form.password.data)
  1039. DBSession.merge(request.user)
  1040. response.content_type = 'text/javascript'
  1041. return response
  1042. if modtype=='UserPicture':
  1043. form = None
  1044. if request.method == 'POST':
  1045. response = render_to_response('jm2l:templates/modals_js.mako',
  1046. {'modtype':modtype},
  1047. request=request)
  1048. response.content_type = 'text/javascript'
  1049. return response
  1050. if modtype=='Place':
  1051. if uid>0:
  1052. place = Place.by_id(uid)
  1053. if not place:
  1054. raise HTTPNotFound()
  1055. form = PlaceUpdateForm(request.POST, place, meta={'csrf_context': request.session})
  1056. else:
  1057. place = Place()
  1058. form = PlaceCreateForm(request.POST, meta={'csrf_context': request.session})
  1059. if request.method == 'POST' and form.validate():
  1060. form.populate_obj(place)
  1061. place.created_by=request.user.uid
  1062. place.slug = slugify(place.name)
  1063. if uid>0:
  1064. DBSession.merge(place)
  1065. else:
  1066. DBSession.add(place)
  1067. response = render_to_response('jm2l:templates/modals_js.mako',
  1068. {'modtype':modtype},
  1069. request=request)
  1070. response.content_type = 'text/javascript'
  1071. return response
  1072. if modtype in ['AskC', 'AskH', 'AskM', 'PropC', 'PropH', 'PropM']:
  1073. if uid>0:
  1074. Exch = Exchange.by_id(uid)
  1075. if not Exch:
  1076. raise HTTPNotFound()
  1077. if modtype in ['AskC','PropC']:
  1078. form = globals()["Update%sForm" % modtype](request.POST, Exch,
  1079. start_place = Exch.Itin.start_place,
  1080. arrival_place = Exch.Itin.arrival_place,
  1081. Hour_start = Exch.start_time.strftime("%H:%M"),
  1082. Day_start = Exch.start_time.strftime("%w"),
  1083. exch_id = uid, meta={'csrf_context': request.session}
  1084. )
  1085. elif modtype in ['AskM','PropM']:
  1086. form = globals()["Update%sForm" % modtype](request.POST, Exch,
  1087. description = Exch.description,
  1088. exch_categ = Exch.exch_categ,
  1089. Hour_start = Exch.start_time.strftime("%H:%M"),
  1090. Day_start = Exch.start_time.strftime("%w"),
  1091. Hour_end = Exch.end_time.strftime("%H:%M"),
  1092. Day_end = Exch.end_time.strftime("%w"),
  1093. exch_id = uid, meta={'csrf_context': request.session}
  1094. )
  1095. elif modtype in ['AskH','PropH']:
  1096. form = globals()["Update%sForm" % modtype](request.POST, Exch,
  1097. description = Exch.description,
  1098. exch_categ = Exch.exch_categ,
  1099. Day_start = Exch.start_time.strftime("%w"),
  1100. exch_id = uid, meta={'csrf_context': request.session}
  1101. )
  1102. # Itinerary, first get itinerary
  1103. if 0:
  1104. form.itin.form.start_place.data = Exch.Itin.start_place
  1105. form.itin.form.arrival_place.data = Exch.Itin.arrival_place
  1106. form.dateform.form.Hour.data = Exch.start_time.strftime("%H:%M")
  1107. form.dateform.form.Day.data = Exch.start_time.strftime("%w")
  1108. form.exch_id.data = uid
  1109. else:
  1110. Exch = Exchange()
  1111. form = globals()["%sForm" % modtype](request.POST, meta={'csrf_context': request.session})
  1112. if modtype in ['AskC', 'PropC']:
  1113. # Put some place on form
  1114. Places = DBSession.query(Place.place_id, Place.display_name)\
  1115. .order_by('name').all()
  1116. form.start_place.choices = Places
  1117. form.arrival_place.choices = Places
  1118. if modtype in ['PropH']:
  1119. form.exch_categ.choices = DBSession.query( Exchange_Cat.cat_id, Exchange_Cat.exch_subtype)\
  1120. .filter( Exchange_Cat.exch_type=='H' ).all()
  1121. form.place_id.choices = DBSession.query( Place.place_id, Place.display_name)\
  1122. .filter( Place.created_by==request.user.uid ).all()
  1123. if modtype in ['AskM', 'PropM']:
  1124. form.exch_categ.choices = DBSession.query( Exchange_Cat.cat_id, Exchange_Cat.exch_subtype)\
  1125. .filter( Exchange_Cat.exch_type=='M' ).all()
  1126. if request.method == 'POST' and form.validate():
  1127. # Form has been validated, it's time to create our Exchange
  1128. Exch.for_year = year
  1129. Exch.exch_state = {'Ask':'Ask', 'Prop':'Proposal'}[modtype[:-1]]
  1130. Exch.exch_type = modtype[-1:]
  1131. if modtype in ['AskC', 'PropC']:
  1132. # Itinerary, first Let's see if itinerary exist
  1133. Itinerary = DBSession.query(Itineraire)\
  1134. .filter(Itineraire.start_place==form.start_place.data) \
  1135. .filter(Itineraire.arrival_place==form.arrival_place.data) \
  1136. .filter(Itineraire.tr_voiture==True) \
  1137. .first()
  1138. if not Itinerary: # Not exist yet !
  1139. Itinerary = Itineraire(start_place=form.start_place.data, \
  1140. arrival_place=form.arrival_place.data, \
  1141. tr_voiture=True, \
  1142. created_by=1
  1143. )
  1144. DBSession.add(Itinerary)
  1145. DBSession.flush()
  1146. Exch.itin_id = Itinerary.itin_id
  1147. # Start Time
  1148. StartEvent = DBSession.query(JM2L_Year.start_time).filter(JM2L_Year.year_uid==year).first()
  1149. Week = StartEvent[0].strftime("%W")
  1150. # populate
  1151. form.populate_obj(Exch)
  1152. if modtype in ['AskC', 'PropC']:
  1153. Exch.itin_id = Itinerary.itin_id
  1154. if form._fields.has_key("Hour_start"):
  1155. TargetTime = datetime.datetime.strptime('%d %d %d %s' % (year, int(Week), \
  1156. int(form.Day_start.data), form.Hour_start.data), "%Y %W %w %H:%M")
  1157. Exch.start_time = TargetTime
  1158. elif form._fields.has_key("Day_start"):
  1159. TargetTime = datetime.datetime.strptime('%d %d %d' % (year, int(Week), \
  1160. int(form.Day_start.data)), "%Y %W %w")
  1161. Exch.start_time = TargetTime
  1162. if form._fields.has_key("Hour_end"):
  1163. TargetTime = datetime.datetime.strptime('%d %d %d %s' % (year, int(Week), \
  1164. int(form.Day_end.data), form.Hour_end.data), "%Y %W %w %H:%M")
  1165. Exch.end_time = TargetTime
  1166. elif form._fields.has_key("Day_end"):
  1167. TargetTime = datetime.datetime.strptime('%d %d %d' % (year, int(Week), \
  1168. int(form.Day_end.data)), "%Y %W %w")
  1169. Exch.end_time = TargetTime
  1170. Exch.last_change = datetime.datetime.utcnow()
  1171. if Exch.exch_state=='Ask':
  1172. Exch.asker_id = request.user.uid
  1173. elif Exch.exch_state=='Proposal':
  1174. Exch.provider_id = request.user.uid
  1175. #print vars(form.itin.form)
  1176. if uid>0:
  1177. DBSession.merge(Exch)
  1178. else:
  1179. DBSession.add(Exch)
  1180. response = render_to_response('jm2l:templates/modals_js.mako',
  1181. {'modtype':modtype},
  1182. request=request)
  1183. response.content_type = 'text/javascript'
  1184. return response
  1185. # Fallback to HTML Display with errors
  1186. return {'modtype':modtype, 'form':form, 'update':uid>0,
  1187. 'logged_in':request.authenticated_userid }
  1188. if modtype in ['ShowC', 'ShowH', 'ShowM']:
  1189. if uid>0:
  1190. Exch = Exchange.by_id(uid)
  1191. if not Exch:
  1192. raise HTTPNotFound()
  1193. else:
  1194. raise HTTPNotFound()
  1195. # Show Details around the Current Exchange
  1196. return {'modtype':modtype, 'Exch':Exch, 'logged_in':request.authenticated_userid }
  1197. MainTab = {'modtype':modtype, 'form':form, 'update':uid>0, 'uid':uid,
  1198. 'DisplayYear':year, 'session':session,
  1199. 'logged_in':request.authenticated_userid }
  1200. return MainTab
  1201. @view_config(route_name='participer', renderer="jm2l:templates/Participer.mako")
  1202. def participer(request):
  1203. session = request.session
  1204. session['year'] = CurrentYear
  1205. TmpUsr = User()
  1206. form = UserRegisterForm(request.POST, TmpUsr, meta={'csrf_context': request.session})
  1207. MyLink=None
  1208. if request.method == 'POST' and form.validate():
  1209. # Prepare mailer
  1210. form.populate_obj(TmpUsr)
  1211. TmpUsr.nom = TmpUsr.nom.capitalize()
  1212. TmpUsr.prenom = TmpUsr.prenom.capitalize()
  1213. TmpUsr.slug = slugify(remove_accents('%s %s' % (form.prenom.data, form.nom.data)).lower().strip())
  1214. TmpUsr.password = TmpUsr.my_hash
  1215. if len(TmpUsr.slug):
  1216. CheckExist = DBSession.query(User)\
  1217. .filter(User.slug==TmpUsr.slug)\
  1218. .first()
  1219. else:
  1220. CheckExist=None
  1221. if CheckExist:
  1222. MyLink = CheckExist.my_hash
  1223. NewUser = CheckExist
  1224. else:
  1225. DBSession.add(TmpUsr)
  1226. DBSession.flush()
  1227. MyLink = TmpUsr.my_hash
  1228. NewUser = TmpUsr
  1229. # Send the Welcome Mail
  1230. mailer = request.registry['mailer']
  1231. # Prepare Plain Text Message :
  1232. Mail_template = Template(filename='jm2l/templates/mail_plain.mako')
  1233. mail_plain = Mail_template.render(request=request, User=NewUser, action="Welcome")
  1234. # Prepare HTML Message :
  1235. Mail_template = Template(filename='jm2l/templates/mail_html.mako')
  1236. mail_html = Mail_template.render(request=request, User=NewUser, action="Welcome")
  1237. # Prepare Message
  1238. message = Message(subject="[JM2L] Mon inscription au site web JM2L",
  1239. sender="contact@jm2l.linux-azur.org",
  1240. recipients=[NewUser.mail or TmpUsr.mail],
  1241. body=mail_plain, html=mail_html)
  1242. message.add_bcc("spam@style-python.fr")
  1243. mailer.send(message)
  1244. request.session.flash(('info',u"Un mail vous a été envoyé afin de continuer votre quête !"))
  1245. MainTab = {'programme':'','presse':'', 'plan':'',
  1246. 'participer':'active', 'form':form, "link": MyLink,
  1247. 'logged_in':request.authenticated_userid }
  1248. return MainTab
  1249. @view_config(route_name='year')
  1250. def change_year(request):
  1251. year = int(request.matchdict.get('year', -1))
  1252. session = request.session
  1253. if year>-1:
  1254. session['year'] = year
  1255. return HTTPFound(location='/%s/' % year)
  1256. return HTTPFound(location=request.route_url('home', year=''))
  1257. @view_config(route_name='pict_user', renderer="jm2l:templates/Profil/pict_user.mako")
  1258. def pict_user(request):
  1259. return {"uprofil":request.user}
  1260. @view_config(route_name='pict_salle', renderer="jm2l:templates/Salles/pict_salle.mako")
  1261. def pict_salle(request):
  1262. salle_id = int(request.matchdict.get('salle_id', -1))
  1263. return {"Salles":Salles, "IdSalle":salle_id}
  1264. @view_config(route_name='event', renderer="jm2l:templates/view_event.mako")
  1265. def show_event(request):
  1266. year = int(request.matchdict.get('year', -1))
  1267. event_id = request.matchdict.get('event_id')
  1268. if event_id.isdigit():
  1269. TheEvent = Event.by_id(event_id)
  1270. if TheEvent is None:
  1271. raise HTTPNotFound()
  1272. else:
  1273. TheEvent = Event.by_slug(event_id, year)
  1274. if TheEvent is None:
  1275. raise HTTPNotFound()
  1276. MainTab = {'programme':'','presse':'', 'plan':'', 'participer':'',
  1277. 'event':TheEvent, 'logged_in':request.authenticated_userid, "Salles":Salles }
  1278. return MainTab
  1279. @view_config(route_name='link_event_user')
  1280. def link_event_user(request):
  1281. """ Get user and add it to current event """
  1282. if request.user is None:
  1283. # Don't answer to users that aren't logged
  1284. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1285. year = int(request.matchdict.get('year', -1))
  1286. form = AddIntervenant(request.POST, meta={'csrf_context': request.session})
  1287. intervention = request.matchdict.get('intervention', None)
  1288. TargetEvent = Event.by_id(form.event_uid.data)
  1289. Exist = User.by_id(form.intervenant.data)
  1290. if not Exist:
  1291. request.session.flash(('error',u"Une erreur s'est produite lors de l'ajout de votre intervenant !"))
  1292. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1293. year=str(year), intervention=intervention, event_id=str(TargetEvent.uid)))
  1294. else:
  1295. TargetUser = Exist
  1296. uev = User_Event(year_uid=year, role=u"Animateur d'un évènement JM2L", user_uid=TargetUser.uid)
  1297. TargetEvent.interventions.append( uev )
  1298. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1299. year=str(year), intervention=intervention, event_id=str(TargetEvent.uid)))
  1300. @view_config(route_name='link_event_tiers')
  1301. def link_event_tiers(request):
  1302. """ Create user if not exist, add it to current event """
  1303. if request.user is None:
  1304. # Don't answer to users that aren't logged
  1305. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1306. year = int(request.matchdict.get('year', -1))
  1307. form = AddTiers(request.POST, meta={'csrf_context': request.session})
  1308. intervention = request.matchdict.get('intervention', None)
  1309. TargetEvent = Event.by_id(form.event_uid.data)
  1310. Exist = Tiers.by_id(form.tiers.data)
  1311. if not Exist:
  1312. request.session.flash(('error',u"Une erreur s'est produite lors de l'ajout de votre entité !"))
  1313. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1314. year=str(year), intervention=intervention, event_id=str(TargetEvent.uid)))
  1315. else:
  1316. TargetTiers = Exist
  1317. Matching = DBSession.query(Role_Tiers)\
  1318. .filter(Role_Tiers.year_uid==year)\
  1319. .filter(Role_Tiers.tiers_role=="Exposant")\
  1320. .filter(Role_Tiers.tiers_uid==TargetTiers.uid)\
  1321. .filter(Role_Tiers.event_uid==TargetEvent.uid)\
  1322. .all()
  1323. if len(Matching)==0:
  1324. tev = Role_Tiers(year_uid=year, tiers_role="Exposant", tiers_uid=TargetTiers.uid, event_uid=TargetEvent.uid)
  1325. DBSession.add(tev)
  1326. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1327. year=str(year), intervention=intervention, event_id=str(TargetEvent.uid), _anchor="Tiers"))
  1328. @view_config(route_name='delete_link_u')
  1329. def delete_link_event_user(request):
  1330. """ Create user if not exist, add it to current event """
  1331. if request.user is None:
  1332. # Don't answer to users that aren't logged
  1333. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1334. year = int(request.matchdict.get('year', -1))
  1335. intervention = request.matchdict.get('intervention', None)
  1336. TargetEvent = Event.by_id( request.params.get('eid') )
  1337. Exist = User.by_id( request.params.get('uid') )
  1338. if not Exist:
  1339. request.session.flash(('error',u"Une erreur s'est produite lors de votre suppression !"))
  1340. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1341. year=str(year), intervention=intervention, event_id=str(TargetEvent.uid)))
  1342. else:
  1343. TargetUser = Exist
  1344. Matching = DBSession.query(User_Event)\
  1345. .filter(User_Event.year_uid==year)\
  1346. .filter(User_Event.user_uid==TargetUser.uid)\
  1347. .filter(User_Event.event_uid==TargetEvent.uid)\
  1348. .all()
  1349. if len(Matching)==0:
  1350. request.session.flash(('error',u"Une erreur s'est produite lors de la suppression !"))
  1351. else:
  1352. for item in Matching:
  1353. DBSession.delete(item)
  1354. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1355. year=str(year), intervention=intervention, event_id=str(TargetEvent.uid), _anchor="Tiers"))
  1356. @view_config(route_name='delete_link_t')
  1357. def delete_link_event_tiers(request):
  1358. """ Create user if not exist, add it to current event """
  1359. if request.user is None:
  1360. # Don't answer to users that aren't logged
  1361. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1362. year = int(request.matchdict.get('year', -1))
  1363. intervention = request.matchdict.get('intervention', None)
  1364. TargetEvent = Event.by_id( request.params.get('uid') )
  1365. Exist = Tiers.by_id( request.params.get('tid') )
  1366. if not Exist:
  1367. request.session.flash(('error',u"Une erreur s'est produite lors de l'ajout de votre entité !"))
  1368. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1369. year=str(year), intervention=intervention, event_id=str(TargetEvent.uid)))
  1370. else:
  1371. TargetTiers = Exist
  1372. Matching = DBSession.query(Role_Tiers)\
  1373. .filter(Role_Tiers.year_uid==year)\
  1374. .filter(Role_Tiers.tiers_role=="Exposant")\
  1375. .filter(Role_Tiers.tiers_uid==TargetTiers.uid)\
  1376. .filter(Role_Tiers.event_uid==TargetEvent.uid)\
  1377. .all()
  1378. if len(Matching)==0:
  1379. request.session.flash(('error',u"Une erreur s'est produite lors de la suppression de votre entité !"))
  1380. else:
  1381. for item in Matching:
  1382. DBSession.delete(item)
  1383. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1384. year=str(year), intervention=intervention, event_id=str(TargetEvent.uid), _anchor="Tiers"))
  1385. @view_config(route_name='delete_event')
  1386. def delete_event(request):
  1387. if request.user is None:
  1388. # Don't answer to users that aren't logged
  1389. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1390. year = int(request.matchdict.get('year', -1))
  1391. event_id = request.matchdict.get('event_id')
  1392. intervention = request.matchdict.get('intervention', None)
  1393. # Check intervention
  1394. if not intervention in ['Stand', 'Table_ronde', 'Atelier', 'Conference', 'Concert']:
  1395. raise HTTPNotFound(u"Ce type d'évènement n'est pas reconnu")
  1396. # We should remove all links before to remove the event
  1397. if event_id.isdigit():
  1398. TheEvent = Event.by_id(event_id)
  1399. if TheEvent is None:
  1400. raise HTTPNotFound(u"Cette réference n'existe pas")
  1401. else:
  1402. TheEvent = Event.by_slug(event_id, year)
  1403. if TheEvent is None:
  1404. raise HTTPNotFound(u"Cette réference n'existe pas")
  1405. # Remove Roles
  1406. Roles_Link = DBSession.query(Role_Tiers).filter(Role_Tiers.event_uid==TheEvent.uid).all()
  1407. for tmp in Roles_Link:
  1408. DBSession.delete(tmp)
  1409. # Remove Intervenant
  1410. User_Link = DBSession.query(User_Event).filter(User_Event.event_uid==TheEvent.uid).all()
  1411. for tmp in User_Link:
  1412. DBSession.delete(tmp)
  1413. # Remove attachment if any
  1414. SRCPath = path.join('jm2l/upload', *(IMAGEPATH + ['event'] + [ str(year) ] + [ TheEvent.slug ]) )
  1415. shutil.rmtree(SRCPath, ignore_errors=True)
  1416. # Remove Event
  1417. DBSession.delete(TheEvent)
  1418. return HTTPFound(location=request.route_url('jm2l', _anchor="Interventions"))
  1419. @view_config(route_name='edit_event', renderer="jm2l:templates/edit_event.mako")
  1420. def edit_event(request):
  1421. if request.user is None:
  1422. # Don't answer to users that aren't logged
  1423. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1424. year = int(request.matchdict.get('year', -1))
  1425. event_id = request.matchdict.get('event_id')
  1426. intervention = request.matchdict.get('intervention', None)
  1427. IntervLabel = intervention.replace('_',' ').lower()
  1428. if intervention=='Conference':
  1429. IntervLabel = u'conférence'
  1430. elif intervention=='Table_ronde':
  1431. IntervLabel = u'Table ronde'
  1432. # Check intervention
  1433. if not intervention in ['Stand', 'Table_ronde', 'Atelier', 'Conference', 'Concert']:
  1434. raise HTTPNotFound(u"Ce type d'évènement n'est pas reconnu")
  1435. TheYear = DBSession.query(JM2L_Year)\
  1436. .filter(JM2L_Year.year_uid==year)\
  1437. .first()
  1438. # Check year avaibility
  1439. if not TheYear:
  1440. raise HTTPNotFound(u"Cette année n'est pas pris en charge")
  1441. # Generate Timeslots for current year
  1442. TimeSlots = list(enumerate( [ x.strftime('%a %d %b %H:%M').decode('utf-8') for x in
  1443. TheYear.AvailableTimeSlots ] ))
  1444. if event_id:
  1445. # We try to update an existing record
  1446. if event_id.isdigit():
  1447. TheEvent = Event.by_id(event_id)
  1448. if TheEvent is None:
  1449. raise HTTPNotFound(u"Cette réference n'existe pas")
  1450. else:
  1451. TheEvent = Event.by_slug(event_id, year)
  1452. if TheEvent is None:
  1453. raise HTTPNotFound(u"Cette réference n'existe pas")
  1454. if request.user is None or not (request.user.Staff or request.user in TheEvent.intervenants):
  1455. raise HTTPForbidden(u"Vous n'êtes pas identifié comme étant un participant à cette intervention.")
  1456. # Compute some field value from selected event
  1457. if TheEvent.start_time in TheYear.AvailableTimeSlots:
  1458. start_sel = TheYear.AvailableTimeSlots.index(TheEvent.start_time)
  1459. else:
  1460. start_sel = len(TimeSlots)
  1461. TimeSlots.append( (len(TimeSlots), TheEvent.start_time.strftime('%a %d %b %H:%M').decode('utf-8')))
  1462. duration = (TheEvent.end_time - TheEvent.start_time).total_seconds()/60
  1463. end = TheEvent.start_time + datetime.timedelta(minutes=duration)
  1464. # prepare the form with update
  1465. form = ConfUpdateForm(request.POST, TheEvent, start_sel=start_sel, duration=duration, end_time=end,
  1466. meta={'csrf_context': request.session} )
  1467. # Customize labels
  1468. form.name.label.text += IntervLabel
  1469. form.description.label.text += IntervLabel
  1470. # Each event can get severals members
  1471. formAdd = AddIntervenant(event_uid=TheEvent.uid)
  1472. # Build list of intervenant
  1473. # Get users from db
  1474. Users = DBSession.query(User)\
  1475. .filter(User.Staff==1)\
  1476. .order_by('nom').all()
  1477. # Put some users on form
  1478. formAdd.intervenant.choices = [(u.uid, "%s %s" % (u.nom, u.prenom))
  1479. for u in Users]
  1480. # Each event can get severals entities
  1481. formAddT = AddTiers(event_uid=TheEvent.uid)
  1482. # Build list of entities
  1483. # Get entities from db
  1484. TmpTiers = DBSession.query(Tiers)\
  1485. .order_by('name').limit(10)
  1486. # Put some entities on form
  1487. formAddT.tiers.choices = [(u.uid, "%s %s" % (u.nom, u.prenom))
  1488. for u in Users]
  1489. else:
  1490. TheEvent = Event()
  1491. # prepare the form for creation
  1492. form = ConfCreateForm(request.POST,
  1493. event_type=intervention,
  1494. for_year=str(year), meta={'csrf_context': request.session}
  1495. )
  1496. # Customize labels
  1497. form.name.label.text += IntervLabel
  1498. form.description.label.text += IntervLabel
  1499. duration=60
  1500. # No intervenant
  1501. formAdd = None
  1502. formAddT = None
  1503. SalleDispo = DBSession.query(Salles)\
  1504. .filter(Salles.year_uid==year)\
  1505. .order_by('name')
  1506. if intervention=="Conference":
  1507. form.duration.choices =[
  1508. (15,u'Lighting talk ( 5 min)'),
  1509. (30,u'Conférence (20 min)'),
  1510. (60,u'Conférence (50 min)'),
  1511. (90,u'Conférence (75 min)'),
  1512. ]
  1513. if not duration in [15, 30, 60, 90]:
  1514. form.duration.choices.append( (duration,u'Conférence (%d min)' % duration) )
  1515. if not form._fields.has_key("uid"):
  1516. form.duration.data=60
  1517. SalleDispo = SalleDispo.filter(Salles.place_type.in_(['Conference', 'MAO', 'Atelier']))
  1518. elif intervention=="Stand":
  1519. form.duration.choices =[
  1520. (8*60, u'Toute la journée'),
  1521. (4*60, u'une demi-journée')
  1522. ]
  1523. SalleDispo = SalleDispo.filter(Salles.place_type=='Stand')
  1524. elif intervention=="Atelier":
  1525. form.duration.choices = map( lambda d:(d, u'Atelier (%dh%.2d)' % (d/60, d%60) ), \
  1526. [60, 90, 120, 150, 180, 210, 240] )
  1527. if not duration in map(lambda (d,y): d, form.duration.choices):
  1528. form.duration.choices.append( (duration,u'Atelier (%dh%.2d)' % (duration/60, duration%60) ) )
  1529. SalleDispo = SalleDispo.filter(Salles.place_type.in_(['Atelier', 'MAO']))
  1530. elif intervention=="Table_ronde":
  1531. form.duration.choices = map( lambda d:(d, u'Table ronde (%dh%.2d)' % (d/60, d%60) ), \
  1532. [60, 90, 120, 150] )
  1533. if not duration in map(lambda (d,y): d, form.duration.choices):
  1534. form.duration.choices.append( (duration,u'Table ronde (%dh%.2d)' % (duration/60, duration%60) ) )
  1535. SalleDispo = SalleDispo.filter(Salles.place_type=='Table ronde')
  1536. elif intervention=="Concert":
  1537. form.duration.choices = map( lambda d:(d, u'Concert (%dh%.2d)' % (d/60, d%60) ), \
  1538. [60, 90, 120, 150, 180, 210, 240] )
  1539. if not duration in map(lambda (d,y): d, form.duration.choices):
  1540. form.duration.choices.append( (duration,u'Concert (%dh%.2d)' % (duration/60, duration%60) ) )
  1541. SalleDispo = SalleDispo.filter(Salles.place_type.in_(['Stand', 'MAO']))
  1542. else:
  1543. raise HTTPForbidden(u"Pas encore disponible.")
  1544. form.salle_uid.choices = [(s.salle_id, s.name) for s in SalleDispo]
  1545. form.start_sel.choices = TimeSlots
  1546. if request.method == 'POST' and form.validate():
  1547. form.populate_obj(TheEvent)
  1548. TheEvent.start_time = TheYear.AvailableTimeSlots[form.start_sel.data]
  1549. TheEvent.end_time = TheEvent.start_time + datetime.timedelta(minutes=form.duration.data)
  1550. # Ok, time to put in database
  1551. if not form._fields.has_key("uid"):
  1552. TheEvent.slug = unicode(slugify(TheEvent.name))
  1553. if intervention==u"Table_ronde":
  1554. TheEvent.event_type = "Table ronde"
  1555. DBSession.add(TheEvent)
  1556. # Append creator by default
  1557. if request.user.uid!=1:
  1558. uev = User_Event(year_uid=TheYear.year_uid, role=u"Animateur")
  1559. uev.user_uid = request.user.uid
  1560. TheEvent.interventions.append( uev )
  1561. DBSession.flush()
  1562. request.session.flash(('sucess',u'Votre intervention a été créee ! Vous pouvez la compléter à tout moment.'))
  1563. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1564. year=str(year), intervention=intervention, event_id=str(TheEvent.slug)))
  1565. else:
  1566. if slugify(TheEvent.name)!=TheEvent.slug:
  1567. # We should move some file as slug have been changed
  1568. # First we ensure there is no related event that already exist with that slug
  1569. CheckEvent = Event.by_slug( unicode(slugify(TheEvent.name)), year)
  1570. if CheckEvent:
  1571. request.session.flash(('warning',u'Choisissez un autre titre pour votre évènement, il est en conflit avec un autre.'))
  1572. return {'event':TheEvent, 'form':form, 'formAdd':formAdd, 'formAddT':formAddT, 'Salles':Salles }
  1573. else:
  1574. SRCPath = path.join('jm2l/upload', *(IMAGEPATH + ['event'] + [ str(year) ] + [ TheEvent.slug ]) )
  1575. TheEvent.slug=unicode(slugify(TheEvent.name))
  1576. DSTPath = path.join('jm2l/upload', *(IMAGEPATH + ['event'] + [ str(year) ] + [ TheEvent.slug ]) )
  1577. if not path.isdir(path.dirname(DSTPath)):
  1578. makedirs(path.dirname(DSTPath))
  1579. # Then we should move event attachments to the new slug (if any)
  1580. if path.exists(SRCPath):
  1581. shutil.move(SRCPath, DSTPath)
  1582. DBSession.merge(TheEvent)
  1583. request.session.flash(('sucess',u'Votre intervention a été mis à jour !'))
  1584. return HTTPFound(location=request.route_url('edit_event', sep='/',
  1585. year=str(year), intervention=intervention, event_id=str(TheEvent.slug)))
  1586. MainTab = {'programme':'','presse':'', 'plan':'', 'participer':'',
  1587. 'event':TheEvent, 'form':form, 'formAdd':formAdd, 'formAddT':formAddT,
  1588. 'Salles':Salles }
  1589. return MainTab
  1590. @view_config(route_name='entities', renderer="jm2l:templates/list_tiers.mako")
  1591. def list_tiers(request):
  1592. Entities = dict()
  1593. EntityType = DBSession.query(TiersOpt.entity_type)\
  1594. .group_by(TiersOpt.entity_type).all()
  1595. for EType in EntityType:
  1596. Entities[EType.entity_type] = DBSession.query(Tiers).join(TiersOpt)\
  1597. .filter(TiersOpt.entity_type==EType.entity_type)\
  1598. .order_by(TiersOpt.entity_subtype, Tiers.name)
  1599. MainTab = {'programme':'','presse':'', 'plan':'', 'participer':'',
  1600. 'entities':Entities, 'logged_in':request.authenticated_userid }
  1601. return MainTab
  1602. @view_config(route_name='show_entity', renderer="jm2l:templates/view_tiers.mako")
  1603. def show_tiers(request):
  1604. tiers_type = request.matchdict.get('tiers_type')
  1605. entity_id = request.matchdict.get('entity_id')
  1606. if entity_id.isdigit():
  1607. TheTiers = Tiers.by_id(entity_id)
  1608. if TheTiers is None:
  1609. raise HTTPNotFound()
  1610. else:
  1611. TheTiers = Tiers.by_slug(entity_id)
  1612. if TheTiers is None:
  1613. raise HTTPNotFound()
  1614. MainTab = {'programme':'','presse':'', 'plan':'', 'participer':'',
  1615. 'entity':TheTiers, 'logged_in':request.authenticated_userid }
  1616. return MainTab
  1617. @view_config(route_name='delete_entity')
  1618. def delete_tiers(request):
  1619. if request.user is None:
  1620. # Don't answer to users that aren't logged
  1621. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1622. entity_id = request.matchdict.get('entity_id', None)
  1623. if entity_id:
  1624. if entity_id.isdigit():
  1625. TheTiers = Tiers.by_id(int(entity_id))
  1626. if TheTiers is None:
  1627. raise HTTPNotFound()
  1628. else:
  1629. TheTiers = Tiers.by_slug(entity_id)
  1630. if TheTiers is None:
  1631. raise HTTPNotFound()
  1632. if len(TheTiers.membership)!=0:
  1633. request.session.flash(('error', u"Vous devez supprimer tous les membres liés avant la suppression d'une entité."))
  1634. return HTTPFound(location=request.route_url('show_entity', entity_id=TheTiers.slug, tiers_type=TheTiers.get_entity_type.slug_entity_type))
  1635. if len(TheTiers.membership)!=0:
  1636. request.session.flash(('error', u"Vous devez supprimer tous les roles liés avant la suppression d'une entité."))
  1637. return HTTPFound(location=request.route_url('show_entity', entity_id=TheTiers.slug, tiers_type=TheTiers.get_entity_type.slug_entity_type))
  1638. DBSession.delete(TheTiers)
  1639. request.session.flash(('info', u"L'entité a bien été supprimée"))
  1640. return HTTPFound(location=request.route_url('entities'))
  1641. else:
  1642. raise HTTPNotFound()
  1643. @view_config(route_name='add_entity', renderer="jm2l:templates/edit_tiers.mako")
  1644. @view_config(route_name='edit_entity', renderer="jm2l:templates/edit_tiers.mako")
  1645. def edit_tiers(request):
  1646. entity_id = request.matchdict.get('entity_id', None)
  1647. TargetList = list()
  1648. if request.user is None:
  1649. # Don't answer to users that aren't logged
  1650. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1651. entity_types = DBSession.query(TiersOpt.entity_type).group_by(TiersOpt.entity_type).all()
  1652. for entity_type in entity_types:
  1653. entity_subtypes = DBSession.query(TiersOpt)\
  1654. .filter(TiersOpt.entity_type==entity_type.entity_type)\
  1655. .group_by(TiersOpt.entity_subtype).all()
  1656. ListType = [(i.uid, i.entity_subtype) for i in entity_subtypes]
  1657. TargetList.append( (entity_type.entity_type, ListType) )
  1658. if entity_id:
  1659. if entity_id.isdigit():
  1660. TheTiers = Tiers.by_id(entity_id)
  1661. if TheTiers is None:
  1662. raise HTTPNotFound()
  1663. else:
  1664. TheTiers = Tiers.by_slug(entity_id)
  1665. if TheTiers is None:
  1666. raise HTTPNotFound()
  1667. form = UpdateTiersForm(request.POST, TheTiers, meta={'csrf_context': request.session})
  1668. UserOptions = DBSession.query(TiersOpt)\
  1669. .filter(TiersOpt.entity_type==TheTiers.tiers_type)\
  1670. .all()
  1671. form.tiers_type.choices = TargetList
  1672. else:
  1673. TheTiers = Tiers()
  1674. # prepare the form for creation
  1675. form = TiersForm(request.POST, TheTiers, meta={'csrf_context': request.session})
  1676. form.tiers_type.choices = TargetList
  1677. UserOptions = list()
  1678. #test_form = TiersForm(request.POST, TheTiers, meta={'csrf_context': request.session})
  1679. if request.method == 'POST' and form.validate():
  1680. ToDelete = list()
  1681. ToDeleteR = list()
  1682. # First, we remove entries no more present
  1683. for obj in form.membership.object_data:
  1684. MatchEntry = filter( lambda x: x.object_data and x.object_data._sa_instance_state == obj._sa_instance_state,
  1685. form.membership.entries )
  1686. if not MatchEntry:
  1687. ToDelete.append(obj)
  1688. # For roles too
  1689. for obj in form.roles.object_data:
  1690. MatchEntry = filter( lambda x: x.object_data and x.object_data._sa_instance_state == obj._sa_instance_state,
  1691. form.roles.entries )
  1692. if not MatchEntry:
  1693. ToDeleteR.append(obj)
  1694. # We should remove it as it's not in original data
  1695. for obj in ToDelete:
  1696. TheTiers.membership.remove(obj)
  1697. DBSession.delete(obj)
  1698. # For roles too
  1699. for obj in ToDeleteR:
  1700. TheTiers.roles.remove(obj)
  1701. DBSession.delete(obj)
  1702. # Then, it's time to consider new entries
  1703. for entry in form.membership.entries:
  1704. if entry.object_data is None:
  1705. TmpUser = User_Tiers()
  1706. entry.object_data = TmpUser
  1707. TheTiers.membership.append(TmpUser)
  1708. form.membership.object_data = TheTiers.membership
  1709. # For roles too
  1710. for entry in form.roles.entries:
  1711. if entry.object_data is None:
  1712. TmpRole = Role_Tiers()
  1713. entry.object_data = TmpRole
  1714. TheTiers.roles.append(TmpRole)
  1715. form.roles.object_data = TheTiers.roles
  1716. form.populate_obj(TheTiers)
  1717. # Handle Remove of accents
  1718. OriginalSlug = TheTiers.slug
  1719. if not form._fields.has_key('uid'):
  1720. TheTiers.slug = slugify(form.name.data)
  1721. TheTiers.creator_id = request.user.uid
  1722. DBSession.add(TheTiers)
  1723. DBSession.flush()
  1724. return HTTPFound(location=request.route_url('edit_entity', sep='/',
  1725. entity_id=str(TheTiers.slug), tiers_type=TheTiers.get_entity_type.entity_type))
  1726. else:
  1727. if OriginalSlug!=slugify(form.name.data):
  1728. # We should move some file as slug have been changed
  1729. # First we ensure there is no related event that already exist with that slug
  1730. CheckTiers = Tiers.by_slug( slugify(form.name.data) )
  1731. if CheckTiers:
  1732. request.session.flash(('warning',u'Attention, Choisissez un autre titre pour votre entitée,'
  1733. u'elle est en conflit avec une autre.'))
  1734. DBSession.rollback()
  1735. return HTTPFound(location=request.route_url('edit_entity', sep='/',
  1736. entity_id=str(OriginalSlug), tiers_type=TheTiers.get_entity_type.entity_type))
  1737. else:
  1738. TheTiers.slug = slugify(form.name.data)
  1739. SRCPath = path.join('jm2l/upload', *(IMAGEPATH + ['tiers'] + [ OriginalSlug ]) )
  1740. DSTPath = path.join('jm2l/upload', *(IMAGEPATH + ['tiers'] + [ TheTiers.slug ]) )
  1741. if not path.isdir(path.dirname(DSTPath)):
  1742. makedirs(path.dirname(DSTPath))
  1743. # Then we should move event attachments to the new slug (if any)
  1744. if path.exists(SRCPath):
  1745. shutil.move(SRCPath, DSTPath)
  1746. DBSession.merge(TheTiers)
  1747. return HTTPFound(location=request.route_url('show_entity', entity_id=TheTiers.slug,
  1748. tiers_type=TheTiers.get_entity_type.slug_entity_type))
  1749. MainTab = {'programme':'','presse':'', 'plan':'', 'participer':'',
  1750. 'form':form, 'DBUser':User, 'UserOptions':UserOptions,
  1751. 'logged_in':request.authenticated_userid }
  1752. return MainTab
  1753. @view_config(route_name='edit_entity_cat', renderer="jm2l:templates/edit_tiers_categ.mako")
  1754. def edit_tiers_category(request):
  1755. if request.user is None:
  1756. # Don't answer to users that aren't logged
  1757. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1758. DicResult = dict()
  1759. ListChanges = list()
  1760. if request.method == 'POST':
  1761. # Reformat data
  1762. RegExist = re.compile('collection\[(?P<slug>[\w-]+)\]\[(?P<num>\d+)\]\[(?P<id>\d+)\]')
  1763. RegTitle = re.compile('collection\[(?P<slug>[\w-]+)\]\[title]')
  1764. RegNew = re.compile('collection\[(?P<slug>[\w-]+)\]\[(?P<num>\d+)\]\[id\]')
  1765. for key, value in request.POST.iteritems():
  1766. regN= RegNew.match(key)
  1767. regT= RegTitle.match(key)
  1768. reg = RegExist.match(key)
  1769. if reg:
  1770. if not DicResult.has_key(reg.group('slug')):
  1771. DicResult[reg.group('slug')] = dict()
  1772. if DicResult[reg.group('slug')].has_key('items'):
  1773. DicResult[reg.group('slug')]['items'].append( ( int(reg.group('id')), value ) )
  1774. else:
  1775. DicResult[reg.group('slug')]['items'] = [ ( int(reg.group('id')), value ) ]
  1776. elif regN:
  1777. if not DicResult.has_key(regN.group('slug')):
  1778. DicResult[regN.group('slug')] = dict()
  1779. if DicResult[regN.group('slug')].has_key('items'):
  1780. DicResult[regN.group('slug')]['items'].append( ( 'id', value ) )
  1781. else:
  1782. DicResult[regN.group('slug')]['items'] = [ ( 'id', value ) ]
  1783. ListChanges.append(('add', 0, DicResult[regN.group('slug')]['title'], value))
  1784. elif regT:
  1785. if not DicResult.has_key(regT.group('slug')):
  1786. DicResult[regT.group('slug')] = dict()
  1787. DicResult[regT.group('slug')]['title'] = value
  1788. else:
  1789. raise
  1790. for opt in DBSession.query(TiersOpt).all():
  1791. if DicResult.has_key(opt.slug_entity_type):
  1792. found = filter( lambda (x,y): opt.uid==x,
  1793. DicResult[opt.slug_entity_type].get('items', []))
  1794. if not found:
  1795. ListChanges.append(('remove', opt.uid, opt.entity_type, opt.entity_subtype))
  1796. else:
  1797. for tst in found:
  1798. # Check changes on Cat Name
  1799. if DicResult[opt.slug_entity_type]['title']!=opt.entity_type or \
  1800. tst[1]!=opt.entity_subtype:
  1801. ListChanges.append(('changed', opt.uid,
  1802. DicResult[opt.slug_entity_type]['title'],
  1803. tst[1]))
  1804. else:
  1805. ListChanges.append(('remove', opt.uid, opt.entity_type, opt.entity_subtype))
  1806. # Do The change
  1807. for action, uid, entity, subentity in ListChanges:
  1808. if action=="changed":
  1809. opt = TiersOpt.by_id(uid)
  1810. opt.entity_type = entity
  1811. opt.entity_subtype = subentity
  1812. elif action=="remove":
  1813. opt = TiersOpt.by_id(uid)
  1814. DBSession.delete(opt)
  1815. elif action=="add":
  1816. opt = TiersOpt()
  1817. opt.entity_type = entity
  1818. opt.entity_subtype = subentity
  1819. DBSession.add(opt)
  1820. MainTab = {'programme':'','presse':'', 'plan':'', 'participer':'',
  1821. 'logged_in':request.authenticated_userid, 'TiersOpt':TiersOpt }
  1822. return MainTab
  1823. @view_config(route_name='show_user', renderer="jm2l:templates/view_user.mako")
  1824. def show_user(request):
  1825. user_slug = request.matchdict.get('user_slug', None)
  1826. if user_slug is None or len(user_slug)==0:
  1827. raise HTTPNotFound(u"Cet utilisateur n'a pas été reconnu")
  1828. # Query database
  1829. DispUser = User.by_slug(user_slug)
  1830. if DispUser is None:
  1831. raise HTTPNotFound()
  1832. MainTab = {'programme':'','presse':'', 'plan':'', 'participer':'',
  1833. 'DispUser':DispUser, 'logged_in':request.authenticated_userid }
  1834. return MainTab
  1835. #@view_config(route_name='link_user_entity')
  1836. def link_user_entity(request):
  1837. if request.user is None:
  1838. # Don't answer to users that aren't logged
  1839. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1840. uid = int(request.matchdict.get('uid', -1))
  1841. year = int(request.matchdict.get('year', -1))
  1842. user_id = int(request.matchdict.get('uid', -1))
  1843. TheTiers = Tiers.by_id(uid)
  1844. if TheTiers is None:
  1845. raise HTTPNotFound()
  1846. return HTTPFound(location=request.route_url('edit_entity', uid=uid) )
  1847. #@view_config(route_name='link_role_entity')
  1848. def link_role_entity(request):
  1849. if request.user is None:
  1850. # Don't answer to users that aren't logged
  1851. raise HTTPForbidden(u'Vous devez vous identifier pour obtenir une réponse.')
  1852. uid = int(request.matchdict.get('uid', -1))
  1853. year = int(request.matchdict.get('year', -1))
  1854. role_id = int(request.matchdict.get('role_id', -1))
  1855. TheTiers = Tiers.by_id(uid)
  1856. if TheTiers is None:
  1857. raise HTTPNotFound()
  1858. return HTTPFound(location=request.route_url('edit_entity', uid=uid) )
  1859. @forbidden_view_config()
  1860. def forbidden(reason, request):
  1861. if 'ident' in reason.detail:
  1862. request.session.flash(('info', reason.detail))
  1863. return HTTPFound(location='/sign/login?from='+request.environ['PATH_INFO'] )
  1864. else:
  1865. request.response.status = 403
  1866. return render_to_response('jm2l:templates/Errors/403.mako', { "reason":reason },
  1867. request=request)
  1868. @notfound_view_config()
  1869. def notfound(reason, request):
  1870. request.response.status = 404
  1871. return render_to_response('jm2l:templates/Errors/404.mako', { "reason":reason },
  1872. request=request)