Skip to content

Commit

Permalink
Implement easier way to access business property from view model (tes…
Browse files Browse the repository at this point in the history
…t will fail)
  • Loading branch information
billyrrr committed Jan 21, 2020
1 parent 3451a8a commit 28bebed
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 137 deletions.
4 changes: 2 additions & 2 deletions examples/meeting_room/tests/test_dav.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def get_from_user_id(cls, user_id, once=False, **kwargs):
**kwargs)

def propagate_change(self):
self.user.save()
self.store.user.save()


class UserViewMediatorDAV(ViewMediatorDAV):
Expand Down Expand Up @@ -328,7 +328,7 @@ def test_propagate_change(users, tickets, location, meeting):

# time.sleep(3)

user_view.user.last_name = "M."
user_view.store.user.last_name = "M."
user_view.propagate_change()

user_ref = Context.db.collection("users").document(user_id)
Expand Down
74 changes: 27 additions & 47 deletions examples/meeting_room/view_models/meeting_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
from examples.meeting_room.domain_models.location import Location
from examples.meeting_room.domain_models.meeting import Meeting
from flask_boiler import fields, schema, view_model, view
from flask_boiler.business_property_store import BPSchema
from flask_boiler.mutation import Mutation, PatchMutation
from flask_boiler.struct import Struct
from flask_boiler.view import DocumentAsView
from flask_boiler.view_model import ViewModelMixin

Expand All @@ -18,12 +20,11 @@ class MeetingSessionSchema(schema.Schema):
num_hearing_aid_requested = fields.Raw()


class MeetingSessionBpStoreSchema:

_users = fields.BusinessPropertyFieldMany(referenced_cls=User)
_tickets = fields.BusinessPropertyFieldMany(referenced_cls=Ticket)
_meeting = fields.BusinessPropertyFieldOne(referenced_cls=Meeting)
_location = fields.BusinessPropertyFieldOne(referenced_cls=Location)
class MeetingSessionBpss(BPSchema):
tickets = fields.StructuralRef(dm_cls=Ticket, many=True)
users = fields.StructuralRef(dm_cls=User, many=True)
meeting = fields.StructuralRef(dm_cls=Meeting)
location = fields.StructuralRef(dm_cls=Location)


class MeetingSessionMixin:
Expand All @@ -35,75 +36,54 @@ def __init__(self, *args, meeting_id=None, **kwargs):
super().__init__(*args, **kwargs)
self._meeting_id = meeting_id

@property
def _users(self):
user_ids = [user_ref.id for user_ref in self._meeting.users]
return {
user_id: self.business_properties[user_id] for user_id in user_ids
}

@property
def _tickets(self):
return {
self.business_properties[ticket_ref.id].user.id:
self.business_properties[ticket_ref.id]
for ticket_ref in self._meeting.tickets
ticket.user.id: ticket
for _, ticket in self.store.tickets.items()
}

@property
def _meeting(self):
"""
TODO: fix evaluation order in source code (add priority flag to some
TODO: view models to be instantiated first)
:return:
"""
return self.business_properties[self._meeting_id]

@property
def meeting_id(self):
return self._meeting.doc_id

@property
def _location(self):
return self.business_properties[self._meeting.location.id]
return self.store.meeting.doc_id

@property
def in_session(self):
return self._meeting.status == "in-session"
return self.store.meeting.status == "in-session"

@in_session.setter
def in_session(self, in_session):
cur_status = self._meeting.status
cur_status = self.store.meeting.status
if cur_status == "in-session" and not in_session:
self._meeting.status = "closed"
self.store.meeting.status = "closed"
elif cur_status == "closed" and in_session:
self._meeting.status = "in-session"
self.store.meeting.status = "in-session"
else:
raise ValueError

@property
def latitude(self):
return self._location.latitude
return self.store.location.latitude

@property
def longitude(self):
return self._location.longitude
return self.store.location.longitude

@property
def address(self):
return self._location.address
return self.store.location.address

@property
def attending(self):
user_ids = [uid for uid in self._users.keys()]
user_ids = [uid for uid in self.store.users.keys()]

if self._meeting.status == "not-started":
if self.store.meeting.status == "not-started":
return list()

