Add docstrings to event models; remove redundant generic test

This commit is contained in:
Xavier Bustamante Talavera 2018-11-12 18:15:24 +01:00
parent 0ec06808ce
commit 560e0ed8dc
7 changed files with 385 additions and 304 deletions

View file

@ -38,43 +38,27 @@ to the `Swagger docs
Physical Actions
****************
The following actions describe and react on the physical condition
The following actions describe and react on the
:class:`ereuse_devicehub.resources.device.states.Physical` condition
of the devices.
ToPrepare, Prepare
==================
Work has been performed to the device to a defined point of
acceptance. Users using this event have to agree what is this point
of acceptance; for some is when the device just works, for others
when some testing has been performed.
**Prepare** dictates that the device has been prepared, whereas
**ToPrepare** that the device has been selected to be prepared.
Usually **ToPrepare** is the next event done after registering the
device.
.. autoclass:: ereuse_devicehub.resources.event.models.Prepare
.. autoclass:: ereuse_devicehub.resources.event.models.ToPrepare
ToRepair, Repair
================
ToRepair is the act of selecting a device to be repaired, and
Repair the act of performing the actual reparations. If a repair
without an error is performed, it represents that the reparation
has been successful.
.. autoclass:: ereuse_devicehub.resources.event.models.Repair
.. autoclass:: ereuse_devicehub.resources.event.models.ToRepair
ReadyToUse
==========
The device is ready to be used. This involves greater preparation
from the ``Prepare`` event, and users should only use a device
after this event is performed.
Users usually require devices with this event before shipping them
to costumers.
.. autoclass:: ereuse_devicehub.resources.event.models.ReadyToUse
Live
====
A keep-alive from a device connected to the Internet with information
about its state (in the form of a ``Snapshot`` event) and usage
statistics.
.. autoclass:: ereuse_devicehub.resources.event.models.Live
DisposeWaste, Recover
=====================
@ -86,6 +70,8 @@ DisposeWaste, Recover
See `ToDisposeProduct, DisposeProduct`_.
.. todo:: Events not developed yet.
Association actions
*******************
Actions that change the associations users have with devices;
@ -99,43 +85,29 @@ and **organize** actions.
Trade actions
=============
Trade actions log the political exchange of devices between users,
stating **owner** xor **usufructuaree**. Every time a trade event
is performed, the old user looses its political possession in favor
of another one.
Not fully developed.
.. autoclass:: ereuse_devicehub.resources.event.models.Trade
Sell
----
The act of taking money from a buyer in exchange of a device.
.. autoclass:: ereuse_devicehub.resources.event.models.Sell
Donate
------
The act of giving devices without compensation.
.. autoclass:: ereuse_devicehub.resources.event.models.Donate
Rent
----
The act of giving money in return for temporary use, but not
ownership, of a device.
.. autoclass:: ereuse_devicehub.resources.event.models.Rent
CancelTrade
-----------
The act of cancelling a `Sell`_, `Donate`_ or `Rent`_.
.. autoclass:: ereuse_devicehub.resources.event.models.CancelTrade
ToDisposeProduct, DisposeProduct
-------------------------
``ToDispose`` and ``DisposeProduct`` manage the process of getting
rid of devices by giving (selling, donating) to another organization
like a waste manager.
``ToDispose`` marks a device for being disposed, and
``DisposeProduct`` dictates that the device has been disposed.
See `DisposeWaste, Recover`_ events for disposing without trading
the device.
.. note:: For usability purposes, users might not directly perform
``Dispose``, but this could automatically be done when
performing ``ToDispose`` + ``Receive`` to a ``RecyclingCenter``.
--------------------------------
.. autoclass:: ereuse_devicehub.resources.event.models.DisposeProduct
.. autoclass:: ereuse_devicehub.resources.event.models.ToDisposeProduct
Transfer actions
================
@ -143,34 +115,27 @@ The act of transferring/moving devices from one place to another.
Receive
-------
The act of physically taking delivery of a device. The receiver
confirms that the devices have arrived, and thus, they
**physically possess** them. Note that
there can only be one **physical possessor** per device, and
``Receive`` changes it.
The receiver can optionally take a role in the reception, giving
it meaning; an user that takes the ``FinalUser`` role in the
reception express that it will use the device, whereas a role
``Transporter`` is used by intermediaries in shipping.
.. todo:: how do we ensure users specify type of reception?
.. autoclass:: ereuse_devicehub.resources.event.models.Receive
.. autoclass:: ereuse_devicehub.resources.enums.ReceiverRole
:members:
:undoc-members:
.. autoattribute:: ereuse_devicehub.resources.device.models.Device.physical_possessor
Organize actions
================
The act of manipulating/administering/supervising/controlling one or
more devices.
.. autoclass:: ereuse_devicehub.resources.event.models.Organize
Reserve, CancelReservation
--------------------------
The act of reserving devices and cancelling them.
-------------------------
Not fully developed.
After this event is performed, the user is the **reservee** of the
devices. There can only be one non-cancelled reservation for
a device, and a reservation can only have one reservee.
.. autoclass:: ereuse_devicehub.resources.event.models.Reserve
.. autoclass:: ereuse_devicehub.resources.event.models.CancelReservation
Assign, Accept, Reject
----------------------
Not developed.
``Assign`` allocates devices to an user. The purpose or meaning
of the association is defined by the users.
@ -179,9 +144,7 @@ assignments.
.. todo:: shall we add ``Deassign`` or make ``Assign``
always define all active users?
.. todo:: Assign won't be developed until further notice.
Assign won't be developed until further notice.
Internal state actions
**********************
@ -190,254 +153,88 @@ their state.
Snapshot
========
The Snapshot sets the physical information of the device (S/N, model...)
and updates it with erasures, benchmarks, ratings, and tests; updates the
composition of its components (adding / removing them), and links tags
to the device.
.. autoclass:: ereuse_devicehub.resources.event.models.Snapshot
When receiving a Snapshot, the DeviceHub creates, adds and removes
components to match the Snapshot. For example, if a Snapshot of a computer
contains a new component, the system searches for the component in its
database and, if not found, its creates it; finally linking it to the
computer.
A Snapshot is used with Remove to represent changes in components for
a device:
1. ``Snapshot`` creates a device if it does not exist, and the same
for its components. This is all done in one ``Snapshot``.
2. If the device exists, it updates its component composition by
*adding* and *removing* them. If,
for example, this new Snasphot doesn't have a component, it means that
this component is not present anymore in the device, thus removing it
from it. Then we have that:
- Components that are added to the device: snapshot2.components -
snapshot1.components
- Components that are removed to the device: snapshot1.components -
snapshot2.components
When adding a component, there may be the case this component existed
before and it was inside another device. In such case, DeviceHub will
perform ``Remove`` on the old parent.
Snapshots from Workbench
------------------------
When processing a device from the Workbench, this one performs a Snapshot
and then performs more events (like testings, benchmarking...).
There are two ways of sending this information. In an async way,
this is, submitting events as soon as Workbench performs then, or
submitting only one Snapshot event with all the other events embedded.
Asynced
^^^^^^^
The use case, which is represented in the ``test_workbench_phases``,
is as follows:
1. In **T1**, WorkbenchServer (as the middleware from Workbench and
Devicehub) submits:
- A ``Snapshot`` event with the required information to **synchronize**
and **rate** the device. This is:
- Identification information about the device and components
(S/N, model, physical characteristics...)
- ``Tags`` in a ``tags`` property in the ``device``.
- ``Rate`` in an ``events`` property in the ``device``.
- ``Benchmarks`` in an ``events`` property in each ``component``
or ``device``.
- ``TestDataStorage`` as in ``Benchmarks``.
- An ordered set of **expected events**, defining which are the next
events that Workbench will perform to the device in ideal
conditions (device doesn't fail, no Internet drop...).
Devicehub **syncs** the device with the database and perform the
``Benchmark``, the ``TestDataStorage``, and finally the ``Rate``.
This leaves the Snapshot **open** to wait for the next events
to come.
2. Assuming that we expect all events, in **T2**, WorkbenchServer
submits a ``StressTest`` with a ``snapshot`` field containing the
ID of the Snapshot in 1, and Devicehub links the event with such
``Snapshot``.
3. In **T3**, WorkbenchServer submits the ``Erase`` with the ``Snapshot``
and ``component`` IDs from 1, linking it to them. It repeats
this for all the erased data storage devices; **T3+Tn** being
*n* the erased data storage devices.
4. WorkbenchServer does like in 3. but for the event ``Install``,
finishing in **T3+Tn+Tx**, being *x* the number of data storage
devices with an OS installed into.
5. In **T3+Tn+Tx**, when all *expected events* have been performed,
Devicehub **closes** the ``Snapshot`` from 1.
Synced
^^^^^^
Optionally, Devicehub understands receiving a ``Snapshot`` with all
the events in an ``events`` property inside each affected ``component``
or ``device``.
Add, Remove
===========
The act of adding and removing components of and from a device.
These are usually used internally from `Snapshot`_, or manually, for
example, when removing a component (like a ``DataStorage`` unit) from
a broken computer.
.. autoclass:: ereuse_devicehub.resources.event.models.Add
.. autoclass:: ereuse_devicehub.resources.event.models.Remove
EraseBasic, EraseSectors
========================
An erasure attempt to a ``DataStorage``. The event contains
information about success and nature of the erasure.
``EraseBasic`` is a fast non-secured way of erasing data storage, and
``EraseSectors`` is a slower secured, sector-by-sector, erasure
method.
Users can generate erasure certificates from successful erasures.
Erasures are an accumulation of **erasure steps**, that are performed
as separate actions, called ``StepRandom``, for an erasure step
that has overwritten data with random bits, and ``StepZero``,
for an erasure step that has overwritten data with zeros.
.. autoclass:: ereuse_devicehub.resources.event.models.EraseBasic
.. autoclass:: ereuse_devicehub.resources.event.models.EraseSectors
.. autoclass:: ereuse_devicehub.resources.event.models.ErasePhysical
Install
=======
The action of install an Operative System to a data storage unit.
.. autoclass:: ereuse_devicehub.resources.event.models.Install
Test
====
The act of testing the physical condition of a device and its
components.
.. autoclass:: ereuse_devicehub.resources.event.models.Test
TestDataStorage
---------------
The act of testing the data storage.
Testing is done using the `S.M.A.R.T self test
<https://en.wikipedia.org/wiki/S.M.A.R.T.#Self-tests>`_. Note
that not all data storage units, specially some new PCIe ones, do not
support SMART testing.
The test takes to other SMART values indicators of the overall health
of the data storage.
.. autoclass:: ereuse_devicehub.resources.event.models.TestDataStorage
StressTest
----------
The act of stressing (putting to the maximum capacity)
a device for an amount of minutes. If the device is not in great
condition won't probably survive such test.
.. autoclass:: ereuse_devicehub.resources.event.models.StressTest
Benchmark
=========
The act of gauging the performance of a device.
.. autoclass:: ereuse_devicehub.resources.event.models.Benchmark
BenchmarkDataStorage
--------------------
Benchmarks the data storage unit reading and writing speeds.
.. autoclass:: ereuse_devicehub.resources.event.models.BenchmarkDataStorage
BenchmarkWithRate
-----------------
The act of benchmarking a device with a single rate.
.. autoclass:: ereuse_devicehub.resources.event.models.BenchmarkWithRate
BenchmarkProcessor
------------------
Benchmarks a processor by executing `BogoMips
<https://en.wikipedia.org/wiki/BogoMips>`_. Note that this is not
a reliable way of rating processors and we keep it for compatibility
purposes.
.. autoclass:: ereuse_devicehub.resources.event.models.BenchmarkProcessor
BenchmarkProcessorSysbench
--------------------------
Benchmarks a processor by using the processor benchmarking utility of
`sysbench <https://github.com/akopytov/sysbench>`_.
.. autoclass:: ereuse_devicehub.resources.event.models.BenchmarkProcessorSysbench
BenchmarkRamSysbench
--------------------
.. autoclass:: ereuse_devicehub.resources.event.models.BenchmarkRamSysbench
Rate
====
Devicehub generates an rating for a device taking into consideration the
visual, functional, and performance.
A Workflow is as follows:
1. An agent generates feedback from the device in the form of benchmark,
visual, and functional information; which is filled in a ``Rate``
event. This is done through a **software**, defining the type
of ``Rate`` event. At the moment we have two rates: ``WorkbenchRate``
and ``PhotoboxRate``.
2. Devicehub gathers this information and computes a score that updates
the ``Rate`` event.
3. Devicehub aggregates different rates and computes a final score for
the device by performing a new ``AggregateRating`` event.
There are three **types** of ``Rate``: ``WorkbenchRate``,
``AppRate``, and ``PhotoboxRate``. ``WorkbenchRate`` can have different
**software** algorithms, and each software algorithm can have several
**versions**. So, we have 3 dimensions for ``WorkbenchRate``:
type, software, version.
Devicehub generates a rate event for each software and version. So,
if an agent fulfills a ``WorkbenchRate`` and there are 2 software
algorithms and each has two versions, Devicehub will generate 4 rates.
Devicehub understands that only one software and version are the
**oficial** (set in the settings of each inventory),
and it will generate an ``AggregateRating`` for only the official
versions. At the same time, ``Price`` only computes the price of
the **oficial** version.
The technical Workflow in Devicehub is as follows:
1. In **T1**, the user performs a ``Snapshot`` by processing the device
through the Workbench. From the benchmarks and the visual and
functional ratings the user does in the device, the system generates
many ``WorkbenchRate`` (as many as software and versions defined).
With only this information, the system generates an ``AggregateRating``,
which is the event that the user will see in the web.
2. In **T2**, the user takes pictures from the device through the
Photobox, and DeviceHub crates an ``ImageSet`` with multiple
``Image`` with information from the photobox.
3. In **T3**, an agent (user or AI) rates the pictures, creating a
``PhotoboxRate`` **for each** picture. When Devicehub receives the
first ``PhotoboxRate`` it creates an ``AggregateRating`` linked
to such ``PhotoboxRate``. So, the agent will perform as many
``PhotoboxRate`` as pictures are in the ``ImageSet``, and Devicehub
will link each ``PhotoboxRate`` to the same ``AggregateRating``.
This will end in **T3+Tn**, being *n* the number of photos to rate.
4. In **T3+Tn**, after the last photo is rated, Devicehub will generate
a new rate for the device: it takes the ``AggregateRating`` from 3.
and computes a rate from all the linked ``PhotoboxRate`` plus the
last available ``WorkbenchRate`` for that device.
If the agent in 3. is an user, Devicehub creates ``PhotoboxUserRate``
and if it is an AI it creates ``PhotoboxAIRate``.
The same ``ImageSet`` can be rated multiple times, generating a new
``AggregateRating`` each time.
.. autoclass:: ereuse_devicehub.resources.event.models.Rate
Price
=====
Price states a selling price for the device, but not necessariliy the
final price this was sold (which is set in the Sell event).
Devicehub automatically computes a price from ``AggregateRating``
events. As in a **Rate**, price can have **software** and **version**,
and there is an **official** price that is used to automatically
compute the price from an ``AggregateRating``. Only the official price
is computed from an ``AggregateRating``.
.. autoclass:: ereuse_devicehub.resources.event.models.Price
Migrate
=======
Moves the devices to a new database/inventory. Devices cannot be
modified anymore at the previous database.
Donation
========
.. todo:: nextcloud/eReuse/99. Tasks/224. Definir datos necesarios
configuración licencia
Not done.
.. autoclass:: ereuse_devicehub.resources.event.models.Migrate
States
******
.. todo:: work on september.
.. autoclass:: ereuse_devicehub.resources.device.states.State
.. uml:: states.puml
.. autoclass:: ereuse_devicehub.resources.device.states.Trading
:members:
:undoc-members:
.. autoclass:: ereuse_devicehub.resources.device.states.Physical
:members:
:undoc-members:

