Add MakeAvailable and Repair; add device.image; add templating file, change layout for project LOT
This commit is contained in:
parent
f8ec8fc882
commit
ce961a1bed
|
@ -23,8 +23,8 @@ state Physical {
|
||||||
ToBeRepaired --> Repaired : Repair
|
ToBeRepaired --> Repaired : Repair
|
||||||
Repaired -> Preparing : ToPrepare
|
Repaired -> Preparing : ToPrepare
|
||||||
Preparing --> Prepared : Prepare
|
Preparing --> Prepared : Prepare
|
||||||
Prepared --> ReadyToBeUsed : ReadyToUse
|
Prepared --> Ready : ReadyToUse
|
||||||
ReadyToBeUsed --> InUse : Live
|
Ready --> InUse : Live
|
||||||
InUse -> InUse : Live
|
InUse -> InUse : Live
|
||||||
state DisposeWaste
|
state DisposeWaste
|
||||||
state Recover
|
state Recover
|
||||||
|
|
|
@ -44,5 +44,5 @@ Physical
|
||||||
:cvar Repaired: The device has been repaired.
|
:cvar Repaired: The device has been repaired.
|
||||||
:cvar Preparing: The device is going to be or being prepared.
|
:cvar Preparing: The device is going to be or being prepared.
|
||||||
:cvar Prepared: The device has been 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.
|
:cvar InUse: The device is being reported to be in active use.
|
||||||
|
|
|
@ -18,11 +18,13 @@ from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.dummy.dummy import Dummy
|
from ereuse_devicehub.dummy.dummy import Dummy
|
||||||
from ereuse_devicehub.resources.device.search import DeviceSearch
|
from ereuse_devicehub.resources.device.search import DeviceSearch
|
||||||
from ereuse_devicehub.resources.inventory import Inventory, InventoryDef
|
from ereuse_devicehub.resources.inventory import Inventory, InventoryDef
|
||||||
|
from ereuse_devicehub.templating import Environment
|
||||||
|
|
||||||
|
|
||||||
class Devicehub(Teal):
|
class Devicehub(Teal):
|
||||||
test_client_class = Client
|
test_client_class = Client
|
||||||
Dummy = Dummy
|
Dummy = Dummy
|
||||||
|
jinja_environment = Environment
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
inventory: str,
|
inventory: str,
|
||||||
|
|
|
@ -104,7 +104,7 @@ class Dummy:
|
||||||
|
|
||||||
# Perform generic actions
|
# Perform generic actions
|
||||||
for pc, model in zip(pcs,
|
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}):
|
m.Prepare}):
|
||||||
user.post({'type': model.t, 'devices': [pc]}, res=m.Action)
|
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.ToPrepare.t, 'devices': [sample_pc]}, res=m.Action)
|
||||||
user.post({'type': m.Prepare.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},
|
user.post({'type': m.Price.t, 'device': sample_pc, 'currency': 'EUR', 'price': 85},
|
||||||
res=m.Action)
|
res=m.Action)
|
||||||
# todo test reserve
|
# todo test reserve
|
||||||
|
|
|
@ -188,9 +188,9 @@ class RepairDef(ActionDef):
|
||||||
SCHEMA = schemas.Repair
|
SCHEMA = schemas.Repair
|
||||||
|
|
||||||
|
|
||||||
class Available(ActionDef):
|
class ReadyDef(ActionDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.Available
|
SCHEMA = schemas.Ready
|
||||||
|
|
||||||
|
|
||||||
class ToPrepareDef(ActionDef):
|
class ToPrepareDef(ActionDef):
|
||||||
|
@ -233,6 +233,11 @@ class RentDef(ActionDef):
|
||||||
SCHEMA = schemas.Rent
|
SCHEMA = schemas.Rent
|
||||||
|
|
||||||
|
|
||||||
|
class MakeAvailable(ActionDef):
|
||||||
|
VIEW = None
|
||||||
|
SCHEMA = schemas.MakeAvailable
|
||||||
|
|
||||||
|
|
||||||
class CancelTradeDef(ActionDef):
|
class CancelTradeDef(ActionDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.CancelTrade
|
SCHEMA = schemas.CancelTrade
|
||||||
|
|
|
@ -1256,7 +1256,7 @@ class Repair(ActionWithMultipleDevices):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Available(ActionWithMultipleDevices):
|
class Ready(ActionWithMultipleDevices):
|
||||||
"""The device is ready to be used.
|
"""The device is ready to be used.
|
||||||
|
|
||||||
This involves greater preparation from the ``Prepare`` action,
|
This involves greater preparation from the ``Prepare`` action,
|
||||||
|
@ -1420,6 +1420,11 @@ class DisposeProduct(Trade):
|
||||||
# ``RecyclingCenter``.
|
# ``RecyclingCenter``.
|
||||||
|
|
||||||
|
|
||||||
|
class MakeAvailable(ActionWithMultipleDevices):
|
||||||
|
"""The act of setting willingness for trading."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Receive(JoinedTableMixin, ActionWithMultipleDevices):
|
class Receive(JoinedTableMixin, ActionWithMultipleDevices):
|
||||||
"""The act of physically taking delivery of a device.
|
"""The act of physically taking delivery of a device.
|
||||||
|
|
||||||
|
|
|
@ -434,7 +434,7 @@ class Repair(ActionWithMultipleDevices):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ReadyToUse(ActionWithMultipleDevices):
|
class Ready(ActionWithMultipleDevices):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -505,6 +505,10 @@ class Rent(Trade):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MakeAvailable(ActionWithMultipleDevices):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CancelTrade(Trade):
|
class CancelTrade(Trade):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,11 @@ class ActionWithOneDevice(Action):
|
||||||
|
|
||||||
class ActionWithMultipleDevices(Action):
|
class ActionWithMultipleDevices(Action):
|
||||||
__doc__ = m.ActionWithMultipleDevices.__doc__
|
__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):
|
class Add(ActionWithOneDevice):
|
||||||
|
@ -347,8 +351,8 @@ class Repair(ActionWithMultipleDevices):
|
||||||
__doc__ = m.Repair.__doc__
|
__doc__ = m.Repair.__doc__
|
||||||
|
|
||||||
|
|
||||||
class Available(ActionWithMultipleDevices):
|
class Ready(ActionWithMultipleDevices):
|
||||||
__doc__ = m.Available.__doc__
|
__doc__ = m.Ready.__doc__
|
||||||
|
|
||||||
|
|
||||||
class ToPrepare(ActionWithMultipleDevices):
|
class ToPrepare(ActionWithMultipleDevices):
|
||||||
|
@ -405,6 +409,10 @@ class Rent(Trade):
|
||||||
__doc__ = m.Rent.__doc__
|
__doc__ = m.Rent.__doc__
|
||||||
|
|
||||||
|
|
||||||
|
class MakeAvailable(ActionWithMultipleDevices):
|
||||||
|
__doc__ = m.MakeAvailable.__doc__
|
||||||
|
|
||||||
|
|
||||||
class CancelTrade(Trade):
|
class CancelTrade(Trade):
|
||||||
__doc__ = m.CancelTrade.__doc__
|
__doc__ = m.CancelTrade.__doc__
|
||||||
|
|
||||||
|
|
|
@ -302,9 +302,9 @@ class Mixer(CookingDef):
|
||||||
SCHEMA = schemas.Mixer
|
SCHEMA = schemas.Mixer
|
||||||
|
|
||||||
|
|
||||||
class DrillDef(DeviceDef):
|
class DIYAndGardeningDef(DeviceDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.Drill
|
SCHEMA = schemas.DIYAndGardening
|
||||||
|
|
||||||
def __init__(self, app, import_name=__name__, static_folder=None, static_url_path=None,
|
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,
|
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)
|
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
|
VIEW = None
|
||||||
SCHEMA = schemas.PackOfScrewdrivers
|
SCHEMA = schemas.PackOfScrewdrivers
|
||||||
|
|
||||||
|
@ -324,21 +329,31 @@ class PackOfScrewdriversDef(DeviceDef):
|
||||||
url_prefix, subdomain, url_defaults, root_path, cli_commands)
|
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
|
VIEW = None
|
||||||
SCHEMA = schemas.Dehumidifier
|
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(HomeDef):
|
||||||
class StairsDef(DeviceDef):
|
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.Stairs
|
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,
|
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,
|
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
|
||||||
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
|
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)
|
url_prefix, subdomain, url_defaults, root_path, cli_commands)
|
||||||
|
|
||||||
|
|
||||||
class BikeDef(DeviceDef):
|
class BikeDef(RecreationDef):
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.Bike
|
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(RecreationDef):
|
||||||
class RacketDef(DeviceDef):
|
|
||||||
VIEW = None
|
VIEW = None
|
||||||
SCHEMA = schemas.Racket
|
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):
|
class ManufacturerDef(Resource):
|
||||||
VIEW = ManufacturerView
|
VIEW = ManufacturerView
|
||||||
|
|
|
@ -79,14 +79,14 @@ class Device(Thing):
|
||||||
generation = db.Column(db.SmallInteger, check_range('generation', 0))
|
generation = db.Column(db.SmallInteger, check_range('generation', 0))
|
||||||
generation.comment = """The generation of the device."""
|
generation.comment = """The generation of the device."""
|
||||||
version = db.Column(db.CIText())
|
version = db.Column(db.CIText())
|
||||||
version.comment = """The version code this device, like v1 or A001."""
|
version.comment = """The version code of this device, like v1 or A001."""
|
||||||
weight = Column(Float(decimal_return_scale=3), check_range('weight', 0.1, 5))
|
weight = Column(Float(decimal_return_scale=4), check_range('weight', 0.1, 5))
|
||||||
weight.comment = """The weight of the device."""
|
weight.comment = """The weight of the device in Kg."""
|
||||||
width = Column(Float(decimal_return_scale=3), check_range('width', 0.1, 5))
|
width = Column(Float(decimal_return_scale=4), check_range('width', 0.1, 5))
|
||||||
width.comment = """The width of the device in meters."""
|
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."""
|
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."""
|
depth.comment = """The depth of the device in meters."""
|
||||||
color = Column(ColorType)
|
color = Column(ColorType)
|
||||||
color.comment = """The predominant color of the device."""
|
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
|
sku.comment = """The Stock Keeping Unit (SKU), i.e. a
|
||||||
merchant-specific identifier for a product or service.
|
merchant-specific identifier for a product or service.
|
||||||
"""
|
"""
|
||||||
|
image = db.Column(db.URL)
|
||||||
|
image.comment = "An image of the device."
|
||||||
|
|
||||||
_NON_PHYSICAL_PROPS = {
|
_NON_PHYSICAL_PROPS = {
|
||||||
'id',
|
'id',
|
||||||
|
@ -120,7 +122,8 @@ class Device(Thing):
|
||||||
'production_date',
|
'production_date',
|
||||||
'variant',
|
'variant',
|
||||||
'version',
|
'version',
|
||||||
'sku'
|
'sku',
|
||||||
|
'image'
|
||||||
}
|
}
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
|
@ -167,7 +170,7 @@ class Device(Thing):
|
||||||
def physical_properties(self) -> Dict[str, object or None]:
|
def physical_properties(self) -> Dict[str, object or None]:
|
||||||
"""Fields that describe the physical properties of a device.
|
"""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.
|
- Column.
|
||||||
- Actual value of the column or None.
|
- Actual value of the column or None.
|
||||||
"""
|
"""
|
||||||
|
@ -291,9 +294,13 @@ class Device(Thing):
|
||||||
if 't' in format_spec:
|
if 't' in format_spec:
|
||||||
v += '{0.t} {0.model}'.format(self)
|
v += '{0.t} {0.model}'.format(self)
|
||||||
if 's' in format_spec:
|
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:
|
if self.serial_number:
|
||||||
v += ' S/N ' + self.serial_number.upper()
|
v += ' ' + self.serial_number.upper()
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
@ -787,7 +794,11 @@ class Mixer(Cooking):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Drill(Device):
|
class DIYAndGardening(Device):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Drill(DIYAndGardening):
|
||||||
max_drill_bit_size = db.Column(db.SmallInteger)
|
max_drill_bit_size = db.Column(db.SmallInteger)
|
||||||
|
|
||||||
|
|
||||||
|
@ -795,21 +806,29 @@ class PackOfScrewdrivers(Device):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Dehumidifier(Device):
|
class Home(Device):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Dehumidifier(Home):
|
||||||
size = db.Column(db.SmallInteger)
|
size = db.Column(db.SmallInteger)
|
||||||
size.comment = """The capacity in Liters."""
|
size.comment = """The capacity in Liters."""
|
||||||
|
|
||||||
|
|
||||||
class Stairs(Device):
|
class Stairs(Home):
|
||||||
max_allowed_weight = db.Column(db.Integer)
|
max_allowed_weight = db.Column(db.Integer)
|
||||||
|
|
||||||
|
|
||||||
class Bike(Device):
|
class Recreation(Device):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Bike(Recreation):
|
||||||
wheel_size = db.Column(db.SmallInteger)
|
wheel_size = db.Column(db.SmallInteger)
|
||||||
gears = db.Column(db.SmallInteger)
|
gears = db.Column(db.SmallInteger)
|
||||||
|
|
||||||
|
|
||||||
class Racket(Device):
|
class Racket(Recreation):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ class Device(Thing):
|
||||||
version = ... # type: Column
|
version = ... # type: Column
|
||||||
variant = ... # type: Column
|
variant = ... # type: Column
|
||||||
sku = ... # type: Column
|
sku = ... # type: Column
|
||||||
|
image = ... #type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
@ -70,6 +71,7 @@ class Device(Thing):
|
||||||
self.version = ... # type: Optional[str]
|
self.version = ... # type: Optional[str]
|
||||||
self.variant = ... # type: Optional[str]
|
self.variant = ... # type: Optional[str]
|
||||||
self.sku = ... # type: Optional[str]
|
self.sku = ... # type: Optional[str]
|
||||||
|
self.image = ... # type: Optional[urlutils.URL]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def actions(self) -> List[e.Action]:
|
def actions(self) -> List[e.Action]:
|
||||||
|
|
|
@ -60,8 +60,9 @@ class Device(Thing):
|
||||||
many=True,
|
many=True,
|
||||||
dump_only=True,
|
dump_only=True,
|
||||||
description=m.Device.working.__doc__)
|
description=m.Device.working.__doc__)
|
||||||
variant = SanitizedStr(description=m.Device.variant)
|
variant = SanitizedStr(description=m.Device.variant.comment)
|
||||||
sku = SanitizedStr(description=m.Device.sku)
|
sku = SanitizedStr(description=m.Device.sku.comment)
|
||||||
|
image = URL(description=m.Device.image.comment)
|
||||||
|
|
||||||
@pre_load
|
@pre_load
|
||||||
def from_actions_to_actions_one(self, data: dict):
|
def from_actions_to_actions_one(self, data: dict):
|
||||||
|
@ -413,26 +414,38 @@ class Mixer(Cooking):
|
||||||
__doc__ = m.Mixer.__doc__
|
__doc__ = m.Mixer.__doc__
|
||||||
|
|
||||||
|
|
||||||
class Drill(Device):
|
class DIYAndGardening(Device):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Drill(DIYAndGardening):
|
||||||
max_drill_bit_size = Integer(data_key='maxDrillBitSize')
|
max_drill_bit_size = Integer(data_key='maxDrillBitSize')
|
||||||
|
|
||||||
|
|
||||||
class PackOfScrewdrivers(Device):
|
class PackOfScrewdrivers(DIYAndGardening):
|
||||||
size = Integer()
|
size = Integer()
|
||||||
|
|
||||||
|
|
||||||
class Dehumidifier(Device):
|
class Home(Device):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Dehumidifier(Home):
|
||||||
size = Integer()
|
size = Integer()
|
||||||
|
|
||||||
|
|
||||||
class Stairs(Device):
|
class Stairs(Home):
|
||||||
max_allowed_weight = Integer(data_key='maxAllowedWeight')
|
max_allowed_weight = Integer(data_key='maxAllowedWeight')
|
||||||
|
|
||||||
|
|
||||||
class Bike(Device):
|
class Recreation(Device):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Bike(Recreation):
|
||||||
wheel_size = Integer(data_key='wheelSize')
|
wheel_size = Integer(data_key='wheelSize')
|
||||||
gears = Integer()
|
gears = Integer()
|
||||||
|
|
||||||
|
|
||||||
class Racket(Device):
|
class Racket(Recreation):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -40,6 +40,7 @@ class Trading(State):
|
||||||
# todo add Pay = e.Pay
|
# todo add Pay = e.Pay
|
||||||
ToBeDisposed = e.ToDisposeProduct
|
ToBeDisposed = e.ToDisposeProduct
|
||||||
ProductDisposed = e.DisposeProduct
|
ProductDisposed = e.DisposeProduct
|
||||||
|
Available = e.MakeAvailable
|
||||||
|
|
||||||
|
|
||||||
class Physical(State):
|
class Physical(State):
|
||||||
|
@ -49,12 +50,12 @@ class Physical(State):
|
||||||
:cvar Repaired: The device has been repaired.
|
:cvar Repaired: The device has been repaired.
|
||||||
:cvar Preparing: The device is going to be or being prepared.
|
:cvar Preparing: The device is going to be or being prepared.
|
||||||
:cvar Prepared: The device has been 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.
|
:cvar InUse: The device is being reported to be in active use.
|
||||||
"""
|
"""
|
||||||
ToBeRepaired = e.ToRepair
|
ToBeRepaired = e.ToRepair
|
||||||
Repaired = e.Repair
|
Repaired = e.Repair
|
||||||
Preparing = e.ToPrepare
|
Preparing = e.ToPrepare
|
||||||
Prepared = e.Prepare
|
Prepared = e.Prepare
|
||||||
ReadyToBeUsed = e.Available
|
Ready = e.Ready
|
||||||
InUse = e.Live
|
InUse = e.Live
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
integrity="sha384-+ENW/yibaokMnme+vBLnHMphUYxHs34h9lpdbSLuAwGkOKFRl4C34WkjazBtb7eT"
|
integrity="sha384-+ENW/yibaokMnme+vBLnHMphUYxHs34h9lpdbSLuAwGkOKFRl4C34WkjazBtb7eT"
|
||||||
crossorigin="anonymous">
|
crossorigin="anonymous">
|
||||||
|
<script src="https://use.fontawesome.com/7553aecc27.js"></script>
|
||||||
<title>Devicehub | {{ device.__format__('t') }}</title>
|
<title>Devicehub | {{ device.__format__('t') }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -22,55 +23,39 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="jumbotron">
|
<div class="container-fluid">
|
||||||
<img class="center-block"
|
<div class="row">
|
||||||
style="height: 13em; padding-bottom: 0.1em"
|
<div class="page-header col-md-6 col-md-offset-3">
|
||||||
src="{{ url_for('Device.static', filename='magrama.svg') }}">
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div class="page-header">
|
|
||||||
<h1>{{ device.__format__('t') }}<br>
|
<h1>{{ device.__format__('t') }}<br>
|
||||||
<small>{{ device.__format__('s') }}</small>
|
<small>{{ device.__format__('s') }}</small>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
|
||||||
<h2 class='text-center'>
|
|
||||||
This is your {{ device.t }}.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p class="text-center">
|
|
||||||
{% if device.trading %}
|
|
||||||
{{ device.trading }}
|
|
||||||
{% endif %}
|
|
||||||
{% if device.trading and device.physical %}
|
|
||||||
and
|
|
||||||
{% endif %}
|
|
||||||
{% if device.physical %}
|
|
||||||
{{ device.physical }}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<article class="col-md-6">
|
<div class="col-md-3">
|
||||||
<h3>You can verify the originality of your device.</h3>
|
{% if device.image %}
|
||||||
<p>
|
<a href="{{ device.image.to_text() }}" class="thumbnail" target="_blank">
|
||||||
If your device comes with the following tag
|
<img src="{{ device.image.to_text() }}"
|
||||||
<img class="img-responsive center-block" style="width: 12em;"
|
alt="Cykel.JPG">
|
||||||
src="{{ url_for('Device.static', filename='photochromic-alone.svg') }}">
|
</a>
|
||||||
it means it has been refurbished by an eReuse.org
|
{% endif %}
|
||||||
certified organization.
|
</div>
|
||||||
</p>
|
<div class="col-md-6">
|
||||||
<p>
|
{% if device.trading == states.Trading.Available %}
|
||||||
The tag is special –illuminate it with the torch of
|
<div class="alert alert-success">
|
||||||
your phone for 6 seconds and it will react like in
|
<i class="fa fa-check-circle"></i> {{ device.trading }}
|
||||||
the following image:
|
</div>
|
||||||
<img class="img-responsive center-block" style="width: 30em;"
|
{% else %}
|
||||||
src="{{ url_for('Device.static', filename='photochromic-tag-web.svg') }}">
|
<div class="alert alert-warning">
|
||||||
This is proof that this device is genuine.
|
<i class="fa fa-exclamation-circle"></i> Not available.
|
||||||
</p>
|
</div>
|
||||||
</article>
|
{% endif %}
|
||||||
<article class="col-md-6">
|
<ul>
|
||||||
<h3>These are the specifications</h3>
|
{% for key, value in device.physical_properties.items() %}
|
||||||
|
<li>{{ key }}: {{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% if isinstance(device, d.Computer) %}
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -189,7 +174,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<h3>This is the traceability log of your device</h3>
|
<h4>Public traceability log of the device</h4>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<small>Latest one.</small>
|
<small>Latest one.</small>
|
||||||
</div>
|
</div>
|
||||||
|
@ -216,9 +201,9 @@
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<small>Oldest one.</small>
|
<small>Oldest one.</small>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -14,6 +14,7 @@ from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.query import SearchQueryParser, things_response
|
from ereuse_devicehub.query import SearchQueryParser, things_response
|
||||||
from ereuse_devicehub.resources import search
|
from ereuse_devicehub.resources import search
|
||||||
from ereuse_devicehub.resources.action import models as actions
|
from ereuse_devicehub.resources.action import models as actions
|
||||||
|
from ereuse_devicehub.resources.device import states
|
||||||
from ereuse_devicehub.resources.device.models import Device, Manufacturer
|
from ereuse_devicehub.resources.device.models import Device, Manufacturer
|
||||||
from ereuse_devicehub.resources.device.search import DeviceSearch
|
from ereuse_devicehub.resources.device.search import DeviceSearch
|
||||||
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
||||||
|
@ -101,7 +102,7 @@ class DeviceView(View):
|
||||||
|
|
||||||
def one_public(self, id: int):
|
def one_public(self, id: int):
|
||||||
device = Device.query.filter_by(id=id).one()
|
device = Device.query.filter_by(id=id).one()
|
||||||
return render_template('devices/layout.html', device=device)
|
return render_template('devices/layout.html', device=device, states=states)
|
||||||
|
|
||||||
@auth.Auth.requires_auth
|
@auth.Auth.requires_auth
|
||||||
def one_private(self, id: int):
|
def one_private(self, id: int):
|
||||||
|
|
13
ereuse_devicehub/templating.py
Normal file
13
ereuse_devicehub/templating.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import flask.templating
|
||||||
|
|
||||||
|
import ereuse_devicehub.resources.device.models
|
||||||
|
|
||||||
|
|
||||||
|
class Environment(flask.templating.Environment):
|
||||||
|
"""As flask's environment but with some globals set"""
|
||||||
|
|
||||||
|
def __init__(self, app, **options):
|
||||||
|
super().__init__(app, **options)
|
||||||
|
self.globals[isinstance.__name__] = isinstance
|
||||||
|
self.globals[issubclass.__name__] = issubclass
|
||||||
|
self.globals['d'] = ereuse_devicehub.resources.device.models
|
|
@ -42,4 +42,4 @@ def test_api_docs(client: Client):
|
||||||
'scheme': 'basic',
|
'scheme': 'basic',
|
||||||
'name': 'Authorization'
|
'name': 'Authorization'
|
||||||
}
|
}
|
||||||
assert len(docs['definitions']) == 110
|
assert len(docs['definitions']) == 114
|
||||||
|
|
|
@ -468,18 +468,18 @@ def test_device_properties_format(app: Devicehub, user: UserClient):
|
||||||
assert format(net) == 'NetworkAdapter 2: model ar8121/ar8113/ar8114 ' \
|
assert format(net) == 'NetworkAdapter 2: model ar8121/ar8113/ar8114 ' \
|
||||||
'gigabit or fast ethernet, S/N 00:24:8c:7f:cf:2d'
|
'gigabit or fast ethernet, S/N 00:24:8c:7f:cf:2d'
|
||||||
assert format(net, 't') == 'NetworkAdapter ar8121/ar8113/ar8114 gigabit or fast ethernet'
|
assert format(net, 't') == 'NetworkAdapter ar8121/ar8113/ar8114 gigabit or fast ethernet'
|
||||||
assert format(net, 's') == '(qualcomm atheros) S/N 00:24:8C:7F:CF:2D – 100 Mbps'
|
assert format(net, 's') == 'qualcomm atheros 00:24:8C:7F:CF:2D – 100 Mbps'
|
||||||
hdd = next(c for c in pc.components if isinstance(c, d.DataStorage))
|
hdd = next(c for c in pc.components if isinstance(c, d.DataStorage))
|
||||||
assert format(hdd) == 'HardDrive 7: model st9160310as, S/N 5sv4tqa6'
|
assert format(hdd) == 'HardDrive 7: model st9160310as, S/N 5sv4tqa6'
|
||||||
assert format(hdd, 't') == 'HardDrive st9160310as'
|
assert format(hdd, 't') == 'HardDrive st9160310as'
|
||||||
assert format(hdd, 's') == '(seagate) S/N 5SV4TQA6 – 152 GB'
|
assert format(hdd, 's') == 'seagate 5SV4TQA6 – 152 GB'
|
||||||
|
|
||||||
|
|
||||||
def test_device_public(user: UserClient, client: Client):
|
def test_device_public(user: UserClient, client: Client):
|
||||||
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
||||||
html, _ = client.get(res=d.Device, item=s['device']['id'], accept=ANY)
|
html, _ = client.get(res=d.Device, item=s['device']['id'], accept=ANY)
|
||||||
assert 'intel atom cpu n270 @ 1.60ghz' in html
|
assert 'intel atom cpu n270 @ 1.60ghz' in html
|
||||||
assert 'S/N 00:24:8C:7F:CF:2D – 100 Mbps' in html
|
assert '00:24:8C:7F:CF:2D – 100 Mbps' in html
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import ipaddress
|
import ipaddress
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Tuple
|
from typing import Tuple, Type
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from flask import current_app as app, g
|
from flask import current_app as app, g
|
||||||
|
@ -168,7 +168,7 @@ def test_update_components_action_multiple():
|
||||||
hdd = HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar')
|
hdd = HardDrive(serial_number='foo', manufacturer='bar', model='foo-bar')
|
||||||
computer.components.add(hdd)
|
computer.components.add(hdd)
|
||||||
|
|
||||||
ready = models.Available()
|
ready = models.Ready()
|
||||||
assert not ready.devices
|
assert not ready.devices
|
||||||
assert not ready.components
|
assert not ready.components
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ def test_update_parent():
|
||||||
(models.ToRepair, states.Physical.ToBeRepaired),
|
(models.ToRepair, states.Physical.ToBeRepaired),
|
||||||
(models.Repair, states.Physical.Repaired),
|
(models.Repair, states.Physical.Repaired),
|
||||||
(models.ToPrepare, states.Physical.Preparing),
|
(models.ToPrepare, states.Physical.Preparing),
|
||||||
(models.Available, states.Physical.ReadyToBeUsed),
|
(models.Ready, states.Physical.Ready),
|
||||||
(models.Prepare, states.Physical.Prepared)
|
(models.Prepare, states.Physical.Prepared)
|
||||||
]))
|
]))
|
||||||
def test_generic_action(action_model_state: Tuple[models.Action, states.Trading],
|
def test_generic_action(action_model_state: Tuple[models.Action, states.Trading],
|
||||||
|
@ -268,24 +268,27 @@ def test_reserve_and_cancel(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('action_model_state',
|
@pytest.mark.parametrize('action_model_state',
|
||||||
(pytest.param(ams, id=ams[0].__class__.__name__)
|
(pytest.param(ams, id=ams[0].__name__)
|
||||||
for ams in [
|
for ams in [
|
||||||
|
(models.MakeAvailable, states.Trading.Available),
|
||||||
(models.Sell, states.Trading.Sold),
|
(models.Sell, states.Trading.Sold),
|
||||||
(models.Donate, states.Trading.Donated),
|
(models.Donate, states.Trading.Donated),
|
||||||
(models.Rent, states.Trading.Renting),
|
(models.Rent, states.Trading.Renting),
|
||||||
(models.DisposeProduct, states.Trading.ProductDisposed)
|
(models.DisposeProduct, states.Trading.ProductDisposed)
|
||||||
]))
|
]))
|
||||||
def test_trade(action_model_state: Tuple[models.Action, states.Trading], user: UserClient):
|
def test_trade(action_model_state: Tuple[Type[models.Action], states.Trading], user: UserClient):
|
||||||
"""Tests POSTing all Trade actions."""
|
"""Tests POSTing all Trade actions."""
|
||||||
|
# todo missing None states.Trading for after cancelling renting, for example
|
||||||
action_model, state = action_model_state
|
action_model, state = action_model_state
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
|
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
|
||||||
action = {
|
action = {
|
||||||
'type': action_model.t,
|
'type': action_model.t,
|
||||||
'devices': [snapshot['device']['id']],
|
'devices': [snapshot['device']['id']]
|
||||||
'to': user.user['individuals'][0]['id'],
|
|
||||||
'shippingDate': '2018-06-29T12:28:54',
|
|
||||||
'invoiceNumber': 'ABC'
|
|
||||||
}
|
}
|
||||||
|
if issubclass(action_model, models.Trade):
|
||||||
|
action['to'] = user.user['individuals'][0]['id']
|
||||||
|
action['shippingDate'] = '2018-06-29T12:28:54'
|
||||||
|
action['invoiceNumber'] = 'ABC'
|
||||||
action, _ = user.post(action, res=models.Action)
|
action, _ = user.post(action, res=models.Action)
|
||||||
assert action['devices'][0]['id'] == snapshot['device']['id']
|
assert action['devices'][0]['id'] == snapshot['device']['id']
|
||||||
device, _ = user.get(res=Device, item=snapshot['device']['id'])
|
device, _ = user.get(res=Device, item=snapshot['device']['id'])
|
||||||
|
|
Reference in a new issue