Merge remote-tracking branch 'origin/rate' into rate
# Conflicts: # ereuse_devicehub/resources/event/models.py # ereuse_devicehub/resources/event/rate/main.py
This commit is contained in:
commit
0c5fa197d7
|
@ -16,7 +16,7 @@ Our main objectives are:
|
||||||
- To highly integrate with existing IT Asset Management Systems.
|
- To highly integrate with existing IT Asset Management Systems.
|
||||||
- To be decentralized.
|
- To be decentralized.
|
||||||
|
|
||||||
Devicehub is built with [Teal](https://github.com/bustawin/teal) and
|
Devicehub is built with [Teal](https://github.com/ereuse/teal) and
|
||||||
[Flask](http://flask.pocoo.org).
|
[Flask](http://flask.pocoo.org).
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
Actions and states
|
|
||||||
##################
|
|
||||||
|
|
||||||
Actions
|
Actions
|
||||||
*******
|
#######
|
||||||
|
|
||||||
Actions are events performed to devices, changing their **state**.
|
Actions are events performed to devices, changing their **state**.
|
||||||
Actions can have attributes defining
|
Actions can have attributes defining
|
||||||
**where** it happened, **who** performed them, **when**, etc.
|
**where** it happened, **who** performed them, **when**, etc.
|
||||||
Actions are stored in a log for each device. An exemplifying action
|
Actions are stored in a log for each device. An exemplifying action
|
||||||
can be ``Repair``, which dictates that a device has been repaired,
|
can be ``Repair``, which dictates that a device has been repaired,
|
||||||
after this action, the device is in the ``repaired`` state.
|
after this action, the device is in the ``repaired`` state. Another
|
||||||
|
example is performing a ``Sell`` to agent 1 (now this agent *owns*
|
||||||
|
the device), and then performing another ``Sell`` to agent 2 (now
|
||||||
|
agent 2 is the owner).
|
||||||
|
|
||||||
Devicehub actions inherit from `schema actions
|
Devicehub actions inherit from `schema actions
|
||||||
<http://schema.org/Action>`_, are written in Pascal case and using
|
<http://schema.org/Action>`_, are written in Pascal case and using
|
||||||
|
@ -19,14 +18,14 @@ is going to be / must be repaired, whereas ``Repair`` states
|
||||||
that the reparation happened. The former actions have the preposition
|
that the reparation happened. The former actions have the preposition
|
||||||
*To* prefixing the verb.
|
*To* prefixing the verb.
|
||||||
|
|
||||||
Actions and states affect devices in different ways or **dimensions**.
|
:ref:`actions:Actions` and :ref:`states:States` affect devices in
|
||||||
|
different ways or **dimensions**.
|
||||||
For example, ``Repair`` affects the **physical** dimension of a device,
|
For example, ``Repair`` affects the **physical** dimension of a device,
|
||||||
and ``Sell`` the **political** dimension of a device. A device
|
and ``Sell`` the **political** dimension of a device. A device
|
||||||
can be in several states at the same time, one per dimension; ie. a
|
can be in several states at the same time, one per dimension; ie. a
|
||||||
device can be ``repaired`` (physical) and ``reserved`` (political),
|
device can be ``repaired`` (physical) and ``reserved`` (political),
|
||||||
but not ``repaired`` and ``disposed`` at the same time:
|
but not ``repaired`` and ``disposed`` at the same time:
|
||||||
|
|
||||||
|
|
||||||
- Physical actions: The following actions describe and react on the
|
- Physical actions: The following actions describe and react on the
|
||||||
Physical condition of the devices.
|
Physical condition of the devices.
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@ but not ``repaired`` and ``disposed`` at the same time:
|
||||||
- DisposeWaste, Recover
|
- DisposeWaste, Recover
|
||||||
|
|
||||||
- Association actions: Actions that change the associations users have with devices;
|
- Association actions: Actions that change the associations users have with devices;
|
||||||
ie. the **owners**, **usufructuarees**, **reservees**,
|
ie. the **owners**, **usufructuarees** (*from usufruct*), **reservees** (*from reserve*),
|
||||||
and **physical possessors**.
|
and **physical possessors**.
|
||||||
|
|
||||||
- Trade
|
- Trade
|
||||||
|
@ -60,15 +59,8 @@ but not ``repaired`` and ``disposed`` at the same time:
|
||||||
The following index has all the actions (please note we are moving from calling them
|
The following index has all the actions (please note we are moving from calling them
|
||||||
``Event`` to call them ``Action``):
|
``Event`` to call them ``Action``):
|
||||||
|
|
||||||
|
Schema
|
||||||
|
******
|
||||||
|
|
||||||
.. dhlist::
|
.. dhlist::
|
||||||
:module: ereuse_devicehub.resources.event.schemas
|
:module: ereuse_devicehub.resources.event.schemas
|
||||||
|
|
||||||
|
|
||||||
States
|
|
||||||
******
|
|
||||||
.. autoclass:: ereuse_devicehub.resources.device.states.State
|
|
||||||
|
|
||||||
.. uml:: states.puml
|
|
||||||
|
|
||||||
.. autoclass:: ereuse_devicehub.resources.device.states.Trading
|
|
||||||
.. autoclass:: ereuse_devicehub.resources.device.states.Physical
|
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
Devices
|
Devices
|
||||||
#########
|
#######
|
||||||
|
Devices are objects that can be identified, and they are the
|
||||||
|
main entity in a Devicehub. Refer to :ref:`devices:Device` for more
|
||||||
|
info.
|
||||||
|
|
||||||
|
Schema
|
||||||
|
******
|
||||||
|
The following schema represents all the device types and their
|
||||||
|
properties.
|
||||||
|
|
||||||
|
.. dhlist::
|
||||||
|
:module: ereuse_devicehub.resources.device.schemas
|
||||||
|
|
||||||
|
API
|
||||||
|
***
|
||||||
You can retrieve devices using ``GET /devices/``, or a specific
|
You can retrieve devices using ``GET /devices/``, or a specific
|
||||||
device by ``GET /devices/24``.
|
device by ``GET /devices/24``.
|
||||||
|
|
||||||
|
@ -46,5 +59,4 @@ The result is a JSON object with the following fields:
|
||||||
- **next**: The number of the next page, if any.
|
- **next**: The number of the next page, if any.
|
||||||
- **last**: The number of the last page, if any.
|
- **last**: The number of the last page, if any.
|
||||||
|
|
||||||
.. dhlist::
|
|
||||||
:module: ereuse_devicehub.resources.device.schemas
|
|
||||||
|
|
|
@ -14,19 +14,36 @@ reusing devices, created under the project
|
||||||
|
|
||||||
Our main objectives are:
|
Our main objectives are:
|
||||||
|
|
||||||
- To offer a common IT Asset Management for donors, receivers and IT
|
- To offer a common IT Asset Management for distributors, refurbishers,
|
||||||
professionals so they can manage devices and exchange them.
|
receivers and other IT professionals so they can manage devices and exchange them.
|
||||||
This is, reusing –and ultimately recycling.
|
This is, reusing —and ultimately recycling.
|
||||||
- To automatically recollect, analyse, process and share
|
- To automatically recollect, analyse, process and share
|
||||||
(controlling privacy) metadata about devices with other tools of the
|
(controlling privacy) metadata about devices with other tools of the
|
||||||
eReuse ecosystem to guarantee traceability, and to provide inputs for
|
eReuse ecosystem to guarantee traceability, and to provide inputs for
|
||||||
the indicators which measure circularity.
|
the indicators which measure circularity.
|
||||||
- To highly integrate with existing IT Asset Management Systems.
|
|
||||||
- To be decentralized.
|
|
||||||
|
|
||||||
Devicehub is built with `Teal <https://github.com/bustawin/teal>`_ and
|
The main entity of a Devicehub are :ref:`devices:Devices`, which is any object that
|
||||||
`Flask <http://flask.pocoo.org>`_.
|
can be identified. Devices are divided in *types* (like ``Computer``),
|
||||||
|
and each one defines *properties*, like serial number, weight,
|
||||||
|
quality rating, pricing, or a list of owners.
|
||||||
|
|
||||||
|
We perform :ref:`actions:Actions` on devices, which are events that
|
||||||
|
change their *state* and *properties*. Examples are sales, reparations,
|
||||||
|
quality diagnostics, data wiping, and location.
|
||||||
|
Actions are stored in the traceability log of the device.
|
||||||
|
|
||||||
|
Devicehub is decentralized, and each instance is an inventory. We can
|
||||||
|
share and exchange devices between inventories —like in real live between
|
||||||
|
organizations.
|
||||||
|
|
||||||
|
:ref:`tags:Tags` identify devices through those organizations and their
|
||||||
|
internal systems. With Devicehub we can manage and print smart tags with
|
||||||
|
QR and NFC capabilities, operating devices by literally scanning them.
|
||||||
|
|
||||||
|
Devicehub is a REST API built with `Teal <https://github.com/ereuse/teal>`_ and
|
||||||
|
`Flask <http://flask.pocoo.org>`_ using `PostgreSQL <https://www.postgresql.org>`_.
|
||||||
|
`DevicehubClient <https://github.com/ereuse/devicehubclient>`_ is the
|
||||||
|
front–end that consumes this API.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
@ -34,6 +51,7 @@ Devicehub is built with `Teal <https://github.com/bustawin/teal>`_ and
|
||||||
api
|
api
|
||||||
devices
|
devices
|
||||||
actions
|
actions
|
||||||
|
states
|
||||||
tags
|
tags
|
||||||
lots
|
lots
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
States
|
||||||
|
######
|
||||||
|
.. note:: In construction.
|
||||||
|
|
||||||
|
A mutable property of a device result of applying an
|
||||||
|
:ref:`actions:Action` to it.
|
||||||
|
|
||||||
|
States are represented as properties in :ref:`devices:Device` and
|
||||||
|
sub–types. They can be steps in a workflow
|
||||||
|
(like ``sold`` and ``payed``, part of a trading), or properties
|
||||||
|
describing computed values from applying events (like a list of owners,
|
||||||
|
or a quality rating).
|
||||||
|
|
||||||
|
There are three types of states:
|
||||||
|
|
||||||
|
* **Trading**: a workflow of states resulting from applying the action
|
||||||
|
:ref:`actions:Trade`.
|
||||||
|
* **Physical**: a workflow of states resulting from applying
|
||||||
|
physical actions (ref. :ref:`actions:Actions`).
|
||||||
|
* **Attributes**: miscellaneous device properties that are not part of
|
||||||
|
a workflow.
|
||||||
|
|
||||||
|
.. uml:: states.puml
|
||||||
|
|
||||||
|
Trading
|
||||||
|
*******
|
||||||
|
Trading states.
|
||||||
|
|
||||||
|
:cvar Reserved: The device has been reserved.
|
||||||
|
:cvar Cancelled: The device has been cancelled.
|
||||||
|
:cvar Sold: The device has been sold.
|
||||||
|
:cvar Donated: The device is donated.
|
||||||
|
:cvar Renting: The device is in renting
|
||||||
|
:cvar ToBeDisposed: The device is disposed.
|
||||||
|
This is the end of life of a device.
|
||||||
|
:cvar ProductDisposed: The device has been removed
|
||||||
|
from the facility. It does not mean end-of-life.
|
||||||
|
|
||||||
|
Physical
|
||||||
|
********
|
||||||
|
Physical states.
|
||||||
|
|
||||||
|
:cvar ToBeRepaired: The device has been selected for reparation.
|
||||||
|
: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 InUse: The device is being reported to be in active use.
|
|
@ -36,6 +36,7 @@ class SQLAlchemy(SchemaSQLAlchemy):
|
||||||
# manually import them all the time
|
# manually import them all the time
|
||||||
UUID = postgresql.UUID
|
UUID = postgresql.UUID
|
||||||
CIText = citext.CIText
|
CIText = citext.CIText
|
||||||
|
PSQL_INT_MAX = 2147483648
|
||||||
|
|
||||||
def drop_all(self, bind='__all__', app=None, common_schema=True):
|
def drop_all(self, bind='__all__', app=None, common_schema=True):
|
||||||
"""A faster nuke-like option to drop everything."""
|
"""A faster nuke-like option to drop everything."""
|
||||||
|
|
|
@ -6,6 +6,10 @@ from ereuse_devicehub.resources.event import models as e
|
||||||
|
|
||||||
|
|
||||||
class State(Enum):
|
class State(Enum):
|
||||||
|
"""A mutable property of a device result of applying an
|
||||||
|
:ref:`actions:Action` to it.
|
||||||
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def events(cls):
|
def events(cls):
|
||||||
"""Events participating in this state."""
|
"""Events participating in this state."""
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from ereuse_devicehub.resources.device import models as d
|
||||||
|
from ereuse_devicehub.resources.event.models import TestDataStorage, BenchmarkDataStorage
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceRow(OrderedDict):
|
||||||
|
NUMS = {
|
||||||
|
d.Display.t: 1,
|
||||||
|
d.Processor.t: 2,
|
||||||
|
d.GraphicCard.t: 2,
|
||||||
|
d.Motherboard.t: 1,
|
||||||
|
d.NetworkAdapter.t: 2,
|
||||||
|
d.SoundCard.t: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO Add more fields information
|
||||||
|
def __init__(self, device: d.Device) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.device = device
|
||||||
|
# General information about device
|
||||||
|
self['Type'] = device.t
|
||||||
|
if isinstance(device, d.Computer):
|
||||||
|
self['Chassis'] = device.chassis
|
||||||
|
else:
|
||||||
|
self['Chassis'] = ''
|
||||||
|
self['Tag 1'] = self['Tag 2'] = self['Tag 3'] = ''
|
||||||
|
for i, tag in zip(range(1, 3), device.tags):
|
||||||
|
self['Tag {}'.format(i)] = format(tag)
|
||||||
|
self['Serial Number'] = device.serial_number
|
||||||
|
self['Model'] = device.model
|
||||||
|
self['Manufacturer'] = device.manufacturer
|
||||||
|
# self['State'] = device.last_event_of()
|
||||||
|
self['Registered in'] = format(device.created, '%c')
|
||||||
|
self['Price'] = device.price
|
||||||
|
if isinstance(device, d.Computer):
|
||||||
|
self['Processor'] = device.processor_model
|
||||||
|
self['RAM (GB)'] = device.ram_size
|
||||||
|
self['Data Storage Size (MB)'] = device.data_storage_size
|
||||||
|
rate = device.rate
|
||||||
|
if rate:
|
||||||
|
self['Rate'] = rate.rating
|
||||||
|
self['Range'] = rate.rating_range
|
||||||
|
self['Processor Rate'] = rate.processor
|
||||||
|
self['Processor Range'] = rate.workbench.processor_range
|
||||||
|
self['RAM Rate'] = rate.ram
|
||||||
|
self['RAM Range'] = rate.workbench.ram_range
|
||||||
|
self['Data Storage Rate'] = rate.data_storage
|
||||||
|
self['Data Storage Range'] = rate.workbench.data_storage_range
|
||||||
|
# More specific information about components
|
||||||
|
if isinstance(device, d.Computer):
|
||||||
|
self.components()
|
||||||
|
|
||||||
|
def components(self):
|
||||||
|
"""
|
||||||
|
Function to get all components information of a device
|
||||||
|
"""
|
||||||
|
assert isinstance(self.device, d.Computer)
|
||||||
|
# todo put an input specific order (non alphabetic) & where are a list of types components
|
||||||
|
for type in sorted(current_app.resources[d.Component.t].subresources_types): # type: str
|
||||||
|
max = self.NUMS.get(type, 4)
|
||||||
|
if type not in ['Component', 'HardDrive', 'SolidStateDrive']:
|
||||||
|
i = 1
|
||||||
|
for component in (r for r in self.device.components if r.type == type):
|
||||||
|
self.fill_component(type, i, component)
|
||||||
|
i += 1
|
||||||
|
if i > max:
|
||||||
|
break
|
||||||
|
while i <= max:
|
||||||
|
self.fill_component(type, i)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
def fill_component(self, type, i, component=None):
|
||||||
|
"""
|
||||||
|
Function to put specific information of components in OrderedDict (csv)
|
||||||
|
:param type: type of component
|
||||||
|
:param component: device.components
|
||||||
|
"""
|
||||||
|
self['{} {}'.format(type, i)] = format(component) if component else ''
|
||||||
|
self['{} {} Manufacturer'.format(type, i)] = component.serial_number if component else ''
|
||||||
|
self['{} {} Model'.format(type, i)] = component.serial_number if component else ''
|
||||||
|
self['{} {} Serial Number'.format(type, i)] = component.serial_number if component else ''
|
||||||
|
|
||||||
|
""" Particular fields for component GraphicCard """
|
||||||
|
if isinstance(component, d.GraphicCard):
|
||||||
|
self['{} {} Memory (MB)'.format(type, i)] = component.memory
|
||||||
|
|
||||||
|
""" Particular fields for component DataStorage.t -> (HardDrive, SolidStateDrive) """
|
||||||
|
if isinstance(component, d.DataStorage):
|
||||||
|
self['{} {} Size (MB)'.format(type, i)] = component.size
|
||||||
|
self['{} {} Privacy'.format(type, i)] = component.privacy
|
||||||
|
try:
|
||||||
|
self['{} {} Lifetime'.format(type, i)] = component.last_event_of(TestDataStorage).lifetime
|
||||||
|
except:
|
||||||
|
self['{} {} Lifetime'.format(type, i)] = ''
|
||||||
|
try:
|
||||||
|
self['{} {} Reading speed'.format(type, i)] = component.last_event_of(BenchmarkDataStorage).read_speed
|
||||||
|
except:
|
||||||
|
self['{} {} Reading speed'.format(type, i)] = ''
|
||||||
|
try:
|
||||||
|
self['{} {} Writing speed'.format(type, i)] = component.last_event_of(BenchmarkDataStorage).write_speed
|
||||||
|
except:
|
||||||
|
self['{} {} Writing speed'.format(type, i)] = ''
|
||||||
|
|
||||||
|
""" Particular fields for component Processor """
|
||||||
|
if isinstance(component, d.Processor):
|
||||||
|
self['{} {} Number of cores'.format(type, i)] = component.cores
|
||||||
|
self['{} {} Speed (GHz)'.format(type, i)] = component.speed
|
||||||
|
|
||||||
|
""" Particular fields for component RamModule """
|
||||||
|
if isinstance(component, d.RamModule):
|
||||||
|
self['{} {} Size (MB)'.format(type, i)] = component.size
|
||||||
|
self['{} {} Speed (MHz)'.format(type, i)] = component.speed
|
||||||
|
|
||||||
|
# todo add Display size, ...
|
||||||
|
# todo add NetworkAdapter speedLink?
|
||||||
|
# todo add some ComputerAccessories
|
|
@ -1,5 +1,8 @@
|
||||||
|
import csv
|
||||||
|
import datetime
|
||||||
import enum
|
import enum
|
||||||
import uuid
|
import uuid
|
||||||
|
from io import StringIO
|
||||||
from typing import Callable, Iterable, Tuple
|
from typing import Callable, Iterable, Tuple
|
||||||
|
|
||||||
import boltons
|
import boltons
|
||||||
|
@ -7,11 +10,14 @@ import flask
|
||||||
import flask_weasyprint
|
import flask_weasyprint
|
||||||
import teal.marshmallow
|
import teal.marshmallow
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
|
from flask import make_response
|
||||||
|
from teal.cache import cache
|
||||||
from teal.resource import Resource
|
from teal.resource import Resource
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.device import models as devs
|
from ereuse_devicehub.resources.device import models as devs
|
||||||
from ereuse_devicehub.resources.device.views import DeviceView
|
from ereuse_devicehub.resources.device.views import DeviceView
|
||||||
|
from ereuse_devicehub.resources.documents.device_row import DeviceRow
|
||||||
from ereuse_devicehub.resources.event import models as evs
|
from ereuse_devicehub.resources.event import models as evs
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,6 +103,33 @@ class DocumentView(DeviceView):
|
||||||
return flask.render_template('documents/erasure.html', **params)
|
return flask.render_template('documents/erasure.html', **params)
|
||||||
|
|
||||||
|
|
||||||
|
class DevicesDocumentView(DeviceView):
|
||||||
|
@cache(datetime.timedelta(minutes=1))
|
||||||
|
def find(self, args: dict):
|
||||||
|
query = self.query(args)
|
||||||
|
return self.generate_post_csv(query)
|
||||||
|
|
||||||
|
def generate_post_csv(self, query):
|
||||||
|
"""
|
||||||
|
Get device query and put information in csv format
|
||||||
|
:param query:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = StringIO()
|
||||||
|
cw = csv.writer(data)
|
||||||
|
first = True
|
||||||
|
for device in query:
|
||||||
|
d = DeviceRow(device)
|
||||||
|
if first:
|
||||||
|
cw.writerow(name for name in d.keys())
|
||||||
|
first = False
|
||||||
|
cw.writerow(v for v in d.values())
|
||||||
|
output = make_response(data.getvalue())
|
||||||
|
output.headers['Content-Disposition'] = 'attachment; filename=export.csv'
|
||||||
|
output.headers['Content-type'] = 'text/csv'
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
class DocumentDef(Resource):
|
class DocumentDef(Resource):
|
||||||
__type__ = 'Document'
|
__type__ = 'Document'
|
||||||
SCHEMA = None
|
SCHEMA = None
|
||||||
|
@ -124,3 +157,9 @@ class DocumentDef(Resource):
|
||||||
self.add_url_rule('/erasures/', defaults=d, view_func=view, methods=get)
|
self.add_url_rule('/erasures/', defaults=d, view_func=view, methods=get)
|
||||||
self.add_url_rule('/erasures/<{}:{}>'.format(self.ID_CONVERTER.value, self.ID_NAME),
|
self.add_url_rule('/erasures/<{}:{}>'.format(self.ID_CONVERTER.value, self.ID_NAME),
|
||||||
view_func=view, methods=get)
|
view_func=view, methods=get)
|
||||||
|
devices_view = DevicesDocumentView.as_view('devicesDocumentView',
|
||||||
|
definition=self,
|
||||||
|
auth=app.auth)
|
||||||
|
if self.AUTH:
|
||||||
|
devices_view = app.auth.requires_auth(devices_view)
|
||||||
|
self.add_url_rule('/devices/', defaults=d, view_func=devices_view, methods=get)
|
||||||
|
|
|
@ -4,7 +4,7 @@ from teal.resource import Converters, Resource
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device.sync import Sync
|
from ereuse_devicehub.resources.device.sync import Sync
|
||||||
from ereuse_devicehub.resources.event import schemas
|
from ereuse_devicehub.resources.event import schemas
|
||||||
from ereuse_devicehub.resources.event.views import EventView, SnapshotView
|
from ereuse_devicehub.resources.event.views import EventView
|
||||||
|
|
||||||
|
|
||||||
class EventDef(Resource):
|
class EventDef(Resource):
|
||||||
|
@ -90,13 +90,14 @@ class InstallDef(EventDef):
|
||||||
|
|
||||||
|
|
||||||
class SnapshotDef(EventDef):
|
class SnapshotDef(EventDef):
|
||||||
VIEW = SnapshotView
|
VIEW = None
|
||||||
SCHEMA = schemas.Snapshot
|
SCHEMA = schemas.Snapshot
|
||||||
|
|
||||||
def __init__(self, app, import_name=__name__.split('.')[0], static_folder=None,
|
def __init__(self, app, import_name=__name__.split('.')[0], static_folder=None,
|
||||||
static_url_path=None,
|
static_url_path=None,
|
||||||
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
|
template_folder=None, url_prefix=None, subdomain=None, url_defaults=None,
|
||||||
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
|
root_path=None, cli_commands: Iterable[Tuple[Callable, str or None]] = tuple()):
|
||||||
|
url_prefix = '/{}'.format(EventDef.resource)
|
||||||
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
|
super().__init__(app, import_name, static_folder, static_url_path, template_folder,
|
||||||
url_prefix, subdomain, url_defaults, root_path, cli_commands)
|
url_prefix, subdomain, url_defaults, root_path, cli_commands)
|
||||||
self.sync = Sync()
|
self.sync = Sync()
|
||||||
|
|
|
@ -12,14 +12,21 @@ from ereuse_devicehub.resources.device.models import Component, Computer
|
||||||
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
||||||
from ereuse_devicehub.resources.event.models import Event, Snapshot, WorkbenchRate
|
from ereuse_devicehub.resources.event.models import Event, Snapshot, WorkbenchRate
|
||||||
|
|
||||||
|
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
||||||
|
|
||||||
|
|
||||||
class EventView(View):
|
class EventView(View):
|
||||||
def post(self):
|
def post(self):
|
||||||
"""Posts an event."""
|
"""Posts an event."""
|
||||||
json = request.get_json(validate=False)
|
json = request.get_json(validate=False)
|
||||||
if 'type' not in json:
|
if not json or 'type' not in json:
|
||||||
raise ValidationError('Resource needs a type.')
|
raise ValidationError('Resource needs a type.')
|
||||||
e = app.resources[json['type']].schema.load(json)
|
# todo there should be a way to better get subclassess resource
|
||||||
|
# defs
|
||||||
|
resource_def = app.resources[json['type']]
|
||||||
|
e = resource_def.schema.load(json)
|
||||||
|
if json['type'] == Snapshot.t:
|
||||||
|
return self.snapshot(e, resource_def)
|
||||||
Model = db.Model._decl_class_registry.data[json['type']]()
|
Model = db.Model._decl_class_registry.data[json['type']]()
|
||||||
event = Model(**e)
|
event = Model(**e)
|
||||||
db.session.add(event)
|
db.session.add(event)
|
||||||
|
@ -34,25 +41,20 @@ class EventView(View):
|
||||||
event = Event.query.filter_by(id=id).one()
|
event = Event.query.filter_by(id=id).one()
|
||||||
return self.schema.jsonify(event)
|
return self.schema.jsonify(event)
|
||||||
|
|
||||||
|
def snapshot(self, snapshot_json: dict, resource_def):
|
||||||
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
|
||||||
|
|
||||||
|
|
||||||
class SnapshotView(View):
|
|
||||||
def post(self):
|
|
||||||
"""
|
"""
|
||||||
Performs a Snapshot.
|
Performs a Snapshot.
|
||||||
|
|
||||||
See `Snapshot` section in docs for more info.
|
See `Snapshot` section in docs for more info.
|
||||||
"""
|
"""
|
||||||
s = request.get_json()
|
|
||||||
# Note that if we set the device / components into the snapshot
|
# Note that if we set the device / components into the snapshot
|
||||||
# model object, when we flush them to the db we will flush
|
# model object, when we flush them to the db we will flush
|
||||||
# snapshot, and we want to wait to flush snapshot at the end
|
# snapshot, and we want to wait to flush snapshot at the end
|
||||||
device = s.pop('device') # type: Computer
|
device = snapshot_json.pop('device') # type: Computer
|
||||||
components = s.pop('components') \
|
components = None
|
||||||
if s['software'] == SnapshotSoftware.Workbench else None # type: List[Component]
|
if snapshot_json['software'] == SnapshotSoftware.Workbench:
|
||||||
snapshot = Snapshot(**s)
|
components = snapshot_json.pop('components') # type: List[Component]
|
||||||
|
snapshot = Snapshot(**snapshot_json)
|
||||||
|
|
||||||
# Remove new events from devices so they don't interfere with sync
|
# Remove new events from devices so they don't interfere with sync
|
||||||
events_device = set(e for e in device.events_one)
|
events_device = set(e for e in device.events_one)
|
||||||
|
@ -62,10 +64,9 @@ class SnapshotView(View):
|
||||||
for component in components:
|
for component in components:
|
||||||
component.events_one.clear()
|
component.events_one.clear()
|
||||||
|
|
||||||
# noinspection PyArgumentList
|
|
||||||
assert not device.events_one
|
assert not device.events_one
|
||||||
assert all(not c.events_one for c in components) if components else True
|
assert all(not c.events_one for c in components) if components else True
|
||||||
db_device, remove_events = self.resource_def.sync.run(device, components)
|
db_device, remove_events = resource_def.sync.run(device, components)
|
||||||
snapshot.device = db_device
|
snapshot.device = db_device
|
||||||
snapshot.events |= remove_events | events_device # Set events to snapshot
|
snapshot.events |= remove_events | events_device # Set events to snapshot
|
||||||
# commit will change the order of the components by what
|
# commit will change the order of the components by what
|
||||||
|
|
|
@ -8,4 +8,3 @@ class Inventory(Thing):
|
||||||
id = mf.String(dump_only=True)
|
id = mf.String(dump_only=True)
|
||||||
name = mf.String(dump_only=True)
|
name = mf.String(dump_only=True)
|
||||||
tag_provider = teal.marshmallow.URL(dump_only=True, data_key='tagProvider')
|
tag_provider = teal.marshmallow.URL(dump_only=True, data_key='tagProvider')
|
||||||
tag_token = mf.UUID(dump_only=True, data_key='tagToken')
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ class User(Thing):
|
||||||
backref=db.backref('users', lazy=True, collection_class=set),
|
backref=db.backref('users', lazy=True, collection_class=set),
|
||||||
secondary=lambda: UserInventory.__table__,
|
secondary=lambda: UserInventory.__table__,
|
||||||
collection_class=set)
|
collection_class=set)
|
||||||
|
|
||||||
# todo set restriction that user has, at least, one active db
|
# todo set restriction that user has, at least, one active db
|
||||||
|
|
||||||
def __init__(self, email, password=None, inventories=None) -> None:
|
def __init__(self, email, password=None, inventories=None) -> None:
|
||||||
|
@ -41,6 +42,10 @@ class User(Thing):
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return '<User {0.email}>'.format(self)
|
return '<User {0.email}>'.format(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> str:
|
||||||
|
return self.__class__.__name__
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def individual(self):
|
def individual(self):
|
||||||
"""The individual associated for this database, or None."""
|
"""The individual associated for this database, or None."""
|
||||||
|
|
|
@ -24,7 +24,7 @@ requests[security]==2.19.1
|
||||||
requests-mock==1.5.2
|
requests-mock==1.5.2
|
||||||
SQLAlchemy==1.2.17
|
SQLAlchemy==1.2.17
|
||||||
SQLAlchemy-Utils==0.33.11
|
SQLAlchemy-Utils==0.33.11
|
||||||
teal==0.2.0a36
|
teal==0.2.0a38
|
||||||
webargs==4.0.0
|
webargs==4.0.0
|
||||||
Werkzeug==0.14.1
|
Werkzeug==0.14.1
|
||||||
sqlalchemy-citext==1.3.post0
|
sqlalchemy-citext==1.3.post0
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -29,7 +29,7 @@ setup(
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'teal>=0.2.0a36', # teal always first
|
'teal>=0.2.0a38', # teal always first
|
||||||
'click',
|
'click',
|
||||||
'click-spinner',
|
'click-spinner',
|
||||||
'ereuse-utils[naming, test, session, cli]>=0.4b21',
|
'ereuse-utils[naming, test, session, cli]>=0.4b21',
|
||||||
|
|
|
@ -22,3 +22,4 @@ elapsed: 25
|
||||||
software: Workbench
|
software: Workbench
|
||||||
uuid: 76860eca-c3fd-41f6-a801-6af7bd8cf832
|
uuid: 76860eca-c3fd-41f6-a801-6af7bd8cf832
|
||||||
version: '11.0'
|
version: '11.0'
|
||||||
|
type: Snapshot
|
||||||
|
|
|
@ -18,3 +18,4 @@ elapsed: 25
|
||||||
software: Workbench
|
software: Workbench
|
||||||
uuid: f2e02261-87a1-4a50-b9b7-92c0e476e5f2
|
uuid: f2e02261-87a1-4a50-b9b7-92c0e476e5f2
|
||||||
version: '11.0'
|
version: '11.0'
|
||||||
|
type: Snapshot
|
||||||
|
|
|
@ -19,3 +19,4 @@ elapsed: 30
|
||||||
software: Workbench
|
software: Workbench
|
||||||
uuid: 3be271b6-5ef4-47d8-8237-5e1133eebfc6
|
uuid: 3be271b6-5ef4-47d8-8237-5e1133eebfc6
|
||||||
version: '11.0'
|
version: '11.0'
|
||||||
|
type: Snapshot
|
||||||
|
|
|
@ -18,3 +18,4 @@ elapsed: 25
|
||||||
software: Workbench
|
software: Workbench
|
||||||
uuid: fd007eb4-48e3-454a-8763-169491904c6e
|
uuid: fd007eb4-48e3-454a-8763-169491904c6e
|
||||||
version: '11.0'
|
version: '11.0'
|
||||||
|
type: Snapshot
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price,Processor,RAM (GB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
||||||
|
Desktop,Microtower,,,,d1s,d1ml,d1mr,Tue Mar 5 19:54:18 2019,,p1ml,0,0,0.8,Very low,1.0,Very low,1.0,Very low,1.0,Very low,,,,,,,,,,,,,,,,,,,,,"GraphicCard 2: model gc1ml, S/N gc1s",gc1s,gc1s,gc1s,,,,,,,,,,,,,,,,,,"Processor 4: model p1ml, S/N p1s",p1s,p1s,p1s,,1.6,,,,,"RamModule 3: model rm1ml, S/N rm1s",rm1s,rm1s,rm1s,,1333,,,,,,,,,,,,,,,,,,,,
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price
|
||||||
|
ComputerMonitor,,,,,cn0fp446728728541c8s,1707fpf,dell,Wed Oct 24 20:57:18 2018
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
{
|
||||||
|
"closed": true,
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"model": "NM10/ICH7 Family High Definition Audio Controller",
|
||||||
|
"serialNumber": null,
|
||||||
|
"type": "SoundCard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Broadcom Inc. and subsidiaries",
|
||||||
|
"model": "NetLink BCM5786 Gigabit Ethernet PCI Express",
|
||||||
|
"serialNumber": "00:1a:6b:5e:7f:10",
|
||||||
|
"speed": 1000,
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"wireless": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"format": "DIMM",
|
||||||
|
"interface": "DDR",
|
||||||
|
"manufacturer": null,
|
||||||
|
"model": null,
|
||||||
|
"serialNumber": null,
|
||||||
|
"size": 1024,
|
||||||
|
"speed": 133.0,
|
||||||
|
"type": "RamModule"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"format": "DIMM",
|
||||||
|
"interface": "DDR",
|
||||||
|
"manufacturer": null,
|
||||||
|
"model": null,
|
||||||
|
"serialNumber": null,
|
||||||
|
"size": 1024,
|
||||||
|
"speed": 133.0,
|
||||||
|
"type": "RamModule"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": 64,
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"elapsed": 33,
|
||||||
|
"rate": 32.9274,
|
||||||
|
"type": "BenchmarkProcessorSysbench"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 0,
|
||||||
|
"rate": 8771.5,
|
||||||
|
"type": "BenchmarkProcessor"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"manufacturer": "Intel Corp.",
|
||||||
|
"model": "Intel Core2 Duo CPU E4500 @ 2.20GHz",
|
||||||
|
"serialNumber": null,
|
||||||
|
"speed": 1.1,
|
||||||
|
"threads": 2,
|
||||||
|
"type": "Processor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"memory": 256.0,
|
||||||
|
"model": "82946GZ/GL Integrated Graphics Controller",
|
||||||
|
"serialNumber": null,
|
||||||
|
"type": "GraphicCard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"firewire": 0,
|
||||||
|
"manufacturer": "LENOVO",
|
||||||
|
"model": "LENOVO",
|
||||||
|
"pcmcia": 0,
|
||||||
|
"serial": 1,
|
||||||
|
"serialNumber": null,
|
||||||
|
"slots": 0,
|
||||||
|
"type": "Motherboard",
|
||||||
|
"usb": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"device": {
|
||||||
|
"chassis": "Microtower",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"appearanceRange": "D",
|
||||||
|
"biosRange": "E",
|
||||||
|
"functionalityRange": "D",
|
||||||
|
"type": "WorkbenchRate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 300,
|
||||||
|
"severity": "Info",
|
||||||
|
"type": "StressTest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 2,
|
||||||
|
"rate": 1.4968,
|
||||||
|
"type": "BenchmarkRamSysbench"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"manufacturer": "LENOVO",
|
||||||
|
"model": "9644W8N",
|
||||||
|
"serialNumber": "0169622",
|
||||||
|
"type": "Desktop"
|
||||||
|
},
|
||||||
|
"elapsed": 338,
|
||||||
|
"endTime": "2019-02-13T11:57:31.378330+00:00",
|
||||||
|
"expectedEvents": [
|
||||||
|
"Benchmark",
|
||||||
|
"TestDataStorage",
|
||||||
|
"StressTest",
|
||||||
|
"Install"
|
||||||
|
],
|
||||||
|
"software": "Workbench",
|
||||||
|
"type": "Snapshot",
|
||||||
|
"uuid": "d7904bd3-7d0f-4918-86b1-e21bfab738f9",
|
||||||
|
"version": "11.0b5"
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price
|
||||||
|
Keyboard,,,,,bar,foo,baz,Wed Oct 24 21:01:48 2018
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
{
|
||||||
|
"closed": true,
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Qualcomm Atheros",
|
||||||
|
"model": "QCA9565 / AR9565 Wireless Network Adapter",
|
||||||
|
"serialNumber": "ac:e0:10:c2:e3:ac",
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"wireless": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Realtek Semiconductor Co., Ltd.",
|
||||||
|
"model": "RTL810xE PCI Express Fast Ethernet controller",
|
||||||
|
"serialNumber": "30:8d:99:25:6c:d9",
|
||||||
|
"speed": 100,
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"wireless": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Advanced Micro Devices, Inc. AMD/ATI",
|
||||||
|
"model": "Kabini HDMI/DP Audio",
|
||||||
|
"serialNumber": null,
|
||||||
|
"type": "SoundCard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Chicony Electronics Co.,Ltd.",
|
||||||
|
"model": "HP Webcam",
|
||||||
|
"serialNumber": "0x0001",
|
||||||
|
"type": "SoundCard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Advanced Micro Devices, Inc. AMD",
|
||||||
|
"model": "FCH Azalia Controller",
|
||||||
|
"serialNumber": null,
|
||||||
|
"type": "SoundCard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"format": "SODIMM",
|
||||||
|
"interface": "DDR3",
|
||||||
|
"manufacturer": "Hynix",
|
||||||
|
"model": "HMT451S6AFR8A-PB",
|
||||||
|
"serialNumber": "11743764",
|
||||||
|
"size": 4096,
|
||||||
|
"speed": 667.0,
|
||||||
|
"type": "RamModule"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": 64,
|
||||||
|
"cores": 2,
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"elapsed": 0,
|
||||||
|
"rate": 3992.32,
|
||||||
|
"type": "BenchmarkProcessor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 65,
|
||||||
|
"rate": 65.3007,
|
||||||
|
"type": "BenchmarkProcessorSysbench"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"manufacturer": "Advanced Micro Devices AMD",
|
||||||
|
"model": "AMD E1-2100 APU with Radeon HD Graphics",
|
||||||
|
"serialNumber": null,
|
||||||
|
"speed": 0.9,
|
||||||
|
"threads": 2,
|
||||||
|
"type": "Processor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"elapsed": 12,
|
||||||
|
"readSpeed": 90.0,
|
||||||
|
"type": "BenchmarkDataStorage",
|
||||||
|
"writeSpeed": 30.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assessment": true,
|
||||||
|
"commandTimeout": 1341,
|
||||||
|
"currentPendingSectorCount": 0,
|
||||||
|
"elapsed": 113,
|
||||||
|
"length": "Short",
|
||||||
|
"lifetime": 1782,
|
||||||
|
"offlineUncorrectable": 0,
|
||||||
|
"powerCycleCount": 806,
|
||||||
|
"reallocatedSectorCount": 224,
|
||||||
|
"reportedUncorrectableErrors": 9961472,
|
||||||
|
"severity": "Info",
|
||||||
|
"status": "Completed without error",
|
||||||
|
"type": "TestDataStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": 32,
|
||||||
|
"elapsed": 690,
|
||||||
|
"name": "LinuxMint-19-x86-es-2018-12.fsa",
|
||||||
|
"severity": "Info",
|
||||||
|
"type": "Install"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interface": "ATA",
|
||||||
|
"manufacturer": null,
|
||||||
|
"model": "HGST HTS545050A7",
|
||||||
|
"serialNumber": "TE85134N34LNSN",
|
||||||
|
"size": 476940,
|
||||||
|
"type": "HardDrive"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"manufacturer": "Advanced Micro Devices, Inc. AMD/ATI",
|
||||||
|
"memory": 256.0,
|
||||||
|
"model": "Kabini Radeon HD 8210",
|
||||||
|
"serialNumber": null,
|
||||||
|
"type": "GraphicCard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"events": [],
|
||||||
|
"firewire": 0,
|
||||||
|
"manufacturer": "Hewlett-Packard",
|
||||||
|
"model": "21F7",
|
||||||
|
"pcmcia": 0,
|
||||||
|
"serial": 1,
|
||||||
|
"serialNumber": "PEHERF41U8P9TV",
|
||||||
|
"slots": 0,
|
||||||
|
"type": "Motherboard",
|
||||||
|
"usb": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"device": {
|
||||||
|
"chassis": "Netbook",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"appearanceRange": "A",
|
||||||
|
"functionalityRange": "A",
|
||||||
|
"type": "WorkbenchRate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 300,
|
||||||
|
"severity": "Info",
|
||||||
|
"type": "StressTest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 6,
|
||||||
|
"rate": 5.8783,
|
||||||
|
"type": "BenchmarkRamSysbench"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"manufacturer": "Hewlett-Packard",
|
||||||
|
"model": "HP 255 G3 Notebook",
|
||||||
|
"serialNumber": "CND52270FW",
|
||||||
|
"type": "Laptop"
|
||||||
|
},
|
||||||
|
"elapsed": 1194,
|
||||||
|
"endTime": "2019-02-13T10:13:50.535387+00:00",
|
||||||
|
"expectedEvents": [
|
||||||
|
"Benchmark",
|
||||||
|
"TestDataStorage",
|
||||||
|
"StressTest",
|
||||||
|
"Install"
|
||||||
|
],
|
||||||
|
"software": "Workbench",
|
||||||
|
"type": "Snapshot",
|
||||||
|
"uuid": "ca564895-567e-4ac2-9a0d-2d1402528687",
|
||||||
|
"version": "11.0b5"
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price,Processor,RAM (GB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
||||||
|
Laptop,Netbook,,,,b8oaas048286,1001pxd,asustek computer inc.,Wed Mar 6 18:22:05 2019,,intel atom cpu n455 @ 1.66ghz,1024,238475,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium,,,,,,,,,,,,,,,,,,,,,"GraphicCard 5: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None",,,,256,,,,,"Motherboard 10: model 1001pxd, S/N eee0123456789",eee0123456789,eee0123456789,eee0123456789,"NetworkAdapter 2: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8",74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,"NetworkAdapter 3: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c",14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,"Processor 4: model intel atom cpu n455 @ 1.66ghz, S/N None",,,,1,1.667,,,,,"RamModule 8: model None, S/N None",,,,1024,667,,,,,,,,,,,,,"SoundCard 6: model nm10/ich7 family high definition audio controller, S/N None",,,,"SoundCard 7: model usb 2.0 uvc vga webcam, S/N 0x0001",0x0001,0x0001,0x0001
|
||||||
|
NetworkAdapter,,,,,74:2f:68:8b:fd:c8,ar9285 wireless network adapter,qualcomm atheros,Wed Mar 6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
NetworkAdapter,,,,,14:da:e9:42:f6:7c,ar8152 v2.0 fast ethernet,qualcomm atheros,Wed Mar 6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
Processor,,,,,,intel atom cpu n455 @ 1.66ghz,intel corp.,Wed Mar 6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
GraphicCard,,,,,,atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller,intel corporation,Wed Mar 6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
SoundCard,,,,,,nm10/ich7 family high definition audio controller,intel corporation,Wed Mar 6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
SoundCard,,,,,0x0001,usb 2.0 uvc vga webcam,azurewave,Wed Mar 6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
RamModule,,,,,,,,Wed Mar 6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
HardDrive,,,,,e2024242cv86hj,hts54322,hitachi,Wed Mar6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
Motherboard,,,,,eee0123456789,1001pxd,asustek computer inc.,Wed Mar 6 18:22:05 2019,,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium
|
||||||
|
Desktop,Microtower,,,,d1s,d1ml,d1mr,Wed Mar 6 18:22:06 2019,,p1ml,0,0,0.8,Very low,1.0,Very low,1.0,Very low,1.0,Very low,,,,,,,,,,,,,,,,,,,,,"GraphicCard 12: model gc1ml, S/N gc1s",gc1s,gc1s,gc1s,,,,,,,,,,,,,,,,,,"Processor 14: model p1ml, S/N p1s",p1s,p1s,p1s,,1.6,,,,,"RamModule 13: model rm1ml, S/N rm1s",rm1s,rm1s,rm1s,,1333,,,,,,,,,,,,,,,,,,,,
|
||||||
|
GraphicCard,,,,,gc1s,gc1ml,gc1mr,Wed Mar 6 18:22:06 2019,,0.8,Very low,1.0,Very low,1.0,Very low,1.0,Very low
|
||||||
|
RamModule,,,,,rm1s,rm1ml,rm1mr,Wed Mar 6 18:22:06 2019,,0.8,Very low,1.0,Very low,1.0,Very low,1.0,Very low
|
||||||
|
Processor,,,,,p1s,p1ml,p1mr,Wed Mar 6 18:22:06 2019,,0.8,Very low,1.0,Very low,1.0,Very low,1.0,Very low
|
||||||
|
Keyboard,,,,,bar,foo,baz,Wed Mar 6 18:22:06 2019,
|
||||||
|
ComputerMonitor,,,,,cn0fp446728728541c8s,1707fpf,dell,Wed Mar 6 18:22:06 2019,
|
Can't render this file because it has a wrong number of fields in line 3.
|
|
@ -0,0 +1,2 @@
|
||||||
|
Type,Chassis,Tag 1,Tag 2,Tag 3,Serial Number,Model,Manufacturer,Registered in,Price,Processor,RAM (GB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range,DataStorage 1,DataStorage 1 Manufacturer,DataStorage 1 Model,DataStorage 1 Serial Number,DataStorage 2,DataStorage 2 Manufacturer,DataStorage 2 Model,DataStorage 2 Serial Number,DataStorage 3,DataStorage 3 Manufacturer,DataStorage 3 Model,DataStorage 3 Serial Number,DataStorage 4,DataStorage 4 Manufacturer,DataStorage 4 Model,DataStorage 4 Serial Number,Display 1,Display 1 Manufacturer,Display 1 Model,Display 1 Serial Number,GraphicCard 1,GraphicCard 1 Manufacturer,GraphicCard 1 Model,GraphicCard 1 Serial Number,GraphicCard 1 Memory (MB),GraphicCard 2,GraphicCard 2 Manufacturer,GraphicCard 2 Model,GraphicCard 2 Serial Number,Motherboard 1,Motherboard 1 Manufacturer,Motherboard 1 Model,Motherboard 1 Serial Number,NetworkAdapter 1,NetworkAdapter 1 Manufacturer,NetworkAdapter 1 Model,NetworkAdapter 1 Serial Number,NetworkAdapter 2,NetworkAdapter 2 Manufacturer,NetworkAdapter 2 Model,NetworkAdapter 2 Serial Number,Processor 1,Processor 1 Manufacturer,Processor 1 Model,Processor 1 Serial Number,Processor 1 Number of cores,Processor 1 Speed (GHz),Processor 2,Processor 2 Manufacturer,Processor 2 Model,Processor 2 Serial Number,RamModule 1,RamModule 1 Manufacturer,RamModule 1 Model,RamModule 1 Serial Number,RamModule 1 Size (MB),RamModule 1 Speed (MHz),RamModule 2,RamModule 2 Manufacturer,RamModule 2 Model,RamModule 2 Serial Number,RamModule 3,RamModule 3 Manufacturer,RamModule 3 Model,RamModule 3 Serial Number,RamModule 4,RamModule 4 Manufacturer,RamModule 4 Model,RamModule 4 Serial Number,SoundCard 1,SoundCard 1 Manufacturer,SoundCard 1 Model,SoundCard 1 Serial Number,SoundCard 2,SoundCard 2 Manufacturer,SoundCard 2 Model,SoundCard 2 Serial Number
|
||||||
|
Laptop,Netbook,,,,b8oaas048286,1001pxd,asustek computer inc.,Tue Mar 5 19:56:08 2019,,intel atom cpu n455 @ 1.66ghz,1024,238475,1.73,Very low,1.0,Very low,1.53,Very low,3.76,Medium,,,,,,,,,,,,,,,,,,,,,"GraphicCard 5: model atom processor d4xx/d5xx/n4xx/n5xx integrated graphics controller, S/N None",,,,256,,,,,"Motherboard 10: model 1001pxd, S/N eee0123456789",eee0123456789,eee0123456789,eee0123456789,"NetworkAdapter 2: model ar9285 wireless network adapter, S/N 74:2f:68:8b:fd:c8",74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,74:2f:68:8b:fd:c8,"NetworkAdapter 3: model ar8152 v2.0 fast ethernet, S/N 14:da:e9:42:f6:7c",14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,14:da:e9:42:f6:7c,"Processor 4: model intel atom cpu n455 @ 1.66ghz, S/N None",,,,1,1.667,,,,,"RamModule 8: model None, S/N None",,,,1024,667,,,,,,,,,,,,,"SoundCard 6: model nm10/ich7 family high definition audio controller, S/N None",,,,"SoundCard 7: model usb 2.0 uvc vga webcam, S/N 0x0001",0x0001,0x0001,0x0001
|
|
|
@ -21,7 +21,6 @@ def test_api_docs(client: Client):
|
||||||
'/users/',
|
'/users/',
|
||||||
'/devices/',
|
'/devices/',
|
||||||
'/tags/',
|
'/tags/',
|
||||||
'/snapshots/',
|
|
||||||
'/users/login/',
|
'/users/login/',
|
||||||
'/events/',
|
'/events/',
|
||||||
'/lots/',
|
'/lots/',
|
||||||
|
@ -29,6 +28,7 @@ def test_api_docs(client: Client):
|
||||||
'/lots/{id}/children',
|
'/lots/{id}/children',
|
||||||
'/lots/{id}/devices',
|
'/lots/{id}/devices',
|
||||||
'/documents/erasures/',
|
'/documents/erasures/',
|
||||||
|
'/documents/devices/',
|
||||||
'/documents/static/{filename}',
|
'/documents/static/{filename}',
|
||||||
'/tags/{tag_id}/device/{device_id}',
|
'/tags/{tag_id}/device/{device_id}',
|
||||||
'/devices/static/{filename}'
|
'/devices/static/{filename}'
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import datetime
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from teal.db import UniqueViolation
|
||||||
|
|
||||||
|
|
||||||
|
def test_unique_violation():
|
||||||
|
class IntegrityErrorMock:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.params = {
|
||||||
|
'uuid': UUID('f5efd26e-8754-46bc-87bf-fbccc39d60d9'),
|
||||||
|
'version': '11.0',
|
||||||
|
'software': 'Workbench', 'elapsed': datetime.timedelta(0, 4),
|
||||||
|
'expected_events': None,
|
||||||
|
'id': UUID('dbdef3d8-2cac-48cb-adb8-419bc3e59687')
|
||||||
|
}
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return """(psycopg2.IntegrityError) duplicate key value violates unique constraint "snapshot_uuid_key"
|
||||||
|
DETAIL: Key (uuid)=(f5efd26e-8754-46bc-87bf-fbccc39d60d9) already exists.
|
||||||
|
[SQL: 'INSERT INTO snapshot (uuid, version, software, elapsed, expected_events, id)
|
||||||
|
VALUES (%(uuid)s, %(version)s, %(software)s, %(elapsed)s, CAST(%(expected_events)s
|
||||||
|
AS snapshotexpectedevents[]), %(id)s)'] [parameters: {'uuid': UUID('f5efd26e-8754-46bc-87bf-fbccc39d60d9'),
|
||||||
|
'version': '11.0', 'software': 'Workbench', 'elapsed': datetime.timedelta(0, 4), 'expected_events': None,
|
||||||
|
'id': UUID('dbdef3d8-2cac-48cb-adb8-419bc3e59687')}] (Background on this error at: http://sqlalche.me/e/gkpj)"""
|
||||||
|
|
||||||
|
u = UniqueViolation(IntegrityErrorMock())
|
||||||
|
assert u.constraint == 'snapshot_uuid_key'
|
||||||
|
assert u.field_name == 'uuid'
|
||||||
|
assert u.field_value == UUID('f5efd26e-8754-46bc-87bf-fbccc39d60d9')
|
|
@ -0,0 +1,169 @@
|
||||||
|
import csv
|
||||||
|
from datetime import datetime
|
||||||
|
from io import StringIO
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ereuse_devicehub.client import UserClient
|
||||||
|
from ereuse_devicehub.resources.documents import documents
|
||||||
|
from ereuse_devicehub.resources.event.models import Snapshot
|
||||||
|
from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_basic_snapshot(user: UserClient):
|
||||||
|
"""
|
||||||
|
Test export device information in a csv file
|
||||||
|
"""
|
||||||
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('basic.csv').open() as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
assert isinstance(datetime.strptime(export_csv[1][8], '%c'), datetime), \
|
||||||
|
'Register in field is not a datetime'
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:8] + fixture_csv[1][9:]
|
||||||
|
export_csv[1] = export_csv[1][:8] + export_csv[1][9:]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Computer information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_full_snapshot(user: UserClient):
|
||||||
|
"""
|
||||||
|
Test a export device with all information and a lot of components
|
||||||
|
"""
|
||||||
|
snapshot, _ = user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('real-eee-1001pxd.csv').open() \
|
||||||
|
as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
assert isinstance(datetime.strptime(export_csv[1][8], '%c'), datetime), \
|
||||||
|
'Register in field is not a datetime'
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:8] + fixture_csv[1][9:]
|
||||||
|
export_csv[1] = export_csv[1][:8] + export_csv[1][9:]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Computer information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_empty(user: UserClient):
|
||||||
|
"""
|
||||||
|
Test to check works correctly exporting csv without any information (no snapshot)
|
||||||
|
"""
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
accept='text/csv',
|
||||||
|
item='devices/')
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
assert len(export_csv) == 0, 'Csv is not empty'
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_computer_monitor(user: UserClient):
|
||||||
|
"""
|
||||||
|
Test a export device type computer monitor
|
||||||
|
"""
|
||||||
|
snapshot, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['ComputerMonitor']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('computer-monitor.csv').open() \
|
||||||
|
as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:8]
|
||||||
|
export_csv[1] = export_csv[1][:8]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Component information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_keyboard(user: UserClient):
|
||||||
|
"""
|
||||||
|
Test a export device type keyboard
|
||||||
|
"""
|
||||||
|
snapshot, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Keyboard']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('keyboard.csv').open() as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:8]
|
||||||
|
export_csv[1] = export_csv[1][:8]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Component information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_multiple_devices(user: UserClient):
|
||||||
|
"""
|
||||||
|
Test a export multiple devices (Computers and other types) with different information
|
||||||
|
"""
|
||||||
|
# Post all devices snapshots
|
||||||
|
snapshot_pc, _ = user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
||||||
|
snapshot_empty, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
|
snapshot_keyboard, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
||||||
|
snapshot_monitor, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
||||||
|
|
||||||
|
# need query param??
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='devices/',
|
||||||
|
accept='text/csv')
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('multiples_devices.csv').open() \
|
||||||
|
as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
|
||||||
|
max_range = max(len(export_csv), len(fixture_csv)) - 1
|
||||||
|
# check if all devices information is correct
|
||||||
|
for i in range(1, max_range):
|
||||||
|
if isinstance(datetime.strptime(export_csv[i][8], '%c'), datetime):
|
||||||
|
export_csv[i] = export_csv[i][:8] + export_csv[i][9:]
|
||||||
|
fixture_csv[i] = fixture_csv[i][:8] + fixture_csv[i][9:]
|
||||||
|
|
||||||
|
assert fixture_csv[i] == export_csv[i], 'Some fields are not equal'
|
|
@ -453,3 +453,14 @@ def test_snapshot_keyboard(user: UserClient):
|
||||||
snapshot = snapshot_and_check(user, s, event_types=('ManualRate',))
|
snapshot = snapshot_and_check(user, s, event_types=('ManualRate',))
|
||||||
keyboard = snapshot['device']
|
keyboard = snapshot['device']
|
||||||
assert keyboard['layout'] == 'ES'
|
assert keyboard['layout'] == 'ES'
|
||||||
|
|
||||||
|
|
||||||
|
def test_pc_rating_rate_none(user: UserClient):
|
||||||
|
"""Tests a Snapshot with EraseSectors."""
|
||||||
|
s = file('desktop-9644w8n-lenovo-0169622.snapshot')
|
||||||
|
snapshot, _ = user.post(res=Snapshot, data=s)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pc_2(user: UserClient):
|
||||||
|
s = file('laptop-hp_255_g3_notebook-hewlett-packard-cnd52270fw.snapshot')
|
||||||
|
snapshot, _ = user.post(res=Snapshot, data=s)
|
||||||
|
|
Reference in New Issue