And more abstraction to fields and prepare for related fields

This commit is contained in:
RunasSudo 2017-12-15 19:51:03 +10:30 committed by Yingtong Li
parent 597519eccc
commit 4d9ad7226a
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
9 changed files with 116 additions and 39 deletions

View File

@ -14,7 +14,7 @@
# 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/>.
from eos.core.objects import EosObject
from eos.core.objects import *
import random
@ -125,7 +125,7 @@ class BigInt(EosObject):
def nbits(self):
return self.impl.bitLength()
def serialise(self, for_hash=False, should_protect=False):
def serialise(self, options=SerialiseOptions.DEFAULT):
return str(self)
@classmethod

View File

@ -14,7 +14,7 @@
# 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/>.
from eos.core.objects import EosObject
from eos.core.objects import *
import math
@ -46,7 +46,7 @@ class BigInt(EosObject):
def nbits(self):
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)
@classmethod

View File

@ -27,6 +27,9 @@ class DBProvider:
def get_all(self, collection):
raise Exception('Not implemented')
def get_all_by_fields(self, collection, fields):
raise Exception('Not implemented')
def get_by_id(self, collection, _id):
raise Exception('Not implemented')
@ -43,6 +46,9 @@ class DummyProvider(DBProvider):
def get_all(self, collection):
pass
def get_all_by_fields(self, collection, fields):
pass
def get_by_id(self, collection, _id):
pass

View File

@ -26,6 +26,16 @@ class MongoDBProvider(eos.core.db.DBProvider):
def get_all(self, collection):
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):
return self.db[collection].find_one(_id)

View File

@ -34,6 +34,20 @@ class PostgreSQLDBProvider(eos.core.db.DBProvider):
self.cur.execute(SQL('SELECT data FROM {}').format(Identifier(table)))
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):
self.create_table(table)
self.cur.execute(SQL('SELECT data FROM {} WHERE _id = %s').format(Identifier(table)), (_id,))

View File

@ -71,12 +71,12 @@ class SHA256:
def update_obj(self, *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
def update_obj_raw(self, *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
def hash_as_b64(self):

View File

@ -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.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
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):
def serialise(self, value, for_hash=False, should_protect=False):
def serialise(self, value, options=SerialiseOptions.DEFAULT):
return value
def deserialise(self, value):
@ -93,8 +112,8 @@ class EmbeddedObjectField(Field):
super().__init__(*args, **kwargs)
self.object_type = object_type
def serialise(self, value, for_hash=False, should_protect=False):
return EosObject.serialise_and_wrap(value, self.object_type, for_hash, should_protect)
def serialise(self, value, options=SerialiseOptions.DEFAULT):
return EosObject.serialise_and_wrap(value, self.object_type, options)
def deserialise(self, value):
return EosObject.deserialise_and_unwrap(value, self.object_type)
@ -104,8 +123,8 @@ class ListField(Field):
super().__init__(default=EosList, *args, **kwargs)
self.element_field = element_field
def serialise(self, value, for_hash=False, should_protect=False):
return [self.element_field.serialise(x, for_hash, should_protect) for x in (value.impl if isinstance(value, EosList) else value)]
def serialise(self, value, options=SerialiseOptions.DEFAULT):
return [self.element_field.serialise(x, options) for x in (value.impl if isinstance(value, EosList) else value)]
def deserialise(self, 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)
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
if value is 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):
if value is None:
return None
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:
class UUIDField(Field):
def __init__(self, *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)
def deserialise(self, value):
@ -145,7 +196,7 @@ class DateTimeField(Field):
return '0' + 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:
return None
@ -215,12 +266,12 @@ class EosObject(metaclass=EosObjectType):
return EosObject.objects[name]
@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 value:
return value.serialise(for_hash, should_protect)
return value.serialise(options)
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
def deserialise_and_unwrap(value, object_type=None):
@ -327,14 +378,9 @@ class DocumentObjectType(EosObjectType):
if is_python:
def make_property(name, field):
def field_getter(self):
return self._field_values[name]
return field.object_get(self)
def field_setter(self, value):
self._field_values[name] = value
if isinstance(value, EosObject):
value._instance = (self, name)
if not value._inited:
value.post_init()
field.object_set(self, value)
return property(field_getter, field_setter)
for attr, val in fields.items():
@ -362,15 +408,11 @@ class DocumentObject(EosObject, metaclass=DocumentObjectType):
pass
else:
def make_property(name, field):
# TNYI: Transcrypt doesn't pass self
def field_getter():
return self._field_values[name]
return field.object_get(self)
def field_setter(value):
self._field_values[name] = value
if isinstance(value, EosObject):
value._instance = (self, name)
if not value._inited:
value.post_init()
field.object_set(self, value)
return (field_getter, field_setter)
prop = make_property(val.real_name, val)
# TNYI: No support for property()
@ -393,8 +435,8 @@ class DocumentObject(EosObject, metaclass=DocumentObjectType):
default = default()
setattr(self, val.real_name, default)
def serialise(self, for_hash=False, should_protect=False):
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))}
def serialise(self, options=SerialiseOptions.DEFAULT):
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
def deserialise(cls, value):
@ -435,6 +477,10 @@ class TopLevelObject(DocumentObject, metaclass=TopLevelObjectType):
def get_all(cls):
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
def get_by_id(cls, _id):
if not isinstance(_id, str):

View File

@ -153,7 +153,7 @@ class BitStream(EosObject):
bs.seek(0)
return bs
def serialise(self):
def serialise(self, options=SerialiseOptions.DEFAULT):
return self.impl
@classmethod
@ -173,7 +173,7 @@ class InfiniteHashBitStream(BitStream):
# 11000110110
# ^----
if nbits is None:
nbits = self.remaining
raise Exception('Cannot read indefinite amount from InfiniteHashBitStream')
while nbits > self.remaining:
self.ctr += 1
self.sha.update_text(str(self.ctr))

View File

@ -214,7 +214,8 @@ def election_admin(func):
@app.route('/election/<election_id>/')
@using_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')
@using_election
@ -326,8 +327,8 @@ def election_api_cast_vote(election):
election.save()
return flask.Response(json.dumps({
'voter': EosObject.serialise_and_wrap(voter, should_protect=True),
'vote': EosObject.serialise_and_wrap(vote, should_protect=True)
'voter': EosObject.serialise_and_wrap(voter, None, SerialiseOptions(should_protect=True)),
'vote': EosObject.serialise_and_wrap(vote, None, SerialiseOptions(should_protect=True))
}), mimetype='application/json')
@app.route('/election/<election_id>/export/question/<int:q_num>/<format>')