From ce961a1bed082cf7010bd7dd6d22fea5e15785bf Mon Sep 17 00:00:00 2001 From: Xavier Bustamante Talavera Date: Sun, 7 Jul 2019 21:36:09 +0200 Subject: [PATCH] Add MakeAvailable and Repair; add device.image; add templating file, change layout for project LOT --- docs/states.puml | 4 +- docs/states.rst | 2 +- ereuse_devicehub/devicehub.py | 2 + ereuse_devicehub/dummy/dummy.py | 4 +- ereuse_devicehub/resources/action/__init__.py | 9 +- ereuse_devicehub/resources/action/models.py | 7 +- ereuse_devicehub/resources/action/models.pyi | 6 +- ereuse_devicehub/resources/action/schemas.py | 14 +- .../resources/device/definitions.py | 53 +-- ereuse_devicehub/resources/device/models.py | 49 ++- ereuse_devicehub/resources/device/models.pyi | 2 + ereuse_devicehub/resources/device/schemas.py | 29 +- ereuse_devicehub/resources/device/states.py | 5 +- .../device/templates/devices/layout.html | 405 +++++++++--------- ereuse_devicehub/resources/device/views.py | 3 +- ereuse_devicehub/templating.py | 13 + tests/test_basic.py | 2 +- tests/test_device.py | 6 +- tests/test_event.py | 21 +- 19 files changed, 350 insertions(+), 286 deletions(-) create mode 100644 ereuse_devicehub/templating.py diff --git a/docs/states.puml b/docs/states.puml index b20e2ab6..f22416af 100644 --- a/docs/states.puml +++ b/docs/states.puml @@ -23,8 +23,8 @@ state Physical { ToBeRepaired --> Repaired : Repair Repaired -> Preparing : ToPrepare Preparing --> Prepared : Prepare - Prepared --> ReadyToBeUsed : ReadyToUse - ReadyToBeUsed --> InUse : Live + Prepared --> Ready : ReadyToUse + Ready --> InUse : Live InUse -> InUse : Live state DisposeWaste state Recover diff --git a/docs/states.rst b/docs/states.rst index 7c253e9a..840e80ba 100644 --- a/docs/states.rst +++ b/docs/states.rst @@ -44,5 +44,5 @@ Physical :cvar Repaired: The device has been repaired. :cvar Preparing: The device is going to be or being prepared. :cvar Prepared: The device has been prepared. -:cvar ReadyToBeUsed: The device is in working conditions. +:cvar Ready: The device is in working conditions. :cvar InUse: The device is being reported to be in active use. diff --git a/ereuse_devicehub/devicehub.py b/ereuse_devicehub/devicehub.py index 4499268b..834fdcc7 100644 --- a/ereuse_devicehub/devicehub.py +++ b/ereuse_devicehub/devicehub.py @@ -18,11 +18,13 @@ from ereuse_devicehub.db import db from ereuse_devicehub.dummy.dummy import Dummy from ereuse_devicehub.resources.device.search import DeviceSearch from ereuse_devicehub.resources.inventory import Inventory, InventoryDef +from ereuse_devicehub.templating import Environment class Devicehub(Teal): test_client_class = Client Dummy = Dummy + jinja_environment = Environment def __init__(self, inventory: str, diff --git a/ereuse_devicehub/dummy/dummy.py b/ereuse_devicehub/dummy/dummy.py index 829bfa6f..89d39893 100644 --- a/ereuse_devicehub/dummy/dummy.py +++ b/ereuse_devicehub/dummy/dummy.py @@ -104,7 +104,7 @@ class Dummy: # Perform generic actions for pc, model in zip(pcs, - {m.ToRepair, m.Repair, m.ToPrepare, m.Available, m.ToPrepare, + {m.ToRepair, m.Repair, m.ToPrepare, m.Ready, m.ToPrepare, m.Prepare}): user.post({'type': model.t, 'devices': [pc]}, res=m.Action) @@ -144,7 +144,7 @@ class Dummy: user.post({'type': m.ToPrepare.t, 'devices': [sample_pc]}, res=m.Action) user.post({'type': m.Prepare.t, 'devices': [sample_pc]}, res=m.Action) - user.post({'type': m.Available.t, 'devices': [sample_pc]}, res=m.Action) + user.post({'type': m.Ready.t, 'devices': [sample_pc]}, res=m.Action) user.post({'type': m.Price.t, 'device': sample_pc, 'currency': 'EUR', 'price': 85}, res=m.Action) # todo test reserve diff --git a/ereuse_devicehub/resources/action/__init__.py b/ereuse_devicehub/resources/action/__init__.py index 6b9588c9..0a940e5c 100644 --- a/ereuse_devicehub/resources/action/__init__.py +++ b/ereuse_devicehub/resources/action/__init__.py @@ -188,9 +188,9 @@ class RepairDef(ActionDef): SCHEMA = schemas.Repair -class Available(ActionDef): +class ReadyDef(ActionDef): VIEW = None - SCHEMA = schemas.Available + SCHEMA = schemas.Ready class ToPrepareDef(ActionDef): @@ -233,6 +233,11 @@ class RentDef(ActionDef): SCHEMA = schemas.Rent +class MakeAvailable(ActionDef): + VIEW = None + SCHEMA = schemas.MakeAvailable + + class CancelTradeDef(ActionDef): VIEW = None SCHEMA = schemas.CancelTrade diff --git a/ereuse_devicehub/resources/action/models.py b/ereuse_devicehub/resources/action/models.py index 0ea6817f..9937a0a8 100644 --- a/ereuse_devicehub/resources/action/models.py +++ b/ereuse_devicehub/resources/action/models.py @@ -1256,7 +1256,7 @@ class Repair(ActionWithMultipleDevices): """ -class Available(ActionWithMultipleDevices): +class Ready(ActionWithMultipleDevices): """The device is ready to be used. This involves greater preparation from the ``Prepare`` action, @@ -1420,6 +1420,11 @@ class DisposeProduct(Trade): # ``RecyclingCenter``. +class MakeAvailable(ActionWithMultipleDevices): + """The act of setting willingness for trading.""" + pass + + class Receive(JoinedTableMixin, ActionWithMultipleDevices): """The act of physically taking delivery of a device. diff --git a/ereuse_devicehub/resources/action/models.pyi b/ereuse_devicehub/resources/action/models.pyi index e18c9eb9..6d9ab5b4 100644 --- a/ereuse_devicehub/resources/action/models.pyi +++ b/ereuse_devicehub/resources/action/models.pyi @@ -434,7 +434,7 @@ class Repair(ActionWithMultipleDevices): pass -class ReadyToUse(ActionWithMultipleDevices): +class Ready(ActionWithMultipleDevices): pass @@ -505,6 +505,10 @@ class Rent(Trade): pass +class MakeAvailable(ActionWithMultipleDevices): + pass + + class CancelTrade(Trade): pass diff --git a/ereuse_devicehub/resources/action/schemas.py b/ereuse_devicehub/resources/action/schemas.py index 5daee52a..63ed2d3a 100644 --- a/ereuse_devicehub/resources/action/schemas.py +++ b/ereuse_devicehub/resources/action/schemas.py @@ -47,7 +47,11 @@ class ActionWithOneDevice(Action): class ActionWithMultipleDevices(Action): __doc__ = m.ActionWithMultipleDevices.__doc__ - devices = NestedOn(s_device.Device, many=True, only_query='id', collection_class=OrderedSet) + devices = NestedOn(s_device.Device, + many=True, + required=True, # todo test ensuring len(devices) >= 1 + only_query='id', + collection_class=OrderedSet) class Add(ActionWithOneDevice): @@ -347,8 +351,8 @@ class Repair(ActionWithMultipleDevices): __doc__ = m.Repair.__doc__ -class Available(ActionWithMultipleDevices): - __doc__ = m.Available.__doc__ +class Ready(ActionWithMultipleDevices): + __doc__ = m.Ready.__doc__ class ToPrepare(ActionWithMultipleDevices): @@ -405,6 +409,10 @@ class Rent(Trade): __doc__ = m.Rent.__doc__ +class MakeAvailable(ActionWithMultipleDevices): + __doc__ = m.MakeAvailable.__doc__ + + class CancelTrade(Trade): __doc__ = m.CancelTrade.__doc__ diff --git a/ereuse_devicehub/resources/device/definitions.py b/ereuse_devicehub/resources/device/definitions.py index 4d2dc07b..559e5428 100644 --- a/ereuse_devicehub/resources/device/definitions.py +++ b/ereuse_devicehub/resources/device/definitions.py @@ -302,9 +302,9 @@ class Mixer(CookingDef): SCHEMA = schemas.Mixer -class DrillDef(DeviceDef): +class DIYAndGardeningDef(DeviceDef): VIEW = None - SCHEMA = schemas.Drill + SCHEMA = schemas.DIYAndGardening def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, @@ -313,7 +313,12 @@ class DrillDef(DeviceDef): url_prefix, subdomain, url_defaults, root_path, cli_commands) -class PackOfScrewdriversDef(DeviceDef): +class DrillDef(DIYAndGardeningDef): + VIEW = None + SCHEMA = schemas.Drill + + +class PackOfScrewdriversDef(DIYAndGardeningDef): VIEW = None SCHEMA = schemas.PackOfScrewdrivers @@ -324,21 +329,31 @@ class PackOfScrewdriversDef(DeviceDef): url_prefix, subdomain, url_defaults, root_path, cli_commands) -class DehumidifierDef(DeviceDef): +class HomeDef(DeviceDef): + VIEW = None + SCHEMA = schemas.Home + + def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, + template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, + root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): + super().__init__(app, import_name, static_folder, static_url_path, template_folder, + url_prefix, subdomain, url_defaults, root_path, cli_commands) + + +class DehumidifierDef(HomeDef): VIEW = None SCHEMA = schemas.Dehumidifier - def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, - template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, - root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): - super().__init__(app, import_name, static_folder, static_url_path, template_folder, - url_prefix, subdomain, url_defaults, root_path, cli_commands) - -class StairsDef(DeviceDef): +class StairsDef(HomeDef): VIEW = None SCHEMA = schemas.Stairs + +class RecreationDef(DeviceDef): + VIEW = None + SCHEMA = schemas.Recreation + def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): @@ -346,27 +361,15 @@ class StairsDef(DeviceDef): url_prefix, subdomain, url_defaults, root_path, cli_commands) -class BikeDef(DeviceDef): +class BikeDef(RecreationDef): VIEW = None SCHEMA = schemas.Bike - def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, - template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, - root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): - super().__init__(app, import_name, static_folder, static_url_path, template_folder, - url_prefix, subdomain, url_defaults, root_path, cli_commands) - -class RacketDef(DeviceDef): +class RacketDef(RecreationDef): VIEW = None SCHEMA = schemas.Racket - def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None, - template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, - root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()): - super().__init__(app, import_name, static_folder, static_url_path, template_folder, - url_prefix, subdomain, url_defaults, root_path, cli_commands) - class ManufacturerDef(Resource): VIEW = ManufacturerView diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 51364bf8..ce5ac219 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -79,14 +79,14 @@ class Device(Thing): generation = db.Column(db.SmallInteger, check_range('generation', 0)) generation.comment = """The generation of the device.""" version = db.Column(db.CIText()) - version.comment = """The version code this device, like v1 or A001.""" - weight = Column(Float(decimal_return_scale=3), check_range('weight', 0.1, 5)) - weight.comment = """The weight of the device.""" - width = Column(Float(decimal_return_scale=3), check_range('width', 0.1, 5)) + version.comment = """The version code of this device, like v1 or A001.""" + weight = Column(Float(decimal_return_scale=4), check_range('weight', 0.1, 5)) + weight.comment = """The weight of the device in Kg.""" + width = Column(Float(decimal_return_scale=4), check_range('width', 0.1, 5)) width.comment = """The width of the device in meters.""" - height = Column(Float(decimal_return_scale=3), check_range('height', 0.1, 5)) + height = Column(Float(decimal_return_scale=4), check_range('height', 0.1, 5)) height.comment = """The height of the device in meters.""" - depth = Column(Float(decimal_return_scale=3), check_range('depth', 0.1, 5)) + depth = Column(Float(decimal_return_scale=4), check_range('depth', 0.1, 5)) depth.comment = """The depth of the device in meters.""" color = Column(ColorType) color.comment = """The predominant color of the device.""" @@ -101,6 +101,8 @@ class Device(Thing): sku.comment = """The Stock Keeping Unit (SKU), i.e. a merchant-specific identifier for a product or service. """ + image = db.Column(db.URL) + image.comment = "An image of the device." _NON_PHYSICAL_PROPS = { 'id', @@ -120,7 +122,8 @@ class Device(Thing): 'production_date', 'variant', 'version', - 'sku' + 'sku', + 'image' } __table_args__ = ( @@ -167,7 +170,7 @@ class Device(Thing): def physical_properties(self) -> Dict[str, object or None]: """Fields that describe the physical properties of a device. - :return A generator where each value is a tuple with tho fields: + :return A dictionary: - Column. - Actual value of the column or None. """ @@ -291,9 +294,13 @@ class Device(Thing): if 't' in format_spec: v += '{0.t} {0.model}'.format(self) if 's' in format_spec: - v += '({0.manufacturer})'.format(self) + superclass = self.__class__.mro()[1] + if not isinstance(self, Device) and superclass != Device: + assert issubclass(superclass, Thing) + v += superclass.__name__ + ' ' + v += '{0.manufacturer}'.format(self) if self.serial_number: - v += ' S/N ' + self.serial_number.upper() + v += ' ' + self.serial_number.upper() return v @@ -787,7 +794,11 @@ class Mixer(Cooking): pass -class Drill(Device): +class DIYAndGardening(Device): + pass + + +class Drill(DIYAndGardening): max_drill_bit_size = db.Column(db.SmallInteger) @@ -795,21 +806,29 @@ class PackOfScrewdrivers(Device): pass -class Dehumidifier(Device): +class Home(Device): + pass + + +class Dehumidifier(Home): size = db.Column(db.SmallInteger) size.comment = """The capacity in Liters.""" -class Stairs(Device): +class Stairs(Home): max_allowed_weight = db.Column(db.Integer) -class Bike(Device): +class Recreation(Device): + pass + + +class Bike(Recreation): wheel_size = db.Column(db.SmallInteger) gears = db.Column(db.SmallInteger) -class Racket(Device): +class Racket(Recreation): pass diff --git a/ereuse_devicehub/resources/device/models.pyi b/ereuse_devicehub/resources/device/models.pyi index 76bae791..51a8ea53 100644 --- a/ereuse_devicehub/resources/device/models.pyi +++ b/ereuse_devicehub/resources/device/models.pyi @@ -45,6 +45,7 @@ class Device(Thing): version = ... # type: Column variant = ... # type: Column sku = ... # type: Column + image = ... #type: Column def __init__(self, **kwargs) -> None: super().__init__(**kwargs) @@ -70,6 +71,7 @@ class Device(Thing): self.version = ... # type: Optional[str] self.variant = ... # type: Optional[str] self.sku = ... # type: Optional[str] + self.image = ... # type: Optional[urlutils.URL] @property def actions(self) -> List[e.Action]: diff --git a/ereuse_devicehub/resources/device/schemas.py b/ereuse_devicehub/resources/device/schemas.py index d2b5b4d1..e5ce1c77 100644 --- a/ereuse_devicehub/resources/device/schemas.py +++ b/ereuse_devicehub/resources/device/schemas.py @@ -60,8 +60,9 @@ class Device(Thing): many=True, dump_only=True, description=m.Device.working.__doc__) - variant = SanitizedStr(description=m.Device.variant) - sku = SanitizedStr(description=m.Device.sku) + variant = SanitizedStr(description=m.Device.variant.comment) + sku = SanitizedStr(description=m.Device.sku.comment) + image = URL(description=m.Device.image.comment) @pre_load def from_actions_to_actions_one(self, data: dict): @@ -413,26 +414,38 @@ class Mixer(Cooking): __doc__ = m.Mixer.__doc__ -class Drill(Device): +class DIYAndGardening(Device): + pass + + +class Drill(DIYAndGardening): max_drill_bit_size = Integer(data_key='maxDrillBitSize') -class PackOfScrewdrivers(Device): +class PackOfScrewdrivers(DIYAndGardening): size = Integer() -class Dehumidifier(Device): +class Home(Device): + pass + + +class Dehumidifier(Home): size = Integer() -class Stairs(Device): +class Stairs(Home): max_allowed_weight = Integer(data_key='maxAllowedWeight') -class Bike(Device): +class Recreation(Device): + pass + + +class Bike(Recreation): wheel_size = Integer(data_key='wheelSize') gears = Integer() -class Racket(Device): +class Racket(Recreation): pass diff --git a/ereuse_devicehub/resources/device/states.py b/ereuse_devicehub/resources/device/states.py index daaff20a..b0d0d439 100644 --- a/ereuse_devicehub/resources/device/states.py +++ b/ereuse_devicehub/resources/device/states.py @@ -40,6 +40,7 @@ class Trading(State): # todo add Pay = e.Pay ToBeDisposed = e.ToDisposeProduct ProductDisposed = e.DisposeProduct + Available = e.MakeAvailable class Physical(State): @@ -49,12 +50,12 @@ class Physical(State): :cvar Repaired: The device has been repaired. :cvar Preparing: The device is going to be or being prepared. :cvar Prepared: The device has been prepared. - :cvar ReadyToBeUsed: The device is in working conditions. + :cvar Ready: The device is in working conditions. :cvar InUse: The device is being reported to be in active use. """ ToBeRepaired = e.ToRepair Repaired = e.Repair Preparing = e.ToPrepare Prepared = e.Prepare - ReadyToBeUsed = e.Available + Ready = e.Ready InUse = e.Live diff --git a/ereuse_devicehub/resources/device/templates/devices/layout.html b/ereuse_devicehub/resources/device/templates/devices/layout.html index afd62ae9..9b3a5f93 100644 --- a/ereuse_devicehub/resources/device/templates/devices/layout.html +++ b/ereuse_devicehub/resources/device/templates/devices/layout.html @@ -2,223 +2,208 @@ - - - - Devicehub | {{ device.__format__('t') }} + + + + + Devicehub | {{ device.__format__('t') }} -
- -
-
-