@@ -0,0 +1,4 @@ | |||
0.0 | |||
--- | |||
- Initial version |
@@ -0,0 +1,2 @@ | |||
include *.txt *.ini *.cfg *.rst | |||
recursive-include jm2l *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml |
@@ -0,0 +1,14 @@ | |||
JM2L README | |||
================== | |||
Getting Started | |||
--------------- | |||
- cd <directory containing this file> | |||
- $VENV/bin/python setup.py develop | |||
- $VENV/bin/initialize_JM2L_db development.ini | |||
- $VENV/bin/pserve development.ini | |||
@@ -0,0 +1,71 @@ | |||
### | |||
# app configuration | |||
# http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html | |||
### | |||
[app:main] | |||
use = egg:JM2L | |||
pyramid.reload_templates = true | |||
pyramid.debug_authorization = false | |||
pyramid.debug_notfound = false | |||
pyramid.debug_routematch = false | |||
pyramid.default_locale_name = en | |||
pyramid.includes = | |||
pyramid_debugtoolbar | |||
pyramid_tm | |||
sqlalchemy.url = sqlite:///%(here)s/JM2L.sqlite | |||
# By default, the toolbar only appears for clients from IP addresses | |||
# '127.0.0.1' and '::1'. | |||
# debugtoolbar.hosts = 127.0.0.1 ::1 | |||
### | |||
# wsgi server configuration | |||
### | |||
[server:main] | |||
use = egg:waitress#main | |||
host = 0.0.0.0 | |||
port = 8080 | |||
### | |||
# logging configuration | |||
# http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html | |||
### | |||
[loggers] | |||
keys = root, jm2l, sqlalchemy | |||
[handlers] | |||
keys = console | |||
[formatters] | |||
keys = generic | |||
[logger_root] | |||
level = INFO | |||
handlers = console | |||
[logger_jm2l] | |||
level = DEBUG | |||
handlers = | |||
qualname = jm2l | |||
[logger_sqlalchemy] | |||
level = WARN | |||
handlers = | |||
qualname = sqlalchemy.engine | |||
# "level = INFO" logs SQL queries. | |||
# "level = DEBUG" logs SQL queries and results. | |||
# "level = WARN" logs neither. (Recommended for production systems.) | |||
[handler_console] | |||
class = StreamHandler | |||
args = (sys.stderr,) | |||
level = NOTSET | |||
formatter = generic | |||
[formatter_generic] | |||
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
@@ -0,0 +1,163 @@ | |||
try: | |||
from html import escape | |||
except ImportError: | |||
from cgi import escape | |||
#from wtforms import widgets | |||
from wtforms.widgets import HTMLString, html_params | |||
from wtforms.fields.core import Field | |||
from wtforms.compat import text_type, izip | |||
class MySelect(object): | |||
""" | |||
Renders a select field. | |||
If `multiple` is True, then the `size` property should be specified on | |||
rendering to make the field useful. | |||
The field must provide an `iter_choices()` method which the widget will | |||
call on rendering; this method must yield tuples of | |||
`(value, label, selected)`. | |||
""" | |||
def __init__(self, multiple=False): | |||
self.multiple = multiple | |||
def __call__(self, field, **kwargs): | |||
kwargs.setdefault('id', field.id) | |||
if self.multiple: | |||
kwargs['multiple'] = True | |||
html = ['<select %s>' % html_params(name=field.name, **kwargs)] | |||
last_group = None | |||
for group, val, label, selected in field.iter_choices(): | |||
if group is None: | |||
html.append(self.render_option(val, label, selected)) | |||
elif last_group != group: | |||
html.append(self.render_optgroup(last_group, group)) | |||
html.append(self.render_option(val, label, selected)) | |||
last_group=group | |||
else: | |||
html.append(self.render_option(val, label, selected)) | |||
if last_group: | |||
html.append(self.render_optgroup(last_group, None)) | |||
html.append('</select>') | |||
return HTMLString(''.join(html)) | |||
@classmethod | |||
def render_option(cls, value, label, selected, **kwargs): | |||
if value is True: | |||
# Handle the special case of a 'True' value. | |||
value = text_type(value) | |||
options = dict(kwargs, value=value) | |||
if selected: | |||
options['selected'] = True | |||
return HTMLString('<option %s>%s</option>' % (html_params(**options), escape(text_type(label), quote=False))) | |||
@classmethod | |||
def render_optgroup(cls, previous_label, label, **kwargs): | |||
options = dict(kwargs) | |||
if previous_label is None: | |||
return HTMLString('<optgroup %s label="%s">' % (html_params(**options), escape(text_type(label), quote=False))) | |||
elif label is None: | |||
return HTMLString('</optgroup>') | |||
else: | |||
return HTMLString('</optgroup><optgroup %s label="%s">' % (html_params(**options), escape(text_type(label), quote=False))) | |||
class MyOption(object): | |||
""" | |||
Renders the individual option from a select field. | |||
This is just a convenience for various custom rendering situations, and an | |||
option by itself does not constitute an entire field. | |||
""" | |||
def __call__(self, field, **kwargs): | |||
return MySelect.render_option(field._value(), field.label.text, field.checked, **kwargs) | |||
class MySelectFieldBase(Field): | |||
#option_widget = widgets.Option() | |||
option_widget = MyOption() | |||
""" | |||
Base class for fields which can be iterated to produce options. | |||
This isn't a field, but an abstract base class for fields which want to | |||
provide this functionality. | |||
""" | |||
def __init__(self, label=None, validators=None, option_widget=None, **kwargs): | |||
super(MySelectFieldBase, self).__init__(label, validators, **kwargs) | |||
if option_widget is not None: | |||
self.option_widget = option_widget | |||
def iter_choices(self): | |||
""" | |||
Provides data for choice widget rendering. Must return a sequence or | |||
iterable of (value, label, selected) tuples. | |||
""" | |||
raise NotImplementedError() | |||
def __iter__(self): | |||
opts = dict(widget=self.option_widget, _name=self.name, _form=None, _meta=self.meta) | |||
for i, (value, label, checked) in enumerate(self.iter_choices()): | |||
opt = self._Option(label=label, id='%s-%d' % (self.id, i), **opts) | |||
opt.process(None, value) | |||
opt.checked = checked | |||
yield opt | |||
class _Option(Field): | |||
checked = False | |||
def _value(self): | |||
return text_type(self.data) | |||
class MySelectField(MySelectFieldBase): | |||
#widget = widgets.Select() | |||
widget = MySelect() | |||
def __init__(self, label=None, validators=None, coerce=text_type, choices=None, **kwargs): | |||
super(MySelectField, self).__init__(label, validators, **kwargs) | |||
self.coerce = coerce | |||
self.choices = choices | |||
def iter_choices(self): | |||
for choiceA, choiceB in self.choices: | |||
if isinstance(choiceB, (tuple, list)): | |||
# We should consider choiceA as an optgroup label | |||
group_label = choiceA | |||
for value, label in choiceB: | |||
yield (group_label, value, label, self.coerce(value) == self.data) | |||
else: | |||
value, label = choiceA, choiceB | |||
# Not an optgroup, let's fallback to classic usage | |||
yield (None, value, label, self.coerce(value) == self.data) | |||
def process_data(self, value): | |||
try: | |||
self.data = self.coerce(value) | |||
except (ValueError, TypeError): | |||
self.data = None | |||
def process_formdata(self, valuelist): | |||
if valuelist: | |||
try: | |||
self.data = self.coerce(valuelist[0]) | |||
except ValueError: | |||
raise ValueError(self.gettext('Invalid Choice: could not coerce')) | |||
def pre_validate(self, form): | |||
for choiceA, choiceB in self.choices: | |||
if isinstance(choiceB, (tuple, list)): | |||
for value, label in choiceB: | |||
if self.data == value: | |||
break | |||
if self.data == value: | |||
break | |||
else: | |||
value, label = choiceA, choiceB | |||
if self.data == value: | |||
break | |||
else: | |||
raise ValueError(self.gettext('Not a valid choice')) |
@@ -0,0 +1,112 @@ | |||
from pyramid.authentication import AuthTktAuthenticationPolicy | |||
from pyramid.authorization import ACLAuthorizationPolicy | |||
from pyramid.config import Configurator | |||
from pyramid.renderers import JSON, JSONP | |||
from pyramid.session import SignedCookieSessionFactory | |||
from sqlalchemy import engine_from_config | |||
from .models import DBSession, get_user | |||
from .security import EntryFactory, groupfinder | |||
import locale | |||
def main(global_config, **settings): | |||
""" This function returns a Pyramid WSGI application. | |||
""" | |||
locale.setlocale(locale.LC_ALL, "fr_FR.UTF-8") | |||
engine = engine_from_config(settings, 'sqlalchemy.') | |||
DBSession.configure(bind=engine) | |||
my_session_factory = SignedCookieSessionFactory('itsaseekreet') | |||
authentication_policy = AuthTktAuthenticationPolicy('somesecret', | |||
callback=groupfinder, hashalg='sha512', debug=True) | |||
authorization_policy = ACLAuthorizationPolicy() | |||
config = Configurator(settings=settings, | |||
root_factory='.security.RootFactory', | |||
authentication_policy=authentication_policy, | |||
authorization_policy=authorization_policy | |||
) | |||
config.add_renderer('json', JSON(indent=4)) | |||
config.add_renderer('jsonp', JSONP(param_name='callback')) | |||
config.set_session_factory(my_session_factory) | |||
config.add_request_method(get_user, 'user', reify=True) | |||
config.add_static_view('static', 'static', cache_max_age=3600) | |||
config.add_static_view('img', 'static/img', cache_max_age=3600) | |||
config.add_static_view('upload', 'upload', cache_max_age=3600) | |||
config.add_route('tester', '/tester') | |||
# ICal Routes | |||
config.add_route('progr_iCal', '/{year:\d+}/JM2L.ics') | |||
# JSON Routes | |||
config.add_route('users_json', '/json-users') | |||
config.add_route('tiers_json', '/json-tiers') | |||
config.add_route('progr_json', '/{year:\d+}/le-prog-json') | |||
config.add_route('timeline_json', '/{year:\d+}/timeline-json') | |||
# Session setting Routes | |||
config.add_route('year', '/year/{year:\d+}') | |||
# HTML Routes - Staff | |||
config.add_route('list_task', '/Staff') | |||
config.add_route('handle_task', '/Staff/tasks{sep:/*}{task_id:(\d+)?}') | |||
config.add_route('action_task', '/Staff/{action:(\w+)}/{task_id:(\d+)}') | |||
# HTML Routes - Public | |||
config.add_route('home', '/') | |||
config.add_route('presse', '/{year:\d+}/dossier-de-presse') | |||
config.add_route('edit_presse', '/{year:\d+}/dossier-de-presse/edit') | |||
config.add_route('programme', '/{year:\d+}/le-programme') | |||
config.add_route('plan', 'nous-rejoindre') | |||
config.add_route('participer', 'participer-l-evenement') | |||
config.add_route('captcha', '/captcha') | |||
## Events | |||
config.add_route('event', '/event/{year:\d+}/{event_id:([\w-]+)?}') | |||
config.add_route('link_event', '/MesJM2L/{year:\d+}/{intervention:\w+}/link') | |||
config.add_route('edit_event', '/MesJM2L/{year:\d+}/{intervention:\w+}{sep:/*}{event_id:([\w-]+)?}') | |||
## Entities | |||
config.add_route('entities', '/entities') #{sep:/*}{Nature:\w+?}') | |||
config.add_route('add_entity', '/entity') | |||
config.add_route('show_entity', '/entity/{tiers_type:(\w+)}/{entity_id:([\w-]+)?}') | |||
config.add_route('edit_entity', '/entity/{tiers_type:(\w+)}/{entity_id:([\w-]+)}/edit') | |||
config.add_route('edit_entity_cat', '/categorie/entity') | |||
## Users | |||
config.add_route('show_user', '/user/{user_slug:([\w-]+)?}') | |||
# HTML Routes - Logged | |||
#config.add_route('profil', 'MesJM2L') | |||
config.add_route('jm2l', '/MesJM2L') | |||
config.add_route('modal', '/{year:\d+}/modal/{modtype:\w+}/{id:(\d+)}') | |||
# Handle exchanges | |||
config.add_route('exchange', '/{year:\d+}/exchange/{modtype:\w+}/{id:(\d+)}/{action:\w+}') | |||
# Handle authentication | |||
config.add_route('register', '/register') | |||
config.add_route('auth', '/sign/{action}') | |||
config.add_route('bymail', '/sign/jm2l/{hash}') | |||
# Handle Multimedia and Uploads | |||
config.add_route('media_uploadform', '/test2') | |||
config.add_route('media_view', '/image/{media_table:\w+}/{uid:\d+}/{name:.+}') | |||
config.add_route('media_upload', '/uploader/{media_table:\w+}/{uid:\d+}/proceed{sep:/*}{name:.*}') | |||
# To Trash routes | |||
config.add_route('test', '/test') | |||
config.add_route('test2', '/toast{sep:/*}{uid:(\d+)?}') | |||
#config.add_route('link_user_entity', '/entity/{uid:(\d+)}/{year:\d+}/user/{user_id:(\d+)}') | |||
#config.add_route('link_role_entity', '/entity/{uid:(\d+)}/{year:\d+}/role/{role_id:(\d+)}') | |||
config.add_route('IntAdd', '/IntAdd/{modtype:\w+}') | |||
config.add_route('IntProp', '/IntProp/{modtype:\w+}') | |||
config.add_route('blog', '/blog/{id:\d+}/{slug}') | |||
config.add_route('blog_action', '/blog/{action}', | |||
factory='jm2l.security.EntryFactory') | |||
config.scan() | |||
return config.make_wsgi_app() |
@@ -0,0 +1,42 @@ | |||
from pyramid.view import view_config | |||
from pyramid.security import remember, forget | |||
from pyramid.httpexceptions import HTTPFound | |||
from .models import User | |||
@view_config(route_name='auth', match_param="action=login", renderer="jm2l:templates/login.mako") | |||
def login(request): | |||
return {} | |||
@view_config(route_name='bymail', renderer="string") | |||
def bymail(request): | |||
myhash = request.matchdict.get('hash', "") | |||
user = User.by_hash(myhash) | |||
if user: | |||
headers = remember(request, user.uid) | |||
return HTTPFound(location=request.route_url('jm2l'), | |||
headers=headers) | |||
else: | |||
headers = forget(request) | |||
return HTTPFound(location=request.route_url('auth', action='login'), | |||
headers=headers) | |||
@view_config(route_name='auth', match_param="action=in", renderer="string", | |||
request_method="POST") | |||
@view_config(route_name='auth', match_param="action=out", renderer="string") | |||
def sign_in_out(request): | |||
username = request.POST.get('username') | |||
if username: | |||
user = User.by_name(username) | |||
if user and user.verify_password(request.POST.get('password')): | |||
headers = remember(request, user.uid) | |||
return HTTPFound(location=request.route_url('jm2l'), | |||
headers=headers) | |||
else: | |||
headers = forget(request) | |||
else: | |||
headers = forget(request) | |||
return HTTPFound(location=request.route_url('home'), | |||
headers=headers) | |||
@@ -0,0 +1,151 @@ | |||
# Stolen from tgcaptcha/plugins | |||
# http://code.google.com/p/tgcaptcha/source/browse/trunk/tgcaptcha/plugins/image/vanasco_dowty/captcha.py | |||
import random | |||
from PIL import Image, ImageDraw, ImageFont, ImageFilter | |||
import StringIO | |||
import math | |||
from pyramid.view import view_config | |||
from words import TabMots | |||
from pyramid.response import Response | |||
class Captcha_Img(object): | |||
def __init__( self, width, height): | |||
self.width = width | |||
self.height = height | |||
self._layers = [ | |||
_PyCaptcha_SineWarp(amplitudeRange = (4, 8) , periodRange=(0.65,0.73) ), | |||
] | |||
def getImg(self): | |||
"""Get a PIL image representing this CAPTCHA test, creating it if necessary""" | |||
if not self._image: | |||
self._image = self.render() | |||
return self._image | |||
def render(self): | |||
"""Render this CAPTCHA, returning a PIL image""" | |||
size = (self.width,self.height) | |||
#img = Image.new("RGB", size ) | |||
img = self._image | |||
for layer in self._layers: | |||
img = layer.render( img ) or img | |||
self._image = img | |||
return self._image | |||
class _PyCaptcha_WarpBase(object): | |||
"""Abstract base class for image warping. Subclasses define a | |||
function that maps points in the output image to points in the input image. | |||
This warping engine runs a grid of points through this transform and uses | |||
PIL's mesh transform to warp the image. | |||
""" | |||
filtering = Image.BILINEAR | |||
resolution = 40 | |||
def get_transform(self, image): | |||
"""Return a transformation function, subclasses should override this""" | |||
return lambda x, y: (x, y) | |||
def render(self, image): | |||
r = self.resolution | |||
xPoints = image.size[0] / r + 2 | |||
yPoints = image.size[1] / r + 2 | |||
f = self.get_transform(image) | |||
# Create a list of arrays with transformed points | |||
xRows = [] | |||
yRows = [] | |||
for j in xrange(yPoints): | |||
xRow = [] | |||
yRow = [] | |||
for i in xrange(xPoints): | |||
x, y = f(i*r, j*r) | |||
# Clamp the edges so we don't get black undefined areas | |||
x = max(0, min(image.size[0]-1, x)) | |||
y = max(0, min(image.size[1]-1, y)) | |||
xRow.append(x) | |||
yRow.append(y) | |||
xRows.append(xRow) | |||
yRows.append(yRow) | |||
# Create the mesh list, with a transformation for | |||
# each square between points on the grid | |||
mesh = [] | |||
for j in xrange(yPoints-1): | |||
for i in xrange(xPoints-1): | |||
mesh.append(( | |||
# Destination rectangle | |||
(i*r, j*r, | |||
(i+1)*r, (j+1)*r), | |||
# Source quadrilateral | |||
(xRows[j ][i ], yRows[j ][i ], | |||
xRows[j+1][i ], yRows[j+1][i ], | |||
xRows[j+1][i+1], yRows[j+1][i+1], | |||
xRows[j ][i+1], yRows[j ][i+1]), | |||
)) | |||
return image.transform(image.size, Image.MESH, mesh, self.filtering) | |||
class _PyCaptcha_SineWarp(_PyCaptcha_WarpBase): | |||
"""Warp the image using a random composition of sine waves""" | |||
def __init__(self, | |||
amplitudeRange = (1,1),#(2, 6), | |||
periodRange = (1,1)#(0.65, 0.73), | |||
): | |||
self.amplitude = random.uniform(*amplitudeRange) | |||
self.period = random.uniform(*periodRange) | |||
self.offset = (random.uniform(0, math.pi * 2 / self.period), | |||
random.uniform(0, math.pi * 2 / self.period)) | |||
def get_transform(self, image): | |||
return (lambda x, y, | |||
a = self.amplitude, | |||
p = self.period, | |||
o = self.offset: | |||
(math.sin( (y+o[0])*p )*a + x, | |||
math.sin( (x+o[1])*p )*a + y)) | |||
@view_config(route_name='captcha') | |||
def DoCaptcha(request): | |||
ImgSize = (230,100) | |||
WorkImg = Image.new( 'RGBA', ImgSize, (255, 255, 255, 0) ) | |||
Xmax, Ymax = WorkImg.size | |||
# Write something on it | |||
draw = ImageDraw.Draw(WorkImg) | |||
# use a truetype font | |||
#font = ImageFont.truetype("/var/lib/defoma/gs.d/dirs/fonts/LiberationMono-Regular.ttf", 40) | |||
# use it | |||
font = ImageFont.truetype("jm2l/static/LiberationMono-Regular.ttf",40) | |||
# Re-position | |||
# Choose a word for captcha | |||
text = random.choice(TabMots) | |||
Xt, Yt = font.getsize(text) | |||
OrX, OrY = (ImgSize[0]-Xt)/2, (ImgSize[1]-Yt)/2 | |||
draw.text((OrX, OrY), text, font=font, fill="#000000") | |||
# Apply a Blur | |||
# WorkImg=WorkImg.filter(ImageFilter.BLUR) | |||
# Apply a DETAIL | |||
WorkImg=WorkImg.filter(ImageFilter.DETAIL) | |||
# randomize parameters for perspective | |||
ax, ay = (random.uniform(0.9,1.2) , random.uniform(0.9,1.2)) | |||
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 | |||
WorkImg= WorkImg.transform(ImgSize, Image.PERSPECTIVE, (ax, bx, -25, by, ay, -10, tx, ty)) | |||
# Apply SinWarp to Captcha | |||
tr = Captcha_Img(Xmax, Ymax) | |||
tr._image = WorkImg | |||
WorkImg = tr.render() | |||
# Apply a Smooth on it | |||
WorkImg=WorkImg.filter(random.choice([ImageFilter.SMOOTH, ImageFilter.SMOOTH_MORE])) | |||
# Save Result | |||
request.session['Captcha'] = text | |||
#session.save() | |||
ImgHandle = StringIO.StringIO() | |||
WorkImg.save(ImgHandle,'png') | |||
ImgHandle.seek(0) | |||
return Response(app_iter=ImgHandle, content_type = 'image/png') |
@@ -0,0 +1,498 @@ | |||
# -*- coding: utf8 -*- | |||
from wtforms import Form, BooleanField, TextField, TextAreaField, SelectField, SubmitField, validators, FieldList | |||
#import .ExtWforms | |||
from .ExtWtforms import MySelectField | |||
from wtforms import HiddenField, DecimalField, DateTimeField, FormField, FileField, DateField | |||
from wtforms.validators import ValidationError | |||
from datetime import datetime | |||
strip_filter = lambda x: x.strip() if x else None | |||
from wtforms.csrf.session import SessionCSRF | |||
from datetime import timedelta | |||
class MyBaseForm(Form): | |||
class Meta: | |||
csrf = True | |||
csrf_class = SessionCSRF | |||
csrf_secret = b'lJDQtOAMC2qe89doIn8u3Mch_DgeLSKO' | |||
csrf_time_limit = timedelta(minutes=20) | |||
class BlogCreateForm(MyBaseForm): | |||
title = TextField('Entry title', [validators.Length(min=1, max=255)], | |||
filters=[strip_filter]) | |||
body = TextAreaField('Entry body', [validators.Length(min=1)], | |||
filters=[strip_filter]) | |||
class BlogUpdateForm(BlogCreateForm): | |||
id = HiddenField() | |||
PLACE_TYPE = [('Aeroport', u'Aรฉroport'), ('Gare','Gare'), ('JM2L','JM2L'), | |||
('Hotel',u'Hรดtel'), ('Habitant','Habitant'), | |||
('Restaurant','Restaurant'), ('Autres','Autres')] | |||
TIERS_ROLE = [('Exposant','Exposant'), ('Sponsor','Sponsor'), | |||
('Donateur','Donateur')] | |||
EVENT_TYPE = ['Stand', 'Table ronde', 'Atelier', 'Concert', 'Conference', 'Repas'] | |||
CONF_DURATION = [ (15,u'Lighting talk ( 5 min)'), | |||
(30,u'Confรฉrence (20 min)'), | |||
(60,u'Confรฉrence (50 min)'), | |||
(90,u'Confรฉrence (75 min)'),] | |||
ATELIER_DURATION = [ (15,u'Lighting talk ( 5 min)'), | |||
(30,u'Confรฉrence (20 min)'), | |||
(60,u'Confรฉrence (50 min)'), | |||
(90,u'Confรฉrence (75 min)'),] | |||
class StaffArea(MyBaseForm): | |||
uid = HiddenField() | |||
name = TextField(u'Pรดle') | |||
description = TextAreaField('Description', [validators.optional(), validators.Length(max=1000000)], | |||
filters=[strip_filter] | |||
) | |||
class StaffTasks(MyBaseForm): | |||
name = TextField(u'Nom de la tรขche') | |||
area_uid = SelectField(u'Pรดle concernรฉ', coerce=int ) | |||
due_date = DateField(u'Date prรฉvue', format='%d/%m/%Y') | |||
closed_by = SelectField(u'Assignรฉ ร ', coerce=int ) | |||
description = TextAreaField('Description', [validators.optional(), validators.Length(max=1000000)], | |||
filters=[strip_filter]) | |||
class EditStaffTasks(StaffTasks): | |||
uid = HiddenField() | |||
class DossPresse(MyBaseForm): | |||
year_uid = HiddenField() | |||
doss_presse = TextAreaField('Dossier de Presse', [validators.optional(), validators.Length(max=1000000)], | |||
filters=[strip_filter]) | |||
class TiersMember(MyBaseForm): | |||
class Meta: | |||
csrf = False | |||
year_uid = SelectField(u'Annรฉe', coerce=int, choices=zip(range(2006,2016),range(2006,2016))) | |||
user_uid = TextField(u'user') | |||
role = TextField(u'Role') | |||
class TiersChoice(MyBaseForm): | |||
class Meta: | |||
csrf = False | |||
year_uid = HiddenField() | |||
user_uid = HiddenField() | |||
tiers_uid = TextField(u'Entitรฉ') | |||
role = TextField(u'Role') | |||
class AddIntervenant(MyBaseForm): | |||
class Meta: | |||
csrf = False | |||
event_uid = HiddenField() | |||
nom = TextField(u'Nom', [validators.Length(max=80)], | |||
filters=[strip_filter], | |||
description = u"Les espaces sont autorisรฉs, la ponctuation n'est " + | |||
u"pas autorisรฉe ร l'exception des points, traits d'union, " + | |||
u"apostrophes et tirets bas." | |||
) | |||
prenom = TextField(u'Prรฉnom', [validators.Length(max=80)], | |||
filters=[strip_filter], | |||
description = u"Les espaces sont autorisรฉs, la ponctuation n'est " + | |||
u"pas autorisรฉe ร l'exception des points, traits d'union, " + | |||
u"apostrophes et tirets bas." | |||
) | |||
email = TextField(u'Email', [validators.required(), | |||
validators.length(max=10), | |||
validators.Email(message='Ceci ne ressemble pas ร une adresse valide')], | |||
description=u"Afin d'รฉviter la duplication d'information et les doublons inutile, "+ | |||
u"pensez d'abord ร lui demander de confirmer le mail qu'il a utilisรฉ lors de "+ | |||
u"son inscription sur le site.") | |||
add = SubmitField('Ajouter des intervenants') | |||
class InterventionForm(MyBaseForm): | |||
event_type = HiddenField() | |||
for_year = HiddenField() | |||
start_time = HiddenField() | |||
end_time = HiddenField() | |||
name = TextField(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 ConfCreateForm(InterventionForm): | |||
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)." | |||
) | |||
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" | |||
) | |||
class ConfUpdateForm(ConfCreateForm): | |||
uid = HiddenField() | |||
class PlaceCreateForm(MyBaseForm): | |||
place_type = SelectField('Type', choices=PLACE_TYPE) | |||
display_name = TextField(u'Nom affichรฉ', [validators.Length(min=1, max=20)], | |||
filters=[strip_filter]) | |||
name = TextField('Nom Complet', [validators.Length(min=1, max=80)], | |||
filters=[strip_filter]) | |||
gps_coord = TextField(u'Coordonnรฉes GPS', [validators.Length(max=30)], | |||
filters=[strip_filter]) | |||
adresse = TextAreaField('Adresse', [validators.Length(max=100)], | |||
filters=[strip_filter]) | |||
codePostal = TextField('Code Postal', [validators.Length(max=5)], | |||
filters=[strip_filter]) | |||
ville = TextField('Ville', [validators.Length(max=40)], | |||
filters=[strip_filter]) | |||
website = TextField('Site Web', [validators.Length(max=100)], | |||
filters=[strip_filter]) | |||
description = TextAreaField('Description', | |||
filters=[strip_filter]) | |||
created_by = HiddenField() | |||
class PlaceUpdateForm(PlaceCreateForm): | |||
place_id = HiddenField() | |||
def captcha_check(form, field): | |||
if form.meta.csrf_context.get('Captcha')!=field.data: | |||
raise ValidationError(u"la vรฉrification captcha est invalide.") | |||
class UserRegisterForm(MyBaseForm): | |||
nom = TextField(u'Nom', [ | |||
validators.Length(max=80, message=u"80 car. maximum"), | |||
validators.required(message=u"Ce champ est obligatoire") ], | |||
filters=[strip_filter] | |||
) | |||
prenom = TextField(u'Prรฉnom', [ | |||
validators.Length(max=80, message=u"80 car. maximum"), | |||
validators.required(message=u"Ce champ est obligatoire"), | |||
validators.Length(max=80)], filters=[strip_filter] | |||
) | |||
mail = TextField(u'Adresse รฉlectronique', [ | |||
validators.required(message=u"Ce champ est obligatoire"), | |||
validators.Email(message=u"Essayez aussi avec une adresse e-mail valide"), | |||
validators.Length(max=100)], | |||
filters=[strip_filter], | |||
description = u"Une adresse e-mail valide." + | |||
u"Cette adresse ne sera pas rendue publique, "+ | |||
u"et ne sera pas divulguรฉ ร des tiers." | |||
) | |||
captcha = TextField(u'Captcha', [validators.Length(max=8), captcha_check], | |||
filters=[strip_filter] | |||
) | |||
class ProfilForm(MyBaseForm): | |||
id = HiddenField() | |||
user_id = HiddenField() | |||
nom = TextField(u'Nom', [validators.Length(max=80)], | |||
filters=[strip_filter], | |||
description = u"Les espaces sont autorisรฉs, la ponctuation n'est " + | |||
u"pas autorisรฉe ร l'exception des points, traits d'union, " + | |||
u"apostrophes et tirets bas." | |||
) | |||
prenom = TextField(u'Prรฉnom', [validators.Length(max=80)], | |||
filters=[strip_filter], | |||
description = u"Les espaces sont autorisรฉs, la ponctuation n'est " + | |||
u"pas autorisรฉe ร l'exception des points, traits d'union, " + | |||
u"apostrophes et tirets bas." | |||
) | |||
pseudo = TextField(u'Pseudo', [validators.Length(max=80)], | |||
filters=[strip_filter], | |||
description = "Votre pseudo d'usage sur la toile." | |||
) | |||
mail = TextField(u'Adresse รฉlectronique', [validators.optional(), validators.Email(), validators.Length(max=100)], | |||
filters=[strip_filter], | |||
description = u"Une adresse e-mail valide. Tous les messages de ce systรจme" + | |||
u"seront envoyรฉs ร cette adresse. Cette adresse ne sera pas rendue publique,"+ | |||
u"et ne sera utilisรฉe que si vous dรฉsirez obtenir un nouveau mot de passe ou" + | |||
u"recevoir personnellement certaines nouvelles ou avertissements." | |||
) | |||
phone = TextField(u'Mobile', [validators.optional(), validators.Length(max=10), | |||
validators.Regexp("\d+", message=u"Le numรฉro de tรฉlรฉphone mobile ne doit contenir que des chiffres")], | |||
filters=[strip_filter], | |||
description = u"Un numรฉro de mobile valide. Afin de pouvoir rester en" + | |||
u"contact avec les personne de l'organisation, et pour vos รฉchanges. " + | |||
u"Ce numรฉro ne sera pas publiรฉ, et ne sera utilisรฉ que si " + | |||
u"vous dรฉsirez recevoir personnellement certaines nouvelles ou alertes." | |||
) | |||
website = TextField(u'Site web', [validators.optional(), validators.URL(), validators.Length(max=100)], | |||
filters=[strip_filter], | |||
description = "Renseignez ici votre site Web." | |||
) | |||
gpg_key = TextAreaField(u'Ma clรฉ GPG', | |||
[validators.optional(), validators.Length(max=9000)], | |||
filters=[strip_filter], | |||
description = u"Vous pouvez insรฉrer votre clรฉ GPG publique pour " + | |||
u"รฉchanger des donnรฉes sรฉcurisรฉes." | |||
) | |||
soc_link = TextAreaField('Mes autres identifiants', | |||
[validators.optional(), validators.Length(max=1000000)], | |||
filters=[strip_filter], | |||
description = u"Vous pouvez insรฉrer ici d'autres identifiants " + | |||
u"permettant aux autres de vous retrouver sur la toile (IRC, jabber, rรฉseaux sociaux etc)." | |||
) | |||
bio = TextAreaField('Biographie', [validators.optional(), validators.Length(max=1000000)], | |||
filters=[strip_filter] | |||
) | |||
tiersship = FieldList(FormField(TiersChoice)) | |||
class DateStartConfidenceForm(MyBaseForm): | |||
ConfidenceLevel = [ | |||
("0",u"exactement ร "), | |||
("1",u"approximativement ร "), | |||
("2",u"ร peu prรจs (5 ร 15 min) vers"), | |||
("3",u"ร une vache prรจs (1h) vers") | |||
] | |||
DayChoice = [("4","Jeudi"), ("5","Vendredi"), ("6","Samedi"), ("0","Dimanche"), ("1","Lundi")] | |||
Day = SelectField(u'Jour', choices=DayChoice ) | |||
Confidence = SelectField(u'Confiance', choices=ConfidenceLevel) | |||
Hour = TextField(u'Heure', [validators.Length(max=5, | |||
message=u"doit faire au maximum 5 caractรจres"), | |||
validators.Regexp("\d+:\d+", | |||
message=u"doit รชtre sous la forme HH:MM")], | |||
filters=[strip_filter]) | |||
start_time = HiddenField() | |||
class ItineraireForm(Form): | |||
start_place = SelectField(u'En partant de', coerce=int) | |||
arrival_place = SelectField(u'et ร destination de', coerce=int) | |||
itin_id = HiddenField() | |||
class AddItineraireForm(Form): | |||
itin = FormField(ItineraireForm) | |||
distance = DecimalField(u'Distance', [validators.Length(min=1, max=4)], | |||
filters=[strip_filter]) | |||
duration = DateTimeField(u'Durรฉe', [validators.Length(min=4, max=5)], | |||
filters=[strip_filter]) | |||
price = DecimalField(u'Prix approx.', [validators.Length(min=1, max=5)], | |||
filters=[strip_filter]) | |||
tr_pied = BooleanField(u'ร pied') | |||
tr_velo = BooleanField(u'ร vรฉlo') | |||
tr_moto = BooleanField(u'ร moto') | |||
tr_voiture = BooleanField(u'en voiture') | |||
tr_taxi = BooleanField(u'en taxi') | |||
tr_bus = BooleanField(u'en bus') | |||
tr_avion = BooleanField(u'en avion') | |||
description = TextAreaField(u'Description de l\'itinรฉraire') | |||
class AddMember(MyBaseForm): | |||
tiers_uid = HiddenField() | |||
nom = TextField(u'Nom', [validators.Length(max=80)], | |||
filters=[strip_filter], | |||
description = u"Les espaces sont autorisรฉs, la ponctuation n'est " + | |||
u"pas autorisรฉe ร l'exception des points, traits d'union, " + | |||
u"apostrophes et tirets bas." | |||
) | |||
prenom = TextField(u'Prรฉnom', [validators.Length(max=80)], | |||
filters=[strip_filter], | |||
description = u"Les espaces sont autorisรฉs, la ponctuation n'est " + | |||
u"pas autorisรฉe ร l'exception des points, traits d'union, " + | |||
u"apostrophes et tirets bas." | |||
) | |||
email = TextField(u'Email', [validators.required(), | |||
validators.length(max=10), | |||
validators.Email(message='Ceci ne ressemble pas ร une adresse valide')], | |||
description=u"Afin d'รฉviter la duplication d'information et les doublons inutile, "+ | |||
u"pensez d'abord ร lui demander de confirmer le mail qu'il a utilisรฉ lors de "+ | |||
u"son inscription sur le site.") | |||
add = SubmitField('Ajouter des membres') | |||
class TiersForm(MyBaseForm): | |||
name = TextField(u'Nom', [validators.Length(max=100)], | |||
filters=[strip_filter], | |||
description = u"Les espaces sont autorisรฉs, la ponctuation n'est " + | |||
u"pas autorisรฉe ร l'exception des points, traits d'union, " + | |||
u"apostrophes et tirets bas." | |||
) | |||
tiers_type = MySelectField('Nature', coerce=int) | |||
website = TextField(u'Site web', [validators.optional(), validators.URL(), validators.Length(max=100)], | |||
filters=[strip_filter], | |||
description = "Renseignez ici le site Web." | |||
) | |||
description = TextAreaField('Descriptif', | |||
[validators.optional(), validators.Length(max=1000000)], | |||
filters=[strip_filter], | |||
description = u"Vous pouvez insรฉrer les dรฉtails" | |||
) | |||
membership = FieldList(FormField(TiersMember)) | |||
class UpdateTiersForm(TiersForm): | |||
uid = HiddenField() | |||
tiers_id = HiddenField() | |||
class ExchCateg(MyBaseForm): | |||
exch_type = HiddenField() | |||
exch_subtype = TextField(u'Catรฉgorie', [validators.Length(max=80)], | |||
filters=[strip_filter], | |||
description = "Le nom de la categorie" | |||
) | |||
description = TextAreaField('Description', | |||
filters=[strip_filter]) | |||
class UpdateExchangeForm(MyBaseForm): | |||
exch_id = HiddenField() | |||
class AskCForm(ItineraireForm): | |||
ConfidenceLevel = [ | |||
("0",u"exactement ร "), | |||
("1",u"approximativement ร "), | |||
("2",u"ร peu prรจs (5 ร 15 min) vers"), | |||
("3",u"ร une vache prรจs (1h) vers") | |||
] | |||
DayChoice = [("4","Jeudi"), ("5","Vendredi"), ("6","Samedi"), ("0","Dimanche"), ("1","Lundi")] | |||
Day_start = SelectField(u'Jour', choices=DayChoice ) | |||
Confidence = SelectField(u'Confiance', choices=ConfidenceLevel) | |||
Hour_start = TextField(u'Heure', [validators.Length(max=5, | |||
message=u"doit faire au maximum 5 caractรจres"), | |||
validators.Regexp("\d+:\d+", | |||
message=u"doit รชtre sous la forme HH:MM")], | |||
filters=[strip_filter]) | |||
start_time = HiddenField() | |||
start_place = SelectField(u'En partant de', coerce=int) | |||
arrival_place = SelectField(u'et ร destination de', coerce=int) | |||
itin_id = HiddenField() | |||
class AskHForm(MyBaseForm): | |||
DayChoice = [("4",u"Jeudi ร Vendredi"), ("5",u"Vendredi ร Samedi"), ("6",u"Samedi ร Dimanche"), ("0",u"Dimanche ร Lundi")] | |||
Day_start = SelectField(u'Pour la nuit de', choices=DayChoice ) | |||
start_time = HiddenField() | |||
description = TextAreaField(u'Description de vos contraintes รฉventuelles', filters=[strip_filter], | |||
description = u"Dรฉcrivez ici vos souhaits et รฉventuellement " | |||
+ u"les contraintes ร prendre en compte. N'hรฉsitez pas ร donner des dรฉtails." | |||
) | |||
class AskMForm(MyBaseForm): | |||
DayChoice = [("4","Jeudi"), ("5","Vendredi"), ("6","Samedi"), ("0","Dimanche"), ("1","Lundi")] | |||
Day_start = SelectField(u"ร partir de", choices=DayChoice ) | |||
Hour_start = TextField(u'vers', [validators.Length(max=5, | |||
message=u"doit faire au maximum 5 caractรจres"), | |||
validators.Regexp("\d+:\d+", | |||
message=u"doit รชtre sous la forme HH:MM")], | |||
filters=[strip_filter]) | |||
start_time = HiddenField() | |||
Day_end = SelectField(u"Jusqu'ร ", choices=DayChoice ) | |||
Hour_end = TextField(u'vers', [validators.Length(max=5, | |||
message=u"doit faire au maximum 5 caractรจres"), | |||
validators.Regexp("\d+:\d+", | |||
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" | |||
) | |||
description = TextAreaField(u'Description du bien', filters=[strip_filter], | |||
description = u"Dรฉcrivez ici les biens que vous souhaitez" | |||
+ u"รฉchanger. N'hรฉsitez pas ร donner des dรฉtails." | |||
) | |||
class PropCForm(ItineraireForm): | |||
ConfidenceLevel = [ | |||
("0",u"exactement ร "), | |||
("1",u"approximativement ร "), | |||
("2",u"ร peu prรจs (5 ร 15 min) vers"), | |||
("3",u"ร une vache prรจs (1h) vers") | |||
] | |||
DayChoice = [("4","Jeudi"), ("5","Vendredi"), ("6","Samedi"), ("0","Dimanche"), ("1","Lundi")] | |||
Day_start = SelectField(u'Jour', choices=DayChoice ) | |||
Confidence = SelectField(u'Confiance', choices=ConfidenceLevel) | |||
Hour_start = TextField(u'Heure', [validators.Length(max=5, | |||
message=u"doit faire au maximum 5 caractรจres"), | |||
validators.Regexp("\d+:\d+", | |||
message=u"doit รชtre sous la forme HH:MM")], | |||
filters=[strip_filter]) | |||
start_time = HiddenField() | |||
start_place = SelectField(u'En partant de', coerce=int) | |||
arrival_place = SelectField(u'et ร destination de', coerce=int) | |||
itin_id = HiddenField() | |||
class PropHForm(MyBaseForm): | |||
DayChoice = [("4",u"Jeudi ร Vendredi"), ("5",u"Vendredi ร Samedi"), ("6",u"Samedi ร Dimanche"), ("0",u"Dimanche ร Lundi")] | |||
Day_start = SelectField(u'Pour la nuit de', choices=DayChoice ) | |||
start_time = HiddenField() | |||
exch_categ = SelectField(u'Type de couchage', coerce=int, | |||
description = u"Indiquez ici le type de couchage proposรฉ") | |||
description = TextAreaField(u'Quelques mots autour du logement que vous proposez', filters=[strip_filter], | |||
description = u"Dรฉcrivez ici quelques dรฉtails sur le logement que vous souhaitez " | |||
+ u"proposer, les contraintes ร prendre en compte. N'hรฉsitez pas ร donner des dรฉtails." | |||
) | |||
place_id = SelectField(u'Emplacement', coerce=int, | |||
description = u"Indiquez ici une des adresses que vous avez proposรฉ") | |||
class PropMForm(MyBaseForm): | |||
DayChoice = [("4","Jeudi"), ("5","Vendredi"), ("6","Samedi"), ("0","Dimanche"), ("1","Lundi")] | |||
Day_start = SelectField(u"ร partir de", choices=DayChoice ) | |||
Hour_start = TextField(u'vers', [validators.Length(max=5, | |||
message=u"doit faire au maximum 5 caractรจres"), | |||
validators.Regexp("\d+:\d+", | |||
message=u"doit รชtre sous la forme HH:MM")], | |||
filters=[strip_filter]) | |||
start_time = HiddenField() | |||
Day_end = SelectField(u"Jusqu'a ", choices=DayChoice ) | |||
Hour_end = TextField(u'vers', [validators.Length(max=5, | |||
message=u"doit faire au maximum 5 caractรจres"), | |||
validators.Regexp("\d+:\d+", | |||
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." | |||
) | |||
class UpdateAskCForm(AskCForm, UpdateExchangeForm): | |||
pass | |||
class UpdateAskHForm(AskHForm, UpdateExchangeForm): | |||
pass | |||
class UpdateAskMForm(AskMForm, UpdateExchangeForm): | |||
pass | |||
class UpdatePropCForm(PropCForm, UpdateExchangeForm): | |||
pass | |||
class UpdatePropHForm(PropHForm, UpdateExchangeForm): | |||
pass | |||
class UpdatePropMForm(PropMForm, UpdateExchangeForm): | |||
pass |
@@ -0,0 +1,616 @@ | |||
# -*- coding: utf8 -*- | |||
import datetime | |||
import sqlalchemy as sa | |||
import hashlib | |||
from pyramid.security import unauthenticated_userid | |||
from sqlalchemy.orm import relationship, backref | |||
from sqlalchemy import func | |||
from sqlalchemy import or_ | |||
from sqlalchemy import ( | |||
Column, | |||
Integer, | |||
Text, | |||
Unicode, | |||
UnicodeText, | |||
DateTime, | |||
Enum, | |||
Boolean, | |||
ForeignKey | |||
) | |||
from slugify import slugify | |||
from webhelpers.text import urlify | |||
from webhelpers.paginate import PageURL_WebOb, Page | |||
from webhelpers.date import time_ago_in_words | |||
from collections import namedtuple | |||
from sqlalchemy.ext.declarative import declarative_base | |||
from sqlalchemy.orm import ( | |||
scoped_session, | |||
sessionmaker, | |||
relation | |||
) | |||
from zope.sqlalchemy import ZopeTransactionExtension | |||
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) | |||
Base = declarative_base() | |||
CurrentYear = 2015 | |||
class TasksArea(Base): | |||
__tablename__ = 'staff_tasks_area' | |||
uid = Column(Integer, primary_key=True) | |||
name = Column(Unicode(80)) | |||
description = Column(UnicodeText) | |||
class Tasks(Base): | |||
__tablename__ = 'staff_tasks' | |||
uid = Column(Integer, primary_key=True) | |||
area_uid = Column(Integer, ForeignKey('staff_tasks_area.uid') ) | |||
due_date = Column(DateTime, default=None) | |||
closed_by = Column(Integer, ForeignKey('users.uid') ) | |||
closed_date = Column(DateTime, default=None) | |||
closed = Column(Integer, default=0) | |||
name = Column(Unicode(80)) | |||
description = Column(UnicodeText) | |||
@classmethod | |||
def by_id(cls, id): | |||
return DBSession.query(cls).filter(cls.uid == id).first() | |||
class User_Event(Base): | |||
""" Crรฉer le lien entre la personne et l' รฉvenement en fonction de l'annรฉe""" | |||
__tablename__ = 'user_event_link' | |||
uid = Column(Integer, primary_key=True) | |||
event_uid = Column(Integer, ForeignKey('events.uid') ) | |||
#, primary_key=True) | |||
# | |||
user_uid = Column(Integer, ForeignKey('users.uid') ) | |||
#, primary_key=True) | |||
# | |||
year_uid = Column(Integer, ForeignKey('jm2l_year.year_uid'), default=CurrentYear) | |||
role = Column(Unicode(80)) | |||
# Define some relation | |||
#user = relationship('User', backref=backref("events_assoc") ) | |||
#event = relationship('events', backref=backref("users_assoc") ) | |||
class JM2L_Year(Base): | |||
__tablename__ = 'jm2l_year' | |||
year_uid = Column(Integer, primary_key=True) | |||
description = Column(UnicodeText) | |||
doss_presse = Column(UnicodeText) | |||
state = Column(Enum('Archived', 'Cancelled', 'Ongoing')) | |||
start_time = Column(DateTime, default=datetime.datetime.utcnow) | |||
end_time = Column(DateTime, default=datetime.datetime.utcnow) | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
@property | |||
def AvailableTimeSlots(self, TimeStep=30): | |||
Available = self.end_time - self.start_time | |||
NbMinutes = Available.total_seconds()/60 | |||
NbSteps = NbMinutes/TimeStep | |||
# Create the range of date each 30min | |||
date_list = [self.start_time + datetime.timedelta(minutes=TimeStep*x) for x in range(0, int(NbSteps))] | |||
# Remove out of range datetime | |||
# Remove hours > 19h | |||
date_list = filter(lambda x:x.hour < 19, date_list) | |||
# Remove hours < 10h | |||
date_list = filter(lambda x:x.hour >= 10, date_list) | |||
# Remove 12h < hours < 13h | |||
date_list = filter(lambda x: x.hour<12 or x.hour>=13, date_list) | |||
return date_list | |||
class User(Base): | |||
__tablename__ = 'users' | |||
uid = Column(Integer, primary_key=True) | |||
user_id = Column(Integer) | |||
nom = Column(Unicode(80)) | |||
prenom = Column(Unicode(80)) | |||
pseudo = Column(Unicode(80)) | |||
slug = Column(Unicode(164)) | |||
mail = Column(Unicode(100)) | |||
password = Column(Unicode(100), nullable=False) | |||
fonction = Column(Unicode(80)) | |||
website = Column(Unicode(100)) | |||
phone = Column(Unicode(10)) | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_logged = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
active = Column(Integer, default=1) | |||
bio = Column(UnicodeText) | |||
gpg_key = Column(UnicodeText) | |||
soc_link = Column(UnicodeText) | |||
Staff = Column(Integer, default=0) | |||
# relations | |||
tiers = relationship('Tiers', secondary='user_tiers_link' ) | |||
events = relationship('Event', secondary='user_event_link' ) | |||
tiersship = relationship('User_Tiers', backref="matching_users") | |||
@classmethod | |||
def by_id(cls, id): | |||
return DBSession.query(cls).filter(cls.uid == id).first() | |||
@classmethod | |||
def by_slug(cls, slug): | |||
return DBSession.query(cls).filter(cls.slug == slug).first() | |||
@classmethod | |||
def by_user_id(cls, user_id): | |||
return DBSession.query(cls).filter(cls.user_id == user_id).first() | |||
@classmethod | |||
def by_name(cls, name): | |||
return DBSession.query(cls).filter(cls.nom == name).first() | |||
@classmethod | |||
def by_hash(cls, tsthash): | |||
for u in DBSession.query(cls): | |||
print u.nom, u.my_hash | |||
if u.my_hash==tsthash: | |||
return u | |||
return None | |||
@property | |||
def my_hash(self): | |||
m = hashlib.sha1() | |||
m.update("Nobody inspects ") | |||
if self.nom: | |||
m.update(unicode.encode(self.nom,'utf8')) | |||
if self.pseudo: | |||
m.update(unicode.encode(self.pseudo,'utf8')) | |||
if self.prenom: | |||
m.update(unicode.encode(self.prenom,'utf8')) | |||
m.update(" the spammish repetition") | |||
return m.hexdigest() | |||
@property | |||
def Photos(self): | |||
return DBSession.query(Media.filename) \ | |||
.filter(Media.media_table=='users') \ | |||
.filter(Media.media_type=='Image') \ | |||
.filter(Media.link_id == self.user_id).all() | |||
@property | |||
def PhotosLinks(self): | |||
from .upload import MediaPath | |||
return MediaPath().get_list('users', self.uid) | |||
@property | |||
def PhotosThumb(self): | |||
from .upload import MediaPath | |||
return MediaPath().get_thumb('users', self.uid) | |||
def verify_password(self, password): | |||
return self.password == password | |||
class TiersOpt(Base): | |||
__tablename__ = 'tiers_opt' | |||
uid = Column(Integer, primary_key=True) | |||
entity_type = Column(Unicode(80), nullable=False) | |||
entity_subtype = Column(Unicode(80)) | |||
entity_role = Column(Unicode(80)) | |||
@property | |||
def slug_entity_type(self): | |||
return slugify(self.entity_type) | |||
@property | |||
def slug_entity_subtype(self): | |||
return slugify(self.entity_subtype) | |||
@classmethod | |||
def get_entity_type(cls): | |||
return DBSession.query(cls, func.count(Tiers.ent_type).label('count'))\ | |||
.outerjoin(Tiers)\ | |||
.group_by(cls.entity_type).all() | |||
@classmethod | |||
def get_entity_sub_type(cls, entity_type): | |||
return DBSession.query(cls, func.count(Tiers.ent_type).label('count'))\ | |||
.outerjoin(Tiers)\ | |||
.filter(cls.entity_type == entity_type)\ | |||
.group_by(cls.entity_subtype).all() | |||
@classmethod | |||
def by_id(cls, id): | |||
return DBSession.query(cls).filter(cls.uid == id).first() | |||
class Tiers(Base): | |||
__tablename__ = 'tiers' | |||
uid = Column(Integer, primary_key=True) | |||
tiers_id = Column(Integer) | |||
name = Column(Unicode(100), nullable=False) | |||
slug = Column(Unicode(100)) | |||
description = Column(UnicodeText) | |||
website = Column(Unicode(100)) | |||
tiers_type = Column(Integer, ForeignKey('tiers_opt.uid'), default=1) | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
# relations | |||
ent_type = relationship('TiersOpt') | |||
#members = relationship('User', secondary='user_tiers_link' ) | |||
members = relationship(User, | |||
secondary='user_tiers_link', | |||
backref=backref('associate', uselist=False), | |||
lazy='dynamic') | |||
creator_id = Column(Integer) | |||
membership = relationship('User_Tiers', backref="matching_tiers") | |||
roles = relationship('Tiers', secondary='role_tiers_link' ) | |||
@classmethod | |||
def by_id(cls, id): | |||
return DBSession.query(cls).filter(cls.uid == id).first() | |||
@classmethod | |||
def by_slug(cls, slug): | |||
return DBSession.query(cls).filter(cls.slug == slug).first() | |||
@property | |||
def get_entity_type(self): | |||
return DBSession.query(TiersOpt)\ | |||
.filter(TiersOpt.uid == self.tiers_type).first() | |||
@property | |||
def logo(self): | |||
return DBSession.query(Media) \ | |||
.filter(Media.media_table == 'tiers') \ | |||
.filter(Media.media_type == 'Image') \ | |||
.filter(Media.link_id == self.uid) | |||
@property | |||
def PhotosLinks(self): | |||
from .upload import MediaPath | |||
return MediaPath().get_list('tiers', self.uid) | |||
@property | |||
def ThumbLinks(self): | |||
from .upload import MediaPath | |||
return MediaPath().get_thumb('tiers', self.uid) | |||
class Role_Tiers(Base): | |||
""" Crรฉer le lien entre le tiers et son rรดle dans l'รฉvenement en fonction de l'annรฉe""" | |||
__tablename__ = 'role_tiers_link' | |||
uid_role = Column(Integer, primary_key=True) | |||
year_uid = Column(Integer, ForeignKey('jm2l_year.year_uid'), default=CurrentYear) | |||
tiers_uid = Column(Integer, ForeignKey('tiers.uid')) | |||
tiers = relationship(Tiers, backref=backref("roles_assoc") ) | |||
tiers_role = Column(Enum('Exposant', 'Sponsor', 'Donateur')) | |||
class User_Tiers(Base): | |||
""" Crรฉer le lien entre la personne et le tiers en fonction de l'annรฉe""" | |||
__tablename__ = 'user_tiers_link' | |||
uid_tiers = Column(Integer, primary_key=True) | |||
year_uid = Column(Integer, ForeignKey('jm2l_year.year_uid'), default=CurrentYear) | |||
tiers_uid = Column(Integer, ForeignKey('tiers.uid')) | |||
tiers = relationship(Tiers, backref=backref("users_assoc") ) | |||
user_uid = Column(Integer, ForeignKey('users.uid')) | |||
user = relationship(User, backref=backref("tiers_assoc") ) | |||
role = Column(Unicode(80)) | |||
class Media(Base): | |||
__tablename__ = 'medias' | |||
media_id = Column(Integer, primary_key=True) | |||
for_year = Column(Integer, ForeignKey('jm2l_year.year_uid')) | |||
media_table = Column(Enum('users', 'tiers', 'place', 'salle', 'RIB', 'Justif', 'event' )) | |||
media_type = Column(Enum('Image', 'Video', 'Pres', 'Document')) | |||
link_id = Column(Integer) | |||
mime_type = Column(Unicode(20)) | |||
size = Column(Integer) | |||
width = Column(Integer) | |||
height = Column(Integer) | |||
length = Column(Integer) | |||
filename = Column(UnicodeText) | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
@property | |||
def get_path(self): | |||
return '/upload/%s/%s/%s' % (self.media_type, self.media_table, self.filename) | |||
class SallePhy(Base): | |||
""" Reprรฉsente une salle dans les locaux """ | |||
__tablename__ = 'phy_salle' | |||
uid = Column(Integer, primary_key=True) | |||
name = Column(Unicode(40)) # Numรฉro de salle vu de polytech | |||
slug = Column(Unicode(40)) | |||
description = Column(UnicodeText) # Description du matรฉriel disponible | |||
nb_places = Column(Integer, default=0) # Nombre de places assises | |||
@classmethod | |||
def by_id(cls, uid): | |||
return DBSession.query(cls).filter(cls.uid == uid).first() | |||
class Salles(Base): | |||
__tablename__ = 'salle' | |||
salle_id = Column(Integer, primary_key=True) | |||
phy_salle_id = Column(Integer, ForeignKey('phy_salle.uid')) | |||
year_uid = Column(Integer, ForeignKey('jm2l_year.year_uid'), default=CurrentYear) | |||
name = Column(Unicode(40)) | |||
place_type = Column(Enum('Conference', 'Stand', 'Ateliers', 'Autres')) | |||
description = Column(UnicodeText) # Description du matรฉriel disponible | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
@classmethod | |||
def by_id(cls, uid): | |||
return DBSession.query(cls).filter(cls.salle_id == uid).first() | |||
class Place(Base): | |||
__tablename__ = 'place' | |||
place_id = Column(Integer, primary_key=True) | |||
usage = Column(Boolean, default=False) # By Default / Extended | |||
place_type = Column(Enum('Aeroport', 'Gare', 'JM2L', \ | |||
'Hotel', 'Habitant', 'Restaurant', 'Autres')) | |||
display_name = Column(Unicode(20)) | |||
name = Column(Unicode(80)) | |||
slug = Column(Unicode(80)) | |||
specific = Column(Unicode(80)) # eg Terminal 2 | |||
gps_coord = Column(Unicode(30)) | |||
adresse = Column(Unicode(100)) | |||
codePostal = Column(Unicode(5)) | |||
ville = Column(Unicode(40)) | |||
website = Column(Unicode(100)) | |||
description = Column(UnicodeText) | |||
created_by = Column(Integer, ForeignKey('users.user_id')) | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
@classmethod | |||
def by_id(cls, uid): | |||
return DBSession.query(cls).filter(cls.place_id == uid).first() | |||
@classmethod | |||
def get_list(cls, All=False): | |||
if All: | |||
return DBSession.query(cls).all() | |||
else: | |||
return DBSession.query(cls).filter(cls.usage==True).all() | |||
class Itineraire(Base): | |||
__tablename__ = 'itineraire' | |||
itin_id = Column(Integer, primary_key=True) | |||
start_place = Column(Integer, ForeignKey('place.place_id')) # Place link | |||
arrival_place = Column(Integer, ForeignKey('place.place_id')) # Place link | |||
distance = Column(Integer) | |||
duration = Column(Integer) | |||
price = Column(Integer) | |||
tr_pied = Column(Boolean, default=False) | |||
tr_velo = Column(Boolean, default=False) | |||
tr_moto = Column(Boolean, default=False) | |||
tr_voiture = Column(Boolean, default=False) | |||
tr_bus = Column(Boolean, default=False) | |||
tr_taxi = Column(Boolean, default=False) | |||
tr_avion = Column(Boolean, default=False) | |||
description = Column(UnicodeText) | |||
created_by = Column(Integer, ForeignKey('users.user_id')) # User link | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
# relations | |||
start = relationship(Place, foreign_keys=[start_place]) | |||
arrival = relationship(Place, foreign_keys=[arrival_place]) | |||
class Exchange_Cat(Base): | |||
__tablename__ = 'exchange_category' | |||
cat_id = Column(Integer, primary_key=True) | |||
exch_type = Column(Enum('H', 'C', 'M')) # Heberg, Co-voit, Materiel | |||
exch_subtype = Column(Unicode(80)) | |||
description = Column(UnicodeText) | |||
class Exchange(Base): | |||
__tablename__ = 'exchanges' | |||
exch_id = Column(Integer, primary_key=True) | |||
for_year = Column(Integer, ForeignKey('jm2l_year.year_uid')) # link JM2L_Year | |||
exch_done = Column(Boolean, default=False) | |||
exch_state = Column(Enum('Ask', 'Proposal')) | |||
exch_type = Column(Enum('H', 'C', 'M')) # Heberg, Co-Voit, Materiel | |||
exch_categ = Column(Integer, ForeignKey('exchange_category.cat_id')) # Exchange_Cat link | |||
# Users | |||
asker_id = Column(Integer, ForeignKey('users.uid')) # User link | |||
provider_id = Column(Integer, ForeignKey('users.uid')) # User link | |||
start_time = Column(DateTime, default=datetime.datetime.utcnow) | |||
end_time = Column(DateTime, default=datetime.datetime.utcnow) | |||
# Co-voiturage | |||
itin_id = Column(Integer, ForeignKey('itineraire.itin_id')) # Itineraire link | |||
# Hebergement | |||
place_id = Column(Integer, ForeignKey('place.place_id')) # Place link | |||
# Materiel | |||
duration = Column(Integer) | |||
description = Column(UnicodeText) | |||
pictures = Column(Unicode(80)) | |||
created_by = Column(Integer) # User link | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
# relations | |||
Category = relationship(Exchange_Cat, backref="exchanges") | |||
Itin = relationship(Itineraire, backref="exchanged") | |||
asker = relationship(User, foreign_keys=[asker_id], backref="asked") | |||
provider = relationship(User, foreign_keys=[provider_id], backref="provided") | |||
@classmethod | |||
def by_id(cls, id): | |||
return DBSession.query(cls).filter(cls.exch_id == id).first() | |||
@classmethod | |||
def get_counters(cls): | |||
return DBSession.query(cls.exch_state, cls.exch_type, cls.exch_done, func.count(cls.exch_id))\ | |||
.filter(cls.for_year==2015)\ | |||
.group_by(cls.exch_state, cls.exch_type, cls.exch_done) | |||
@classmethod | |||
def get_my_counters(cls, uid): | |||
return DBSession.query(cls.exch_state, cls.exch_type, cls.exch_done, func.count(cls.exch_id))\ | |||
.filter(cls.for_year==2015)\ | |||
.filter( or_(cls.asker_id==uid, cls.provider_id==uid) )\ | |||
.group_by(cls.exch_state, cls.exch_type, cls.exch_done) | |||
@classmethod | |||
def get_overview(cls, uid): | |||
# Build a Dic with all exchange to save database access | |||
DicResult= {} | |||
for extype in ['F','C','H','M']: | |||
DicResult[extype] = {} | |||
for exstate in ['Ask','Proposal','Missing','Agree']: | |||
DicResult[extype][exstate]=[] | |||
DicResult[extype]['Counters']={'AllAsk':0, 'AllProp':0, 'AllAgree':0} | |||
Query = DBSession.query(cls)\ | |||
.filter(cls.for_year==2015)\ | |||
.order_by(cls.start_time).all() | |||
for item in Query: | |||
if item.exch_done: | |||
DicResult[item.exch_type]['Counters']['AllAgree']+=1 | |||
if item.exch_state=='Ask': | |||
DicResult[item.exch_type]['Counters']['AllAsk']+=1 | |||
if item.exch_state=='Proposal': | |||
DicResult[item.exch_type]['Counters']['AllProp']+=1 | |||
if item.asker_id==uid or item.provider_id==uid: | |||
if item.asker_id==uid and item.exch_state=='Ask': | |||
DicResult[item.exch_type]['Ask'].append(item) | |||
if item.provider_id==uid and item.exch_state=='Ask': | |||
DicResult[item.exch_type]['Proposal'].append(item) | |||
if item.asker_id==uid and item.exch_state=='Proposal': | |||
DicResult[item.exch_type]['Ask'].append(item) | |||
if item.provider_id==uid and item.exch_state=='Proposal': | |||
DicResult[item.exch_type]['Proposal'].append(item) | |||
if item.exch_done: | |||
DicResult[item.exch_type]['Agree'].append(item) | |||
else: | |||
DicResult[item.exch_type]['Missing'].append(item) | |||
return DicResult | |||
@classmethod | |||
def get_pub_list(cls, exch_type): | |||
return DBSession.query(cls).filter(cls.for_year==2015 and exch_state in ['Ask','Proposal'])\ | |||
.filter(cls.exch_type=='%s' % exch_type)\ | |||
.filter(cls.exch_done==False)\ | |||
.all() | |||
@classmethod | |||
def get_my_list(cls, uid, exch_type): | |||
DicResult = {} | |||
DicResult['Ask']=DBSession.query(cls)\ | |||
.filter(cls.for_year==2015)\ | |||
.filter( or_(cls.asker_id==uid, cls.provider_id==uid) )\ | |||
.filter(cls.exch_type=='%s' % exch_type)\ | |||
.filter(cls.exch_state=='Ask')\ | |||
.order_by(cls.start_time).all() | |||
DicResult['Proposal']=DBSession.query(cls)\ | |||
.filter(cls.for_year==2015)\ | |||
.filter( or_(cls.asker_id==uid, cls.provider_id==uid) )\ | |||
.filter(cls.exch_type=='%s' % exch_type)\ | |||
.filter(cls.exch_state=='Proposal')\ | |||
.order_by(cls.start_time).all() | |||
return DicResult | |||
class Sejour(Base): | |||
__tablename__ = 'sejour' | |||
sej_id = Column(Integer, primary_key=True) | |||
user_id = Column(Integer, ForeignKey('users.user_id')) # User link | |||
for_year = Column(Integer, ForeignKey('jm2l_year.year_uid')) # link JM2L_Year | |||
arrival_time = Column(DateTime) | |||
arrival_place = Column(Integer, ForeignKey('place.place_id')) # Place link | |||
depart_time = Column(DateTime) | |||
depart_place = Column(Integer, ForeignKey('place.place_id')) # Place link | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
class Event(Base): | |||
__tablename__ = 'events' | |||
uid = Column(Integer, primary_key=True) | |||
salle_uid = Column(Integer, ForeignKey('salle.salle_id')) | |||
event_uid = Column(Integer) | |||
for_year = Column(Integer, ForeignKey('jm2l_year.year_uid')) # link JM2L_Year | |||
name = Column(Unicode(100), nullable=False) | |||
slug = Column(Unicode(100)) | |||
event_type = Column(Enum('Stand', 'Table ronde', 'Atelier', 'Concert', 'Conference', 'Repas')) | |||
start_time = Column(DateTime, default=datetime.datetime.utcnow) | |||
end_time = Column(DateTime, default=datetime.datetime.utcnow) | |||
description = Column(UnicodeText) | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
last_change = Column(DateTime, default=datetime.datetime.utcnow) | |||
intervenants = relationship(User, | |||
secondary='user_event_link', | |||
backref=backref('participate', uselist=False), | |||
lazy='dynamic') | |||
interventions = relationship(User_Event, backref="matching_events") | |||
Salle = relationship(Salles, backref='allevents') | |||
@classmethod | |||
def by_id(cls, uid): | |||
return DBSession.query(cls)\ | |||
.filter(cls.uid == uid).first() | |||
@classmethod | |||
def by_slug(cls, slug, year=None): | |||
if not year is None: | |||
return DBSession.query(cls)\ | |||
.filter(cls.for_year==year)\ | |||
.filter(cls.slug == slug).first() | |||
else: | |||
return DBSession.query(cls)\ | |||
.filter(cls.slug == slug).first() | |||
@property | |||
def video(self): | |||
return DBSession.query(Media) \ | |||
.filter(Media.media_table == 'event') \ | |||
.filter(Media.media_type == 'Video') \ | |||
.filter(Media.link_id == self.uid) | |||
@property | |||
def presentation(self): | |||
return DBSession.query(Media) \ | |||
.filter(Media.media_table == 'event') \ | |||
.filter(Media.media_type == 'Pres') \ | |||
.filter(Media.link_id == self.uid) | |||
@property | |||
def created_in_words(self): | |||
return time_ago_in_words(self.created) | |||
class Entry(Base): | |||
__tablename__ = 'entries' | |||
id = Column(Integer, primary_key=True) | |||
active = Column(Integer, default=True) | |||
title = Column(Unicode(255), unique=True, nullable=False) | |||
body = Column(UnicodeText, default=u'') | |||
created = Column(DateTime, default=datetime.datetime.utcnow) | |||
edited = Column(DateTime, default=datetime.datetime.utcnow) | |||
@classmethod | |||
def all(cls): | |||
return DBSession.query(Entry).order_by(sa.desc(Entry.created)) | |||
@classmethod | |||
def by_id(cls, id): | |||
return DBSession.query(Entry).filter(Entry.id == id).first() | |||
@property | |||
def slug(self): | |||
return urlify(self.title) | |||
@property | |||
def created_in_words(self): | |||
return time_ago_in_words(self.created) | |||
@classmethod | |||
def get_paginator(cls, request, page=1): | |||
page_url = PageURL_WebOb(request) | |||
return Page(Entry.all(), page, url=page_url, items_per_page=5) | |||
#class Seances(Base): | |||
# __tablename__ = 'seances' | |||
def get_user(request): | |||
# the below line is just an example, use your own method of | |||
# accessing a database connection here (this could even be another | |||
# request property such as request.db, implemented using this same | |||
# pattern). | |||
userid = unauthenticated_userid(request) | |||
if userid is not None: | |||
# this should return None if the user doesn't exist | |||
# in the database | |||
return DBSession.query(User).filter(User.uid==userid).first() |
@@ -0,0 +1 @@ | |||
# package |
@@ -0,0 +1,40 @@ | |||
# -*- coding: utf8 -*- | |||
import os | |||
import sys | |||
import transaction | |||
import time | |||
import lxml.etree as ET | |||
from datetime import datetime | |||
from sqlalchemy import engine_from_config | |||
from sqlalchemy import create_engine | |||
import unicodedata | |||
import urllib | |||
#ย Usefull tools | |||
from slugify import slugify | |||
from sqlite3 import dbapi2 as sqlite | |||
from os import path | |||
from pyramid.paster import ( | |||
get_appsettings, | |||
setup_logging, | |||
) | |||
from jm2l.models import * | |||
def usage(argv): | |||
cmd = os.path.basename(argv[0]) | |||
print('usage: %s <config_uri>\n' | |||
'(example: "%s development.ini")' % (cmd, cmd)) | |||
sys.exit(1) | |||
def main(argv=sys.argv): | |||
if len(argv) != 2: | |||
usage(argv) | |||
config_uri = "development.ini" | |||
setup_logging(config_uri) | |||
settings = get_appsettings(config_uri) | |||
engine = engine_from_config(settings, 'sqlalchemy.') | |||
DBSession.configure(bind=engine) | |||
Base.metadata.create_all(engine) |
@@ -0,0 +1,25 @@ | |||
# -*- coding: utf8 -*- | |||
from pyramid.security import Allow, Everyone, Authenticated | |||
USERS = { 1:'editor', | |||
'editor':'editor', | |||
'viewer':'viewer'} | |||
GROUPS = {'editor':['group:editors'], 1:['group:editors']} | |||
def groupfinder(userid, request): | |||
if userid in USERS: | |||
return GROUPS.get(userid, []) | |||
class EntryFactory(object): | |||
__acl__ = [(Allow, Everyone, 'view'), | |||
(Allow, Authenticated, 'create'), | |||
(Allow, Authenticated, 'edit'), ] | |||
def __init__(self, request): | |||
pass | |||
class RootFactory(object): | |||
__acl__ = [ (Allow, Everyone, 'view'), | |||
(Allow, 'group:editors', 'edit') ] | |||
def __init__(self, request): | |||
pass |
@@ -0,0 +1,60 @@ | |||
<!doctype html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<title>Page Not Found</title> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<style> | |||
* { | |||
line-height: 1.2; | |||
margin: 0; | |||
} | |||
html { | |||
color: #888; | |||
display: table; | |||
font-family: sans-serif; | |||
height: 100%; | |||
text-align: center; | |||
width: 100%; | |||
} | |||
body { | |||
display: table-cell; | |||
vertical-align: middle; | |||
margin: 2em auto; | |||
} | |||
h1 { | |||
color: #555; | |||
font-size: 2em; | |||
font-weight: 400; | |||
} | |||
p { | |||
margin: 0 auto; | |||
width: 280px; | |||
} | |||
@media only screen and (max-width: 280px) { | |||
body, p { | |||
width: 95%; | |||
} | |||
h1 { | |||
font-size: 1.5em; | |||
margin: 0 0 0.3em; | |||
} | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<h1>Page Not Found</h1> | |||
<p>Sorry, but the page you were trying to view does not exist.</p> | |||
</body> | |||
</html> | |||
<!-- IE needs 512+ bytes: http://blogs.msdn.com/b/ieinternals/archive/2010/08/19/http-error-pages-in-internet-explorer.aspx --> |
@@ -0,0 +1,470 @@ | |||
/*! | |||
* Bootstrap v3.3.1 (http://getbootstrap.com) | |||
* Copyright 2011-2014 Twitter, Inc. | |||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |||
*/ | |||
.btn-default, | |||
.btn-primary, | |||
.btn-success, | |||
.btn-info, | |||
.btn-warning, | |||
.btn-danger { | |||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); | |||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); | |||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); | |||
} | |||
.btn-default:active, | |||
.btn-primary:active, | |||
.btn-success:active, | |||
.btn-info:active, | |||
.btn-warning:active, | |||
.btn-danger:active, | |||
.btn-default.active, | |||
.btn-primary.active, | |||
.btn-success.active, | |||
.btn-info.active, | |||
.btn-warning.active, | |||
.btn-danger.active { | |||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); | |||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); | |||
} | |||
.btn-default .badge, | |||
.btn-primary .badge, | |||
.btn-success .badge, | |||
.btn-info .badge, | |||
.btn-warning .badge, | |||
.btn-danger .badge { | |||
text-shadow: none; | |||
} | |||
.btn:active, | |||
.btn.active { | |||
background-image: none; | |||
} | |||
.btn-default { | |||
text-shadow: 0 1px 0 #fff; | |||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); | |||
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); | |||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); | |||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #dbdbdb; | |||
border-color: #ccc; | |||
} | |||
.btn-default:hover, | |||
.btn-default:focus { | |||
background-color: #e0e0e0; | |||
background-position: 0 -15px; | |||
} | |||
.btn-default:active, | |||
.btn-default.active { | |||
background-color: #e0e0e0; | |||
border-color: #dbdbdb; | |||
} | |||
.btn-default:disabled, | |||
.btn-default[disabled] { | |||
background-color: #e0e0e0; | |||
background-image: none; | |||
} | |||
.btn-primary { | |||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); | |||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); | |||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); | |||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #245580; | |||
} | |||
.btn-primary:hover, | |||
.btn-primary:focus { | |||
background-color: #265a88; | |||
background-position: 0 -15px; | |||
} | |||
.btn-primary:active, | |||
.btn-primary.active { | |||
background-color: #265a88; | |||
border-color: #245580; | |||
} | |||
.btn-primary:disabled, | |||
.btn-primary[disabled] { | |||
background-color: #265a88; | |||
background-image: none; | |||
} | |||
.btn-success { | |||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); | |||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); | |||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); | |||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #3e8f3e; | |||
} | |||
.btn-success:hover, | |||
.btn-success:focus { | |||
background-color: #419641; | |||
background-position: 0 -15px; | |||
} | |||
.btn-success:active, | |||
.btn-success.active { | |||
background-color: #419641; | |||
border-color: #3e8f3e; | |||
} | |||
.btn-success:disabled, | |||
.btn-success[disabled] { | |||
background-color: #419641; | |||
background-image: none; | |||
} | |||
.btn-info { | |||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); | |||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); | |||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); | |||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #28a4c9; | |||
} | |||
.btn-info:hover, | |||
.btn-info:focus { | |||
background-color: #2aabd2; | |||
background-position: 0 -15px; | |||
} | |||
.btn-info:active, | |||
.btn-info.active { | |||
background-color: #2aabd2; | |||
border-color: #28a4c9; | |||
} | |||
.btn-info:disabled, | |||
.btn-info[disabled] { | |||
background-color: #2aabd2; | |||
background-image: none; | |||
} | |||
.btn-warning { | |||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); | |||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); | |||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); | |||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #e38d13; | |||
} | |||
.btn-warning:hover, | |||
.btn-warning:focus { | |||
background-color: #eb9316; | |||
background-position: 0 -15px; | |||
} | |||
.btn-warning:active, | |||
.btn-warning.active { | |||
background-color: #eb9316; | |||
border-color: #e38d13; | |||
} | |||
.btn-warning:disabled, | |||
.btn-warning[disabled] { | |||
background-color: #eb9316; | |||
background-image: none; | |||
} | |||
.btn-danger { | |||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); | |||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); | |||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); | |||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #b92c28; | |||
} | |||
.btn-danger:hover, | |||
.btn-danger:focus { | |||
background-color: #c12e2a; | |||
background-position: 0 -15px; | |||
} | |||
.btn-danger:active, | |||
.btn-danger.active { | |||
background-color: #c12e2a; | |||
border-color: #b92c28; | |||
} | |||
.btn-danger:disabled, | |||
.btn-danger[disabled] { | |||
background-color: #c12e2a; | |||
background-image: none; | |||
} | |||
.thumbnail, | |||
.img-thumbnail { | |||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); | |||