View file

@ -14,7 +14,7 @@
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# sys.path.insert(0, os.path.abspath('..'))
# -- Project information -----------------------------------------------------
@ -42,7 +42,8 @@ extensions = [
'sphinx.ext.todo',
'sphinx.ext.viewcode',
'sphinxcontrib.plantuml',
'sphinx.ext.autosectionlabel'
'sphinx.ext.autosectionlabel',
'sphinx.ext.autodoc'
]
# Add any paths that contain templates here, relative to this directory.
@ -159,7 +160,7 @@ texinfo_documents = [
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}
intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
# -- Options for todo extension ----------------------------------------------
@ -173,4 +174,5 @@ plantuml_output_format = 'svg_img'
html_favicon = 'img/favicon.ico'
# autosectionlabel
autosectionlabel_prefix_document = True
autosectionlabel_prefix_document = True
autodoc_member_order = 'bysource'

View file

@ -46,3 +46,10 @@ The result is a JSON object with the following fields:
or ``1``.
- **perPage**: How many devices are in every page, fixed to ``30``.
- **total**: How many total devices passed the filters.
Models
******
.. automodule:: ereuse_devicehub.resources.device.models
:members:
:member-order: bysource

View file

@ -178,6 +178,10 @@ class Device(Thing):
that has it physically. As an example, a transporter could
be a physical possessor of a device although it does not
own it legally.
Note that there can only be one physical possessor per device,
and :class:`ereuse_devicehub.resources.event.models.Receive`
changes it.
"""
from ereuse_devicehub.resources.event.models import Receive
with suppress(LookupError):
@ -190,7 +194,7 @@ class Device(Thing):
device is working if the list is empty.
This property returns, for the last test performed of each type,
the one with the worst severity of them, or None if no
the one with the worst severity of them, or `None` if no
test has been executed.
"""
from ereuse_devicehub.resources.event.models import Test

