And more abstraction to fields and prepare for related fields
This commit is contained in:
parent
597519eccc
commit
4d9ad7226a
@ -14,7 +14,7 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from eos.core.objects import EosObject
|
from eos.core.objects import *
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ class BigInt(EosObject):
|
|||||||
def nbits(self):
|
def nbits(self):
|
||||||
return self.impl.bitLength()
|
return self.impl.bitLength()
|
||||||
|
|
||||||
def serialise(self, for_hash=False, should_protect=False):
|
def serialise(self, options=SerialiseOptions.DEFAULT):
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from eos.core.objects import EosObject
|
from eos.core.objects import *
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class BigInt(EosObject):
|
|||||||
def nbits(self):
|
def nbits(self):
|
||||||
return math.ceil(math.log2(self.impl)) if self.impl > 0 else 0
|
return math.ceil(math.log2(self.impl)) if self.impl > 0 else 0
|
||||||
|
|
||||||
def serialise(self, for_hash=False, should_protect=False):
|
def serialise(self, options=SerialiseOptions.DEFAULT):
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -27,6 +27,9 @@ class DBProvider:
|
|||||||
def get_all(self, collection):
|
def get_all(self, collection):
|
||||||
raise Exception('Not implemented')
|
raise Exception('Not implemented')
|
||||||
|
|
||||||
|
def get_all_by_fields(self, collection, fields):
|
||||||
|
raise Exception('Not implemented')
|
||||||
|
|
||||||
def get_by_id(self, collection, _id):
|
def get_by_id(self, collection, _id):
|
||||||
raise Exception('Not implemented')
|
raise Exception('Not implemented')
|
||||||
|
|
||||||
@ -43,6 +46,9 @@ class DummyProvider(DBProvider):
|
|||||||
def get_all(self, collection):
|
def get_all(self, collection):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_all_by_fields(self, collection, fields):
|
||||||
|
pass
|
||||||
|
|
||||||
def get_by_id(self, collection, _id):
|
def get_by_id(self, collection, _id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -26,6 +26,16 @@ class MongoDBProvider(eos.core.db.DBProvider):
|
|||||||
def get_all(self, collection):
|
def get_all(self, collection):
|
||||||
return self.db[collection].find()
|
return self.db[collection].find()
|
||||||
|
|
||||||
|
def get_all_by_fields(self, collection, fields):
|
||||||
|
query = {}
|
||||||
|
if '_id' in fields:
|
||||||
|
query['_id'] = fields.pop('_id')
|
||||||
|
if 'type' in fields:
|
||||||
|
query['type'] = fields.pop('type')
|
||||||
|
for field in fields:
|
||||||
|
query['value.' + field] = fields.pop(field)
|
||||||
|
return self.db[collection].find(query)
|
||||||
|
|
||||||
def get_by_id(self, collection, _id):
|
def get_by_id(self, collection, _id):
|
||||||
return self.db[collection].find_one(_id)
|
return self.db[collection].find_one(_id)
|
||||||
|
|
||||||
|
@ -34,6 +34,20 @@ class PostgreSQLDBProvider(eos.core.db.DBProvider):
|
|||||||
self.cur.execute(SQL('SELECT data FROM {}').format(Identifier(table)))
|
self.cur.execute(SQL('SELECT data FROM {}').format(Identifier(table)))
|
||||||
return [x[0] for x in self.cur.fetchall()]
|
return [x[0] for x in self.cur.fetchall()]
|
||||||
|
|
||||||
|
def get_all_by_fields(self, table, fields):
|
||||||
|
# TODO: Make this much better
|
||||||
|
result = []
|
||||||
|
for val in self.get_all(table):
|
||||||
|
if '_id' in fields and val['_id'] != fields.pop('_id'):
|
||||||
|
continue
|
||||||
|
if 'type' in fields and val['type'] != fields.pop('type'):
|
||||||
|
continue
|
||||||
|
for field in fields:
|
||||||
|
if val['value'][field] != fields[field]:
|
||||||
|
continue
|
||||||
|
result.append(val)
|
||||||
|
return result
|
||||||
|
|
||||||
def get_by_id(self, table, _id):
|
def get_by_id(self, table, _id):
|
||||||
self.create_table(table)
|
self.create_table(table)
|
||||||
self.cur.execute(SQL('SELECT data FROM {} WHERE _id = %s').format(Identifier(table)), (_id,))
|
self.cur.execute(SQL('SELECT data FROM {} WHERE _id = %s').format(Identifier(table)), (_id,))
|
||||||
|
@ -71,12 +71,12 @@ class SHA256:
|
|||||||
|
|
||||||
def update_obj(self, *values):
|
def update_obj(self, *values):
|
||||||
for value in values:
|
for value in values:
|
||||||
self.update_text(EosObject.to_json(EosObject.serialise_and_wrap(value, None, True)))
|
self.update_text(EosObject.to_json(EosObject.serialise_and_wrap(value, None, SerialiseOptions(for_hash=True))))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def update_obj_raw(self, *values):
|
def update_obj_raw(self, *values):
|
||||||
for value in values:
|
for value in values:
|
||||||
self.update_text(EosObject.to_json(EosObject.serialise_and_wrap(value, None, False)))
|
self.update_text(EosObject.to_json(EosObject.serialise_and_wrap(value, None, SerialiseOptions(for_hash=False))))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def hash_as_b64(self):
|
def hash_as_b64(self):
|
||||||
|
@ -74,9 +74,28 @@ class Field:
|
|||||||
self.default = kwargs['default'] if 'default' in kwargs else kwargs['py_default'] if 'py_default' in kwargs else None
|
self.default = kwargs['default'] if 'default' in kwargs else kwargs['py_default'] if 'py_default' in kwargs else None
|
||||||
self.is_protected = kwargs['is_protected'] if 'is_protected' in kwargs else False
|
self.is_protected = kwargs['is_protected'] if 'is_protected' in kwargs else False
|
||||||
self.is_hashed = kwargs['is_hashed'] if 'is_hashed' in kwargs else not self.is_protected
|
self.is_hashed = kwargs['is_hashed'] if 'is_hashed' in kwargs else not self.is_protected
|
||||||
|
|
||||||
|
def object_get(self, obj):
|
||||||
|
return obj._field_values[self.real_name]
|
||||||
|
|
||||||
|
def object_set(self, obj, value):
|
||||||
|
obj._field_values[self.real_name] = value
|
||||||
|
|
||||||
|
if isinstance(value, EosObject):
|
||||||
|
value._instance = (obj, self.real_name)
|
||||||
|
if not value._inited:
|
||||||
|
value.post_init()
|
||||||
|
|
||||||
|
class SerialiseOptions:
|
||||||
|
def __init__(self, for_hash=False, should_protect=False, combine_related=False):
|
||||||
|
self.for_hash = for_hash
|
||||||
|
self.should_protect = should_protect
|
||||||
|
self.combine_related = combine_related
|
||||||
|
|
||||||
|
SerialiseOptions.DEFAULT = SerialiseOptions()
|
||||||
|
|
||||||
class PrimitiveField(Field):
|
class PrimitiveField(Field):
|
||||||
def serialise(self, value, for_hash=False, should_protect=False):
|
def serialise(self, value, options=SerialiseOptions.DEFAULT):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def deserialise(self, value):
|
def deserialise(self, value):
|
||||||
@ -93,8 +112,8 @@ class EmbeddedObjectField(Field):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.object_type = object_type
|
self.object_type = object_type
|
||||||
|
|
||||||
def serialise(self, value, for_hash=False, should_protect=False):
|
def serialise(self, value, options=SerialiseOptions.DEFAULT):
|
||||||
return EosObject.serialise_and_wrap(value, self.object_type, for_hash, should_protect)
|
return EosObject.serialise_and_wrap(value, self.object_type, options)
|
||||||
|
|
||||||
def deserialise(self, value):
|
def deserialise(self, value):
|
||||||
return EosObject.deserialise_and_unwrap(value, self.object_type)
|
return EosObject.deserialise_and_unwrap(value, self.object_type)
|
||||||
@ -104,8 +123,8 @@ class ListField(Field):
|
|||||||
super().__init__(default=EosList, *args, **kwargs)
|
super().__init__(default=EosList, *args, **kwargs)
|
||||||
self.element_field = element_field
|
self.element_field = element_field
|
||||||
|
|
||||||
def serialise(self, value, for_hash=False, should_protect=False):
|
def serialise(self, value, options=SerialiseOptions.DEFAULT):
|
||||||
return [self.element_field.serialise(x, for_hash, should_protect) for x in (value.impl if isinstance(value, EosList) else value)]
|
return [self.element_field.serialise(x, options) for x in (value.impl if isinstance(value, EosList) else value)]
|
||||||
|
|
||||||
def deserialise(self, value):
|
def deserialise(self, value):
|
||||||
return EosList([self.element_field.deserialise(x) for x in value])
|
return EosList([self.element_field.deserialise(x) for x in value])
|
||||||
@ -115,23 +134,55 @@ class EmbeddedObjectListField(Field):
|
|||||||
super().__init__(default=EosList, *args, **kwargs)
|
super().__init__(default=EosList, *args, **kwargs)
|
||||||
self.object_type = object_type
|
self.object_type = object_type
|
||||||
|
|
||||||
def serialise(self, value, for_hash=False, should_protect=False):
|
def serialise(self, value, options=SerialiseOptions.DEFAULT):
|
||||||
# TNYI: Doesn't know how to deal with iterators like EosList
|
# TNYI: Doesn't know how to deal with iterators like EosList
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
return [EosObject.serialise_and_wrap(x, self.object_type, for_hash, should_protect) for x in (value.impl if isinstance(value, EosList) else value)]
|
return [EosObject.serialise_and_wrap(x, self.object_type, options) for x in (value.impl if isinstance(value, EosList) else value)]
|
||||||
|
|
||||||
def deserialise(self, value):
|
def deserialise(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
return EosList([EosObject.deserialise_and_unwrap(x, self.object_type) for x in value])
|
return EosList([EosObject.deserialise_and_unwrap(x, self.object_type) for x in value])
|
||||||
|
|
||||||
|
class RelatedObjectListManager:
|
||||||
|
def __init__(self, field, obj):
|
||||||
|
self.field = field
|
||||||
|
self.obj = obj
|
||||||
|
|
||||||
|
def get_all(self):
|
||||||
|
query = {self.field.related_field: getattr(self.obj, self.field.this_field)}
|
||||||
|
return self.field.object_type.get_all_by_fields(**query)
|
||||||
|
|
||||||
|
class RelatedObjectListField(Field):
|
||||||
|
def __init__(self, object_type=None, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.object_type = object_type
|
||||||
|
self.this_field = args['this_field'] if 'this_field' in args else '_id'
|
||||||
|
self.related_field = args['related_field'] if 'related_field' in args else 'related_id'
|
||||||
|
|
||||||
|
def object_get(self, obj):
|
||||||
|
return RelatedObjectListManager(self, obj)
|
||||||
|
|
||||||
|
def object_set(self, obj, value):
|
||||||
|
raise Exception('Cannot directly set related field')
|
||||||
|
|
||||||
|
def serialise(self, value, options=SerialiseOptions.DEFAULT):
|
||||||
|
if not options.combine_related:
|
||||||
|
return None
|
||||||
|
return EmbeddedObjectListField(object_type=self.object_type).serialise(value.get_all(), options)
|
||||||
|
|
||||||
|
def deserialise(self, value):
|
||||||
|
if value is None:
|
||||||
|
return self.get_manager()
|
||||||
|
return EosList([EosObject.deserialise_and_unwrap(x, self.object_type) for x in value])
|
||||||
|
|
||||||
if is_python:
|
if is_python:
|
||||||
class UUIDField(Field):
|
class UUIDField(Field):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(default=uuid.uuid4, *args, **kwargs)
|
super().__init__(default=uuid.uuid4, *args, **kwargs)
|
||||||
|
|
||||||
def serialise(self, value, for_hash=False, should_protect=False):
|
def serialise(self, value, options=SerialiseOptions.DEFAULT):
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
def deserialise(self, value):
|
def deserialise(self, value):
|
||||||
@ -145,7 +196,7 @@ class DateTimeField(Field):
|
|||||||
return '0' + str(number)
|
return '0' + str(number)
|
||||||
return str(number)
|
return str(number)
|
||||||
|
|
||||||
def serialise(self, value, for_hash=False, should_protect=False):
|
def serialise(self, value, options=SerialiseOptions.DEFAULT):
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -215,12 +266,12 @@ class EosObject(metaclass=EosObjectType):
|
|||||||
return EosObject.objects[name]
|
return EosObject.objects[name]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialise_and_wrap(value, object_type=None, for_hash=False, should_protect=False):
|
def serialise_and_wrap(value, object_type=None, options=SerialiseOptions.DEFAULT):
|
||||||
if object_type:
|
if object_type:
|
||||||
if value:
|
if value:
|
||||||
return value.serialise(for_hash, should_protect)
|
return value.serialise(options)
|
||||||
return None
|
return None
|
||||||
return {'type': value._name, 'value': (value.serialise(for_hash, should_protect) if value else None)}
|
return {'type': value._name, 'value': (value.serialise(options) if value else None)}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def deserialise_and_unwrap(value, object_type=None):
|
def deserialise_and_unwrap(value, object_type=None):
|
||||||
@ -327,14 +378,9 @@ class DocumentObjectType(EosObjectType):
|
|||||||
if is_python:
|
if is_python:
|
||||||
def make_property(name, field):
|
def make_property(name, field):
|
||||||
def field_getter(self):
|
def field_getter(self):
|
||||||
return self._field_values[name]
|
return field.object_get(self)
|
||||||
def field_setter(self, value):
|
def field_setter(self, value):
|
||||||
self._field_values[name] = value
|
field.object_set(self, value)
|
||||||
|
|
||||||
if isinstance(value, EosObject):
|
|
||||||
value._instance = (self, name)
|
|
||||||
if not value._inited:
|
|
||||||
value.post_init()
|
|
||||||
return property(field_getter, field_setter)
|
return property(field_getter, field_setter)
|
||||||
|
|
||||||
for attr, val in fields.items():
|
for attr, val in fields.items():
|
||||||
@ -362,15 +408,11 @@ class DocumentObject(EosObject, metaclass=DocumentObjectType):
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
def make_property(name, field):
|
def make_property(name, field):
|
||||||
|
# TNYI: Transcrypt doesn't pass self
|
||||||
def field_getter():
|
def field_getter():
|
||||||
return self._field_values[name]
|
return field.object_get(self)
|
||||||
def field_setter(value):
|
def field_setter(value):
|
||||||
self._field_values[name] = value
|
field.object_set(self, value)
|
||||||
|
|
||||||
if isinstance(value, EosObject):
|
|
||||||
value._instance = (self, name)
|
|
||||||
if not value._inited:
|
|
||||||
value.post_init()
|
|
||||||
return (field_getter, field_setter)
|
return (field_getter, field_setter)
|
||||||
prop = make_property(val.real_name, val)
|
prop = make_property(val.real_name, val)
|
||||||
# TNYI: No support for property()
|
# TNYI: No support for property()
|
||||||
@ -393,8 +435,8 @@ class DocumentObject(EosObject, metaclass=DocumentObjectType):
|
|||||||
default = default()
|
default = default()
|
||||||
setattr(self, val.real_name, default)
|
setattr(self, val.real_name, default)
|
||||||
|
|
||||||
def serialise(self, for_hash=False, should_protect=False):
|
def serialise(self, options=SerialiseOptions.DEFAULT):
|
||||||
return {val.real_name: val.serialise(getattr(self, val.real_name), for_hash, should_protect) for attr, val in self._fields.items() if ((val.is_hashed or not for_hash) and (not should_protect or not val.is_protected))}
|
return {val.real_name: val.serialise(getattr(self, val.real_name), options) for attr, val in self._fields.items() if ((val.is_hashed or not options.for_hash) and (not options.should_protect or not val.is_protected))}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def deserialise(cls, value):
|
def deserialise(cls, value):
|
||||||
@ -435,6 +477,10 @@ class TopLevelObject(DocumentObject, metaclass=TopLevelObjectType):
|
|||||||
def get_all(cls):
|
def get_all(cls):
|
||||||
return [EosObject.deserialise_and_unwrap(x) for x in dbinfo.provider.get_all(cls._db_name)]
|
return [EosObject.deserialise_and_unwrap(x) for x in dbinfo.provider.get_all(cls._db_name)]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all_by_fields(cls, **fields):
|
||||||
|
return [EosObject.deserialise_and_unwrap(x) for x in dbinfo.provider.get_all_by_fields(cls._db_name, fields)]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_id(cls, _id):
|
def get_by_id(cls, _id):
|
||||||
if not isinstance(_id, str):
|
if not isinstance(_id, str):
|
||||||
|
@ -153,7 +153,7 @@ class BitStream(EosObject):
|
|||||||
bs.seek(0)
|
bs.seek(0)
|
||||||
return bs
|
return bs
|
||||||
|
|
||||||
def serialise(self):
|
def serialise(self, options=SerialiseOptions.DEFAULT):
|
||||||
return self.impl
|
return self.impl
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -173,7 +173,7 @@ class InfiniteHashBitStream(BitStream):
|
|||||||
# 11000110110
|
# 11000110110
|
||||||
# ^----
|
# ^----
|
||||||
if nbits is None:
|
if nbits is None:
|
||||||
nbits = self.remaining
|
raise Exception('Cannot read indefinite amount from InfiniteHashBitStream')
|
||||||
while nbits > self.remaining:
|
while nbits > self.remaining:
|
||||||
self.ctr += 1
|
self.ctr += 1
|
||||||
self.sha.update_text(str(self.ctr))
|
self.sha.update_text(str(self.ctr))
|
||||||
|
@ -214,7 +214,8 @@ def election_admin(func):
|
|||||||
@app.route('/election/<election_id>/')
|
@app.route('/election/<election_id>/')
|
||||||
@using_election
|
@using_election
|
||||||
def election_api_json(election):
|
def election_api_json(election):
|
||||||
return flask.Response(EosObject.to_json(EosObject.serialise_and_wrap(election, should_protect=True, for_hash=('full' not in flask.request.args))), mimetype='application/json')
|
is_full = 'full' in flask.request.args
|
||||||
|
return flask.Response(EosObject.to_json(EosObject.serialise_and_wrap(election, None, SerialiseOptions(should_protect=True, for_hash=(not is_full), combine_related=True))), mimetype='application/json')
|
||||||
|
|
||||||
@app.route('/election/<election_id>/view')
|
@app.route('/election/<election_id>/view')
|
||||||
@using_election
|
@using_election
|
||||||
@ -326,8 +327,8 @@ def election_api_cast_vote(election):
|
|||||||
election.save()
|
election.save()
|
||||||
|
|
||||||
return flask.Response(json.dumps({
|
return flask.Response(json.dumps({
|
||||||
'voter': EosObject.serialise_and_wrap(voter, should_protect=True),
|
'voter': EosObject.serialise_and_wrap(voter, None, SerialiseOptions(should_protect=True)),
|
||||||
'vote': EosObject.serialise_and_wrap(vote, should_protect=True)
|
'vote': EosObject.serialise_and_wrap(vote, None, SerialiseOptions(should_protect=True))
|
||||||
}), mimetype='application/json')
|
}), mimetype='application/json')
|
||||||
|
|
||||||
@app.route('/election/<election_id>/export/question/<int:q_num>/<format>')
|
@app.route('/election/<election_id>/export/question/<int:q_num>/<format>')
|
||||||
|
Loading…
Reference in New Issue
Block a user