From 560e0ed8dc057a7323c88aa488a9d2e117dc1453 Mon Sep 17 00:00:00 2001 From: Xavier Bustamante Talavera Date: Mon, 12 Nov 2018 18:15:24 +0100 Subject: [PATCH] Add docstrings to event models; remove redundant generic test --- docs/actions.rst | 335 ++++---------------- docs/conf.py | 10 +- docs/devices.rst | 7 + ereuse_devicehub/resources/device/models.py | 6 +- ereuse_devicehub/resources/event/models.py | 318 +++++++++++++++++-- ereuse_devicehub/resources/lot/models.py | 12 +- tests/test_event.py | 1 - 7 files changed, 385 insertions(+), 304 deletions(-) diff --git a/docs/actions.rst b/docs/actions.rst index 639835ce..fb3eee7a 100644 --- a/docs/actions.rst +++ b/docs/actions.rst @@ -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 -`_. 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 -`_. 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 `_. +.. 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: diff --git a/docs/conf.py b/docs/conf.py index 84540ff3..fd4964e9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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 \ No newline at end of file +autosectionlabel_prefix_document = True +autodoc_member_order = 'bysource' diff --git a/docs/devices.rst b/docs/devices.rst index 4403b992..c6ca3ea4 100644 --- a/docs/devices.rst +++ b/docs/devices.rst @@ -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 diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index a13b4de1..4c77872b 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -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 diff --git a/ereuse_devicehub/resources/event/models.py b/ereuse_devicehub/resources/event/models.py index da5d65b0..a7db0b1f 100644 --- a/ereuse_devicehub/resources/event/models.py +++ b/ereuse_devicehub/resources/event/models.py @@ -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 `_. + + 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) `_: + + 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 `_. + """ + # 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 + `_. 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 + `_. 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 `_. + """ 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. diff --git a/ereuse_devicehub/resources/lot/models.py b/ereuse_devicehub/resources/lot/models.py index 6e850cdc..8604965c 100644 --- a/ereuse_devicehub/resources/lot/models.py +++ b/ereuse_devicehub/resources/lot/models.py @@ -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) diff --git a/tests/test_event.py b/tests/test_event.py index 22d6d61f..410b792d 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -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):