Skip to content

Commit

Permalink
campaign creation permission and experimental flags column approach
Browse files Browse the repository at this point in the history
  • Loading branch information
mahmoud committed Sep 16, 2016
1 parent ac30a94 commit 8829a15
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 9 deletions.
11 changes: 9 additions & 2 deletions montage/rdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy

from simple_serdes import DictableBase
from simple_serdes import DictableBase, JSONEncodedDict


Base = declarative_base(cls=DictableBase)
Expand All @@ -42,6 +42,7 @@ class User(Base):

id = Column(Integer, primary_key=True)
username = Column(String)
flags = Column(JSONEncodedDict)
last_login_date = Column(DateTime)

create_date = Column(DateTime, server_default=func.now())
Expand All @@ -55,6 +56,10 @@ class User(Base):
creator=lambda r: RoundJuror(round=r))
# update_date?

def __init__(self, **kw):
self.flags = kw.pop('flags', {})
super(User, self).__init__(**kw)


class Campaign(Base):
__tablename__ = 'campaigns'
Expand Down Expand Up @@ -166,7 +171,6 @@ class Entry(Base):
upload_date = Column(DateTime)

# TODO: img_sha1/page_touched for updates?

create_date = Column(DateTime, server_default=func.now())

entered_rounds = relationship('RoundEntry')
Expand All @@ -181,6 +185,9 @@ class RoundEntry(Base):
entry_id = Column(Integer, ForeignKey('entries.id'))
round_id = Column(Integer, ForeignKey('rounds.id'))

dq_reason = Column(String) # in case it's disqualified
# examples: too low resolution, out of date range

entry = relationship(Entry, back_populates='entered_rounds')
round = relationship(Round, back_populates='round_entries')

Expand Down
2 changes: 1 addition & 1 deletion montage/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def get_juror_round(user_dao, round_id):

def create_app(env_name='prod'):
# render functions have been removed, as this is now managed by
# the ResponesDictMiddleware
# the MessageMiddleware
routes = [('/', home),
('/admin', get_admin_landing),
GET('/admin/campaign', get_admin_landing),
Expand Down
58 changes: 52 additions & 6 deletions montage/simple_serdes.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@

import json
import datetime
from json import JSONEncoder

from sqlalchemy import inspect
from sqlalchemy.types import TypeDecorator, String
from sqlalchemy.ext.mutable import Mutable


from sqlalchemy.orm.state import InstanceState


class EntityJSONEncoder(JSONEncoder):
class EntityJSONEncoder(json.JSONEncoder):
""" JSON encoder for custom classes:
Uses __json__() method if available to prepare the object.
Especially useful for SQLAlchemy models
"""
def __init__(self, *a, **kw):
self.eager = kw.pop(eager, False)
self.eager = kw.pop('eager', False)
super(EntityJSONEncoder, self).__init__(*a, **kw)

def default(self, o):
Expand All @@ -26,13 +30,13 @@ def default(self, o):
def get_entity_propnames(entity):
""" Get entity property names
:param entity: Entity
:param entity: Entity
:type entity: sqlalchemy.ext.declarative.api.DeclarativeMeta
:returns: Set of entity property names
:rtype: set
"""
ins = entity if isinstance(entity, InstanceState) else inspect(entity)
return set(ins.mapper.column_attrs.keys() + ins.mapper.relationships.keys())
e = entity if isinstance(entity, InstanceState) else inspect(entity)
return set(e.mapper.column_attrs.keys() + e.mapper.relationships.keys())


def get_entity_loaded_propnames(entity):
Expand Down Expand Up @@ -94,3 +98,45 @@ def __repr__(self):

cn = self.__class__.__name__
return '<%s %s>' % (cn, ' '.join(parts))


class JSONEncodedDict(TypeDecorator):
impl = String

def process_bind_param(self, value, dialect):
if value is None:
value = {}
return json.dumps(value)

def process_result_value(self, value, dialect):
if value is None:
value = '{}'
return json.loads(value)


class MutableDict(Mutable, dict):
@classmethod
def coerce(cls, key, value):
"Convert plain dictionaries to MutableDict."

if not isinstance(value, MutableDict):
if isinstance(value, dict):
return MutableDict(value)

# this call will raise ValueError
return Mutable.coerce(key, value)
else:
return value

def __setitem__(self, key, value):
"Detect dictionary set events and emit change events."
dict.__setitem__(self, key, value)
self.changed()

def __delitem__(self, key):
"Detect dictionary del events and emit change events."
dict.__delitem__(self, key)
self.changed()


MutableDict.associate_with(JSONEncodedDict)

0 comments on commit 8829a15

Please sign in to comment.