res = list()
for user_id in sorted(user_ids):
ticket = self._tickets[user_id]
user = self._users[user_id]
user = self.store.users[user_id]
if ticket.attendance:
d = {
"name": user.display_name,
Expand Down Expand Up @@ -140,33 +120,33 @@ def new(cls, doc_id=None):

@classmethod
def get_from_meeting_id(cls, meeting_id, once=False, **kwargs):
struct = dict()
struct = Struct(schema_obj=MeetingSessionBpss())

m: Meeting = Meeting.get(doc_id=meeting_id)

struct[m.doc_id] = (Meeting, m.doc_ref.id)
struct["meeting"] = (Meeting, m.doc_ref.id)

for user_ref in m.users:
obj_type = User
doc_id = user_ref.id
struct[doc_id] = (obj_type, user_ref.id)
struct["users"][doc_id] = (obj_type, user_ref.id)

for ticket_ref in m.tickets:
obj_type = Ticket
doc_id = ticket_ref.id
struct["tickets"][doc_id] = (obj_type, ticket_ref.id)

struct[doc_id] = (obj_type, ticket_ref.id)

struct[m.location.id] = (Location, m.location.id)
struct["location"] = (Location, m.location.id)

obj = cls.get(struct_d=struct, once=once,
meeting_id=m.doc_ref.id,
**kwargs)
# time.sleep(2) # TODO: delete after implementing sync

return obj

def propagate_change(self):
self._meeting.save()
self.store.propagate_back()


class MeetingSession(MeetingSessionMixin, view.FlaskAsView):
Expand Down
31 changes: 14 additions & 17 deletions examples/meeting_room/view_models/user_view.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from examples.meeting_room.domain_models.user import User
from examples.meeting_room.domain_models.meeting import Meeting
from flask_boiler import fields, schema, view_model, view
from flask_boiler.business_property_store import BPSchema
from flask_boiler.struct import Struct


class UserViewSchema(schema.Schema):
Expand All @@ -13,58 +15,53 @@ class UserViewSchema(schema.Schema):
meetings = fields.Relationship(many=True, dump_only=True)


class UserBpss(BPSchema):
user = fields.StructuralRef(dm_cls=User)


class UserViewMixin:

class Meta:
schema_cls = UserViewSchema

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user = None

@classmethod
def new(cls, doc_id=None):
return cls.get_from_user_id(user_id=doc_id)

def set_user(self, user):
self.user = user

@property
def first_name(self):
return self.user.first_name
return self.store.user.first_name

@property
def last_name(self):
return self.user.last_name
return self.store.user.last_name

@last_name.setter
def last_name(self, new_last_name):
self.user.last_name = new_last_name
self.store.user.last_name = new_last_name

@property
def organization(self):
return self.user.organization
return self.store.user.organization

@property
def hearing_aid_requested(self):
return self.user.hearing_aid_requested
return self.store.user.hearing_aid_requested

@property
def meetings(self):
return list(Meeting.where(users=("array_contains", self.user.doc_ref)))

def get_vm_update_callback(self, dm_cls):
def user_update_func(vm: UserView, dm):
vm.set_user(dm)
return user_update_func
return list(Meeting.where(users=("array_contains", self.store.user.doc_ref)))

@classmethod
def get_from_user_id(cls, user_id, once=False, **kwargs):
struct = dict()
struct = Struct(schema_obj=UserBpss())

u: User = User.get(doc_id=user_id)

struct[u.doc_id] = (User, u.doc_ref.id)
struct["user"] = (User, u.doc_ref.id)

return super().get(struct_d=struct, once=once, **kwargs)

Expand Down
29 changes: 20 additions & 9 deletions flask_boiler/business_property_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,41 @@ def structural_ref_fields(self):
return [fd for _, fd in self.fields.items() if isinstance(fd, StructuralRef)]


class BusinessPropertyStore(Schemed):
class BusinessPropertyStore:

def __init__(self, struct):
def __init__(self, struct, snapshot_container, schema_obj):
super().__init__()

self._container = SnapshotContainer()
self._container = snapshot_container
self.struct = struct
self.schema_obj = struct.schema_obj
self._g, self._gr, self._manifest = \
self._get_manifests(self.struct, self.schema_obj)
self.objs = dict()

@property
def bprefs(self):
return self._manifest.copy()

def refresh(self):
for doc_ref in self._manifest:
self.objs[doc_ref] = snapshot_to_obj(self._container.get(doc_ref))

def __getattr__(self, item):

if item not in self._g:
raise AttributeError

if isinstance(self._g[item], dict):
return {
k: snapshot_to_obj(self._container.get(v))
k: self.objs[v]
for k, v in self._g[item].items()
}
else:
return snapshot_to_obj(self._container.get(self._g[item]))
return self.objs[self._g[item]]

def propagate_back(self):
for _, obj in self.objs.items():
obj.save()

@staticmethod
def _get_manifests(struct, schema_obj) -> Tuple:
Expand All @@ -62,18 +74,17 @@ def _get_manifests(struct, schema_obj) -> Tuple:
key = fd.attribute
val = struct[key]

dm_cls = fd.dm_cls
if fd.many:
g[key] = dict()
for k, v in val.items():
if "." in k:
raise ValueError
doc_ref = to_ref(dm_cls, v)
doc_ref = to_ref(*v)
g[key][k] = doc_ref
gr[doc_ref].append("{}.{}".format(key, k))
manifest.add(doc_ref)
else:
doc_ref = to_ref(dm_cls, val)
doc_ref = to_ref(*val)
g[key] = doc_ref
gr[doc_ref].append(key)
manifest.add(doc_ref)
Expand Down
18 changes: 0 additions & 18 deletions flask_boiler/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,24 +250,6 @@ class Remainder(fields.Dict, Field):
pass


class BusinessPropertyFieldBase(fields.Raw, Field):
pass


class BusinessPropertyFieldMany(BusinessPropertyFieldBase):

@property
def default_value(self):
return set()


class BusinessPropertyFieldOne(BusinessPropertyFieldBase):

@property
def default_value(self):
return None


# class BpStoreField(fields.Raw, Field):
#
# def __init__(self, *args, **kwargs):
Expand Down
30 changes: 30 additions & 0 deletions flask_boiler/struct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from collections import UserDict


class Struct(UserDict):

def __init__(self, schema_obj):
super().__init__()
self.schema_obj = schema_obj

@property
def vals(self):
for _, val in self.data.items():
if isinstance(val, dict):
for _, v in val.items():
yield v
else:
yield val

def __getitem__(self, key):
"""
Initializes a field to dict if it was not declared before
:param item:
:return:
"""

if key not in self.data.keys():
self.data[key] = dict()
return super().__getitem__(key)

0 comments on commit 28bebed

Please sign in to comment.