View file

@ -263,11 +263,19 @@ class EventDevice(db.Model):
class Add(EventWithOneDevice):
pass
"""The act of adding components to a device.
It is usually used internally from a :class:`.Snapshot`, for
example, when adding a secondary data storage to a computer.
"""
class Remove(EventWithOneDevice):
pass
"""The act of removing components from a device.
It is usually used internally from a :class:`.Snapshot`, for
example, when removing a component from a broken computer.
"""
class Allocate(JoinedTableMixin, EventWithMultipleDevices):
@ -283,6 +291,30 @@ class Deallocate(JoinedTableMixin, EventWithMultipleDevices):
class EraseBasic(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""An erasure attempt to a ``DataStorage``. The event contains
information about success and nature of the erasure.
EraseBasic is a software-based fast non-100%-secured way of
erasing data storage, performed
by Workbench Computer when executing the open-source
`shred <https://en.wikipedia.org/wiki/Shred_(Unix)>`_.
Users can generate erasure certificates from successful erasures.
Erasures are an accumulation of **erasure steps**, that are performed
as separate actions, called ``StepRandom``, for an erasure step
that has overwritten data with random bits, and ``StepZero``,
for an erasure step that has overwritten data with zeros.
For example, if steps are set in the following order and the user
used `EraseSectors`, the event represents a
`British HMG Infosec Standard 5 (HMG IS5) <https://en.wikipedia.org/
wiki/Infosec_Standard_5>`_:
1. A first step writing zeroes to the hard-drives.
2. A second step erasing with random data, verifying the erasure
success in each hard-drive sector.
"""
zeros = Column(Boolean, nullable=False)
zeros.comment = """
Whether this erasure had a first erasure step consisting of
@ -296,11 +328,14 @@ class EraseBasic(JoinedWithOneDeviceMixin, EventWithOneDevice):
class EraseSectors(EraseBasic):
pass
"""A secured-way of erasing data storages, checking sector-by-sector
the erasure, using `badblocks <https://en.wikipedia.org/wiki/Badblocks>`_.
"""
# todo make a property that says if the data wiping process is british...
class ErasePhysical(EraseBasic):
"""Physical destruction of a data storage unit."""
"""The act of physically destroying a data storage unit."""
# todo add attributes
pass
@ -346,6 +381,92 @@ class StepRandom(Step):
class Snapshot(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The Snapshot sets the physical information of the device (S/N, model...)
and updates it with erasures, benchmarks, ratings, and tests; updates the
composition of its components (adding / removing them), and links tags
to the device.
When receiving a Snapshot, the DeviceHub creates, adds and removes
components to match the Snapshot. For example, if a Snapshot of a computer
contains a new component, the system searches for the component in its
database and, if not found, its creates it; finally linking it to the
computer.
A Snapshot is used with Remove to represent changes in components for
a device:
1. ``Snapshot`` creates a device if it does not exist, and the same
for its components. This is all done in one ``Snapshot``.
2. If the device exists, it updates its component composition by
*adding* and *removing* them. If,
for example, this new Snasphot doesn't have a component, it means that
this component is not present anymore in the device, thus removing it
from it. Then we have that:
- Components that are added to the device: snapshot2.components -
snapshot1.components
- Components that are removed to the device: snapshot1.components -
snapshot2.components
When adding a component, there may be the case this component existed
before and it was inside another device. In such case, DeviceHub will
perform ``Remove`` on the old parent.
**Snapshots from Workbench**
When processing a device from the Workbench, this one performs a Snapshot
and then performs more events (like testings, benchmarking...).
There are two ways of sending this information. In an async way,
this is, submitting events as soon as Workbench performs then, or
submitting only one Snapshot event with all the other events embedded.
**Asynced**
The use case, which is represented in the ``test_workbench_phases``,
is as follows:
1. In **T1**, WorkbenchServer (as the middleware from Workbench and
Devicehub) submits:
- A ``Snapshot`` event with the required information to **synchronize**
and **rate** the device. This is:
- Identification information about the device and components
(S/N, model, physical characteristics...)
- ``Tags`` in a ``tags`` property in the ``device``.
- ``Rate`` in an ``events`` property in the ``device``.
- ``Benchmarks`` in an ``events`` property in each ``component``
or ``device``.
- ``TestDataStorage`` as in ``Benchmarks``.
- An ordered set of **expected events**, defining which are the next
events that Workbench will perform to the device in ideal
conditions (device doesn't fail, no Internet drop...).
Devicehub **syncs** the device with the database and perform the
``Benchmark``, the ``TestDataStorage``, and finally the ``Rate``.
This leaves the Snapshot **open** to wait for the next events
to come.
2. Assuming that we expect all events, in **T2**, WorkbenchServer
submits a ``StressTest`` with a ``snapshot`` field containing the
ID of the Snapshot in 1, and Devicehub links the event with such
``Snapshot``.
3. In **T3**, WorkbenchServer submits the ``Erase`` with the ``Snapshot``
and ``component`` IDs from 1, linking it to them. It repeats
this for all the erased data storage devices; **T3+Tn** being
*n* the erased data storage devices.
4. WorkbenchServer does like in 3. but for the event ``Install``,
finishing in **T3+Tn+Tx**, being *x* the number of data storage
devices with an OS installed into.
5. In **T3+Tn+Tx**, when all *expected events* have been performed,
Devicehub **closes** the ``Snapshot`` from 1.
**Synced**
Optionally, Devicehub understands receiving a ``Snapshot`` with all
the events in an ``events`` property inside each affected ``component``
or ``device``.
"""
uuid = Column(UUID(as_uuid=True), unique=True)
version = Column(StrictVersionType(STR_SM_SIZE), nullable=False)
software = Column(DBEnum(SnapshotSoftware), nullable=False)
@ -361,6 +482,9 @@ class Snapshot(JoinedWithOneDeviceMixin, EventWithOneDevice):
class Install(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The action of installing an Operative System to a data
storage unit.
"""
elapsed = Column(Interval, nullable=False)
@ -375,6 +499,48 @@ class SnapshotRequest(db.Model):
class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""Devicehub generates an rating for a device taking into consideration the
visual, functional, and performance.
A Workflow is as follows:
1. An agent generates feedback from the device in the form of benchmark,
visual, and functional information; which is filled in a ``Rate``
event. This is done through a **software**, defining the type
of ``Rate`` event. At the moment we have ``WorkbenchRate``.
2. Devicehub gathers this information and computes a score that updates
the ``Rate`` event.
3. Devicehub aggregates different rates and computes a final score for
the device by performing a new ``AggregateRating`` event.
There are two base **types** of ``Rate``: ``WorkbenchRate``,
``ManualRate``. ``WorkbenchRate`` can have different
**software** algorithms, and each software algorithm can have several
**versions**. So, we have 3 dimensions for ``WorkbenchRate``:
type, software, version.
Devicehub generates a rate event for each software and version. So,
if an agent fulfills a ``WorkbenchRate`` and there are 2 software
algorithms and each has two versions, Devicehub will generate 4 rates.
Devicehub understands that only one software and version are the
**oficial** (set in the settings of each inventory),
and it will generate an ``AggregateRating`` for only the official
versions. At the same time, ``Price`` only computes the price of
the **oficial** version.
The technical Workflow in Devicehub is as follows:
1. In **T1**, the user performs a ``Snapshot`` by processing the device
through the Workbench. From the benchmarks and the visual and
functional ratings the user does in the device, the system generates
many ``WorkbenchRate`` (as many as software and versions defined).
With only this information, the system generates an ``AggregateRating``,
which is the event that the user will see in the web.
2. In **T2**, the agent can optionally visually re-rate the device
using the mobile app, generating an ``AppRate``. This new
action generates a new ``AggregateRating`` with the ``AppRate``
plus the ``WorkbenchRate`` from 1.
"""
rating = Column(Float(decimal_return_scale=2), check_range('rating', *RATE_POSITIVE))
rating.comment = """The rating for the content."""
software = Column(DBEnum(RatingSoftware))
@ -572,6 +738,16 @@ class AggregateRate(Rate):
class Price(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""Price states a selling price for the device, but not
necessarily the final price this is sold (which is set in the Sell
event).
Devicehub automatically computes a price from ``AggregateRating``
events. As in a **Rate**, price can have **software** and **version**,
and there is an **official** price that is used to automatically
compute the price from an ``AggregateRating``. Only the official price
is computed from an ``AggregateRating``.
"""
SCALE = 4
ROUND = ROUND_HALF_EVEN
currency = Column(DBEnum(Currency), nullable=False)
@ -713,6 +889,12 @@ class EreusePrice(Price):
class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of testing the physical condition of a device and its
components.
Testing errors and warnings are easily taken in
:attr:`ereuse_devicehub.resources.device.models.Device.working`.
"""
elapsed = Column(Interval, nullable=False)
@declared_attr
@ -731,6 +913,17 @@ class Test(JoinedWithOneDeviceMixin, EventWithOneDevice):
class TestDataStorage(Test):
"""
The act of testing the data storage.
Testing is done using the `S.M.A.R.T self test
<https://en.wikipedia.org/wiki/S.M.A.R.T.#Self-tests>`_. Note
that not all data storage units, specially some new PCIe ones, do not
support SMART testing.
The test takes to other SMART values indicators of the overall health
of the data storage.
"""
id = Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True)
length = Column(DBEnum(TestDataStorageLength), nullable=False) # todo from type
status = Column(Unicode(), check_lower('status'), nullable=False)
@ -768,6 +961,10 @@ class TestDataStorage(Test):
class StressTest(Test):
"""The act of stressing (putting to the maximum capacity)
a device for an amount of minutes. If the device is not in great
condition won't probably survive such test.
"""
@validates('elapsed')
def is_minute_and_bigger_than_1_minute(self, _, value: timedelta):
@ -781,6 +978,7 @@ class StressTest(Test):
class Benchmark(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""The act of gauging the performance of a device."""
elapsed = Column(Interval)
@declared_attr
@ -799,6 +997,7 @@ class Benchmark(JoinedWithOneDeviceMixin, EventWithOneDevice):
class BenchmarkDataStorage(Benchmark):
"""Benchmarks the data storage unit reading and writing speeds."""
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
read_speed = Column(Float(decimal_return_scale=2), nullable=False)
write_speed = Column(Float(decimal_return_scale=2), nullable=False)
@ -808,6 +1007,7 @@ class BenchmarkDataStorage(Benchmark):
class BenchmarkWithRate(Benchmark):
"""The act of benchmarking a device with a single rate."""
id = Column(UUID(as_uuid=True), ForeignKey(Benchmark.id), primary_key=True)
rate = Column(Float, nullable=False)
@ -816,11 +1016,18 @@ class BenchmarkWithRate(Benchmark):
class BenchmarkProcessor(BenchmarkWithRate):
"""Benchmarks a processor by executing `BogoMips
<https://en.wikipedia.org/wiki/BogoMips>`_. Note that this is not
a reliable way of rating processors and we keep it for compatibility
purposes.
"""
pass
class BenchmarkProcessorSysbench(BenchmarkProcessor):
pass
"""Benchmarks a processor by using the processor benchmarking
utility of `sysbench <https://github.com/akopytov/sysbench>`_.
"""
class BenchmarkRamSysbench(BenchmarkWithRate):
@ -828,26 +1035,54 @@ class BenchmarkRamSysbench(BenchmarkWithRate):
class ToRepair(EventWithMultipleDevices):
pass
"""Select a device to be repaired."""
class Repair(EventWithMultipleDevices):
pass
"""Repair is the act of performing reparations.
If a repair without an error is performed,
it represents that the reparation has been successful.
"""
class ReadyToUse(EventWithMultipleDevices):
pass
"""The device is ready to be used.
This involves greater preparation from the ``Prepare`` event,
and users should only use a device after this event is performed.
Users usually require devices with this event before shipping them
to costumers.
"""
class ToPrepare(EventWithMultipleDevices):
"""The device has been selected for preparation.
See Prepare for more info.
Usually **ToPrepare** is the next event done after registering the
device.
"""
pass
class Prepare(EventWithMultipleDevices):
pass
"""Work has been performed to the device to a defined point of
acceptance.
Users using this event have to agree what is this point
of acceptance; for some is when the device just works, for others
when some testing has been performed.
"""
class Live(JoinedWithOneDeviceMixin, EventWithOneDevice):
"""A keep-alive from a device connected to the Internet with
information about its state (in the form of a ``Snapshot`` event)
and usage statistics.
"""
ip = Column(IP, nullable=False,
comment='The IP where the live was triggered.')
subdivision_confidence = Column(SmallInteger,
@ -870,18 +1105,34 @@ class Live(JoinedWithOneDeviceMixin, EventWithOneDevice):
class Organize(JoinedTableMixin, EventWithMultipleDevices):
pass
"""The act of manipulating/administering/supervising/controlling
one or more devices.
"""
class Reserve(Organize):
pass
"""The act of reserving devices and cancelling them.
After this event is performed, the user is the **reservee** of the
devices. There can only be one non-cancelled reservation for
a device, and a reservation can only have one reservee.
"""
class CancelReservation(Organize):
pass
"""The act of cancelling a reservation."""
class Trade(JoinedTableMixin, EventWithMultipleDevices):
"""Trade actions log the political exchange of devices between users.
Every time a trade event is performed, the old user looses its
political possession, for example ownership, in favor of another
user.
Performing trade events changes the *Trading* state of the
device :class:`ereuse_devicehub.resources.device.states.Trading`.
"""
shipping_date = Column(DateTime)
shipping_date.comment = """
When are the devices going to be ready for shipping?
@ -924,36 +1175,67 @@ class Trade(JoinedTableMixin, EventWithMultipleDevices):
class Sell(Trade):
pass
"""The act of taking money from a buyer in exchange of a device."""
class Donate(Trade):
pass
"""The act of giving devices without compensation."""
class Rent(Trade):
pass
"""The act of giving money in return for temporary use, but not
ownership, of a device.
"""
class CancelTrade(Trade):
pass
"""The act of cancelling a `Sell`_, `Donate`_ or `Rent`_."""
# todo cancelTrade does not do anything
class ToDisposeProduct(Trade):
pass
"""The act of setting a device for being disposed.
See :class:`.DisposeProduct`.
"""
# todo test this
class DisposeProduct(Trade):
pass
"""The act of getting rid of devices by giving (selling, donating)
to another organization, like a waste manager.
See :class:`.ToDispose` and :class:`.DisposeProduct` for
disposing without trading the device. See :class:`.DisposeWaste`
and :class:`.Recover` for disposing in-house, this is,
without trading the device.
"""
# todo For usability purposes, users might not directly perform
# *DisposeProduct*, but this could automatically be done when
# performing :class:`.ToDispose` + :class:`.Receive` to a
# ``RecyclingCenter``.
class Receive(JoinedTableMixin, EventWithMultipleDevices):
"""The act of physically taking delivery of a device.
The receiver confirms that the devices have arrived, and thus,
they are the
:attr:`ereuse_devicehub.resources.device.models.Device.physical_possessor`.
The receiver can optionally take a
:class:`ereuse_devicehub.resources.enums.ReceiverRole`.
"""
role = Column(DBEnum(ReceiverRole),
nullable=False,
default=ReceiverRole.Intermediary)
class Migrate(JoinedTableMixin, EventWithMultipleDevices):
"""Moves the devices to a new database/inventory. Devices cannot be
modified anymore at the previous database.
"""
other = Column(URL(), nullable=False)
other.comment = """
The URL of the Migrate in the other end.

View file

@ -14,7 +14,7 @@ from teal.db import CASCADE_OWN, UUIDLtree
from teal.resource import url_for_resource
from ereuse_devicehub.db import create_view, db
from ereuse_devicehub.resources.device.models import Component, Computer, Device
from ereuse_devicehub.resources.device.models import Component, Device
from ereuse_devicehub.resources.models import Thing
from ereuse_devicehub.resources.user.models import User
@ -90,16 +90,6 @@ class Lot(Thing):
_id = UUIDLtree.convert(id)
return (cls.id == Path.lot_id) & Path.path.lquery(exp.cast('*.{}.*'.format(_id), LQUERY))
@classmethod
def device_in_lotq(cls):
parent = Computer.__table__.alias()
device_inside_lot = (Device.id == LotDevice.device_id) & (Lot.id == LotDevice.lot_id)
parent_device_in_lot = (Device.id == Component.id) \
& (Component.parent_id == parent.c.id) \
& (parent.c.id == LotDevice.device_id) \
& (Lot.id == LotDevice.lot_id)
return device_inside_lot | parent_device_in_lot
@property
def parents(self):
return self.parentsq(self.id)

View file

@ -209,7 +209,6 @@ def test_update_parent():
(models.Repair, states.Physical.Repaired),
(models.ToPrepare, states.Physical.Preparing),
(models.ReadyToUse, states.Physical.ReadyToBeUsed),
(models.ToPrepare, states.Physical.Preparing),
(models.Prepare, states.Physical.Prepared)
])
def test_generic_event(event_model_state: Tuple[models.Event, states.Trading], user: UserClient):