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
|
||||
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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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__
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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]:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
rel="stylesheet"
|
||||
integrity="sha384-+ENW/yibaokMnme+vBLnHMphUYxHs34h9lpdbSLuAwGkOKFRl4C34WkjazBtb7eT"
|
||||
crossorigin="anonymous">
|
||||
<script src="https://use.fontawesome.com/7553aecc27.js"></script>
|
||||
<title>Devicehub | {{ device.__format__('t') }}</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -22,55 +23,39 @@
|
|||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="jumbotron">
|
||||
<img class="center-block"
|
||||
style="height: 13em; padding-bottom: 0.1em"
|
||||
src="{{ url_for('Device.static', filename='magrama.svg') }}">
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="page-header col-md-6 col-md-offset-3">
|
||||
<h1>{{ device.__format__('t') }}<br>
|
||||
<small>{{ device.__format__('s') }}</small>
|
||||
</h1>
|
||||
</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>
|
||||
<div class="row">
|
||||
<article class="col-md-6">
|
||||
<h3>You can verify the originality of your device.</h3>
|
||||
<p>
|
||||
If your device comes with the following tag
|
||||
<img class="img-responsive center-block" style="width: 12em;"
|
||||
src="{{ url_for('Device.static', filename='photochromic-alone.svg') }}">
|
||||
it means it has been refurbished by an eReuse.org
|
||||
certified organization.
|
||||
</p>
|
||||
<p>
|
||||
The tag is special –illuminate it with the torch of
|
||||
your phone for 6 seconds and it will react like in
|
||||
the following image:
|
||||
<img class="img-responsive center-block" style="width: 30em;"
|
||||
src="{{ url_for('Device.static', filename='photochromic-tag-web.svg') }}">
|
||||
This is proof that this device is genuine.
|
||||
</p>
|
||||
</article>
|
||||
<article class="col-md-6">
|
||||
<h3>These are the specifications</h3>
|
||||
<div class="col-md-3">
|
||||
{% if device.image %}
|
||||
<a href="{{ device.image.to_text() }}" class="thumbnail" target="_blank">
|
||||
<img src="{{ device.image.to_text() }}"
|
||||
alt="Cykel.JPG">
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% if device.trading == states.Trading.Available %}
|
||||
<div class="alert alert-success">
|
||||
<i class="fa fa-check-circle"></i> {{ device.trading }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="fa fa-exclamation-circle"></i> Not available.
|
||||
</div>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% for key, value in device.physical_properties.items() %}
|
||||
<li>{{ key }}: {{ value }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if isinstance(device, d.Computer) %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
|
@ -189,7 +174,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h3>This is the traceability log of your device</h3>
|
||||
<h4>Public traceability log of the device</h4>
|
||||
<div class="text-right">
|
||||
<small>Latest one.</small>
|
||||
</div>
|
||||
|
@ -216,9 +201,9 @@
|
|||
<div class="text-right">
|
||||
<small>Oldest one.</small>
|
||||
</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -14,6 +14,7 @@ from ereuse_devicehub.db import db
|
|||
from ereuse_devicehub.query import SearchQueryParser, things_response
|
||||
from ereuse_devicehub.resources import search
|
||||
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.search import DeviceSearch
|
||||
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
||||
|
@ -101,7 +102,7 @@ class DeviceView(View):
|
|||
|
||||
def one_public(self, id: int):
|
||||
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
|
||||
def one_private(self, id: int):
|
||||
|
|
|
@ -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',
|
||||
'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 ' \
|
||||
'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, '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))
|
||||
assert format(hdd) == 'HardDrive 7: model st9160310as, S/N 5sv4tqa6'
|
||||
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):
|
||||
s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot)
|
||||
html, _ = client.get(res=d.Device, item=s['device']['id'], accept=ANY)
|
||||
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__)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import ipaddress
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
from typing import Tuple
|
||||
from typing import Tuple, Type
|
||||
|
||||
import pytest
|
||||
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')
|
||||
computer.components.add(hdd)
|
||||
|
||||
ready = models.Available()
|
||||
ready = models.Ready()
|
||||
assert not ready.devices
|
||||
assert not ready.components
|
||||
|
||||
|
@ -214,7 +214,7 @@ def test_update_parent():
|
|||
(models.ToRepair, states.Physical.ToBeRepaired),
|
||||
(models.Repair, states.Physical.Repaired),
|
||||
(models.ToPrepare, states.Physical.Preparing),
|
||||
(models.Available, states.Physical.ReadyToBeUsed),
|
||||
(models.Ready, states.Physical.Ready),
|
||||
(models.Prepare, states.Physical.Prepared)
|
||||
]))
|
||||
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.param(ams, id=ams[0].__class__.__name__)
|
||||
(pytest.param(ams, id=ams[0].__name__)
|
||||
for ams in [
|
||||
(models.MakeAvailable, states.Trading.Available),
|
||||
(models.Sell, states.Trading.Sold),
|
||||
(models.Donate, states.Trading.Donated),
|
||||
(models.Rent, states.Trading.Renting),
|
||||
(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."""
|
||||
# todo missing None states.Trading for after cancelling renting, for example
|
||||
action_model, state = action_model_state
|
||||
snapshot, _ = user.post(file('basic.snapshot'), res=models.Snapshot)
|
||||
action = {
|
||||
'type': action_model.t,
|
||||
'devices': [snapshot['device']['id']],
|
||||
'to': user.user['individuals'][0]['id'],
|
||||
'shippingDate': '2018-06-29T12:28:54',
|
||||
'invoiceNumber': 'ABC'
|
||||
'devices': [snapshot['device']['id']]
|
||||
}
|
||||
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)
|
||||
assert action['devices'][0]['id'] == snapshot['device']['id']
|
||||
device, _ = user.get(res=Device, item=snapshot['device']['id'])
|
||||
|
|
Reference in New Issue