Merge branch 'rate' into devel
|
@ -41,8 +41,7 @@ def get_version(ctx, param, value):
|
||||||
@click.group(cls=DevicehubGroup,
|
@click.group(cls=DevicehubGroup,
|
||||||
context_settings=Devicehub.cli_context_settings,
|
context_settings=Devicehub.cli_context_settings,
|
||||||
add_version_option=False,
|
add_version_option=False,
|
||||||
help="""
|
help="""Manages the Devicehub of the inventory {}.
|
||||||
Manages the Devicehub of the inventory {}.
|
|
||||||
|
|
||||||
Use 'export dhi=xx' to set the inventory that this CLI
|
Use 'export dhi=xx' to set the inventory that this CLI
|
||||||
manages. For example 'export dhi=db1' and then executing
|
manages. For example 'export dhi=db1' and then executing
|
||||||
|
|
|
@ -121,8 +121,7 @@ class Client(TealClient):
|
||||||
|
|
||||||
|
|
||||||
class UserClient(Client):
|
class UserClient(Client):
|
||||||
"""
|
"""A client that identifies all of its requests with a specific user.
|
||||||
A client that identifies all of its requests with a specific user.
|
|
||||||
|
|
||||||
It will automatically perform login on the first request.
|
It will automatically perform login on the first request.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -26,8 +26,7 @@ class DevicehubConfig(Config):
|
||||||
PASSWORD_SCHEMES = {'pbkdf2_sha256'} # type: Set[str]
|
PASSWORD_SCHEMES = {'pbkdf2_sha256'} # type: Set[str]
|
||||||
SQLALCHEMY_DATABASE_URI = 'postgresql://dhub:ereuse@localhost/devicehub' # type: str
|
SQLALCHEMY_DATABASE_URI = 'postgresql://dhub:ereuse@localhost/devicehub' # type: str
|
||||||
MIN_WORKBENCH = StrictVersion('11.0a1') # type: StrictVersion
|
MIN_WORKBENCH = StrictVersion('11.0a1') # type: StrictVersion
|
||||||
"""
|
"""The minimum version of ereuse.org workbench that this devicehub
|
||||||
the minimum version of ereuse.org workbench that this devicehub
|
|
||||||
accepts. we recommend not changing this value.
|
accepts. we recommend not changing this value.
|
||||||
"""
|
"""
|
||||||
API_DOC_CONFIG_TITLE = 'Devicehub'
|
API_DOC_CONFIG_TITLE = 'Devicehub'
|
||||||
|
@ -42,6 +41,4 @@ class DevicehubConfig(Config):
|
||||||
PRICE_SOFTWARE = PriceSoftware.Ereuse
|
PRICE_SOFTWARE = PriceSoftware.Ereuse
|
||||||
PRICE_VERSION = StrictVersion('1.0')
|
PRICE_VERSION = StrictVersion('1.0')
|
||||||
PRICE_CURRENCY = Currency.EUR
|
PRICE_CURRENCY = Currency.EUR
|
||||||
"""
|
"""Official versions."""
|
||||||
Official versions
|
|
||||||
"""
|
|
||||||
|
|
|
@ -27,8 +27,7 @@ class DhSession(SchemaSession):
|
||||||
|
|
||||||
|
|
||||||
class SQLAlchemy(SchemaSQLAlchemy):
|
class SQLAlchemy(SchemaSQLAlchemy):
|
||||||
"""
|
"""Superuser must create the required extensions in the public
|
||||||
Superuser must create the required extensions in the public
|
|
||||||
schema of the database, as it is in the `search_path`
|
schema of the database, as it is in the `search_path`
|
||||||
defined in teal.
|
defined in teal.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
"""
|
"""This file contains all actions can apply to a device and is sorted according
|
||||||
This file contains all actions can apply to a device and is sorted according
|
|
||||||
to a structure based on:
|
to a structure based on:
|
||||||
|
|
||||||
* Generic Actions
|
* Generic Actions
|
||||||
|
@ -72,34 +71,29 @@ class Action(Thing):
|
||||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
|
||||||
type = Column(Unicode, nullable=False)
|
type = Column(Unicode, nullable=False)
|
||||||
name = Column(CIText(), default='', nullable=False)
|
name = Column(CIText(), default='', nullable=False)
|
||||||
name.comment = """
|
name.comment = """A name or title for the action. Used when searching
|
||||||
A name or title for the action. Used when searching for actions.
|
for actions.
|
||||||
"""
|
"""
|
||||||
severity = Column(teal.db.IntEnum(Severity), default=Severity.Info, nullable=False)
|
severity = Column(teal.db.IntEnum(Severity), default=Severity.Info, nullable=False)
|
||||||
severity.comment = Severity.__doc__
|
severity.comment = Severity.__doc__
|
||||||
closed = Column(Boolean, default=True, nullable=False)
|
closed = Column(Boolean, default=True, nullable=False)
|
||||||
closed.comment = """
|
closed.comment = """Whether the author has finished the action.
|
||||||
Whether the author has finished the action.
|
After this is set to True, no modifications are allowed.
|
||||||
After this is set to True, no modifications are allowed.
|
By default actions are closed when performed.
|
||||||
By default actions are closed when performed.
|
|
||||||
"""
|
"""
|
||||||
description = Column(Unicode, default='', nullable=False)
|
description = Column(Unicode, default='', nullable=False)
|
||||||
description.comment = """
|
description.comment = """A comment about the action."""
|
||||||
A comment about the action.
|
|
||||||
"""
|
|
||||||
start_time = Column(db.TIMESTAMP(timezone=True))
|
start_time = Column(db.TIMESTAMP(timezone=True))
|
||||||
start_time.comment = """
|
start_time.comment = """When the action starts. For some actions like
|
||||||
When the action starts. For some actions like reservations
|
reservations the time when they are available, for others like renting
|
||||||
the time when they are available, for others like renting
|
when the renting starts.
|
||||||
when the renting starts.
|
|
||||||
"""
|
"""
|
||||||
end_time = Column(db.TIMESTAMP(timezone=True))
|
end_time = Column(db.TIMESTAMP(timezone=True))
|
||||||
end_time.comment = """
|
end_time.comment = """When the action ends. For some actions like reservations
|
||||||
When the action ends. For some actions like reservations
|
the time when they expire, for others like renting
|
||||||
the time when they expire, for others like renting
|
the time the end rents. For punctual actions it is the time
|
||||||
the time the end rents. For punctual actions it is the time
|
they are performed; it differs with ``created`` in which
|
||||||
they are performed; it differs with ``created`` in which
|
created is the where the system received the action.
|
||||||
created is the where the system received the action.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
snapshot_id = Column(UUID(as_uuid=True), ForeignKey('snapshot.id',
|
snapshot_id = Column(UUID(as_uuid=True), ForeignKey('snapshot.id',
|
||||||
|
@ -120,8 +114,7 @@ class Action(Thing):
|
||||||
author = relationship(User,
|
author = relationship(User,
|
||||||
backref=backref('authored_actions', lazy=True, collection_class=set),
|
backref=backref('authored_actions', lazy=True, collection_class=set),
|
||||||
primaryjoin=author_id == User.id)
|
primaryjoin=author_id == User.id)
|
||||||
author_id.comment = """
|
author_id.comment = """The user that recorded this action in the system.
|
||||||
The user that recorded this action in the system.
|
|
||||||
|
|
||||||
This does not necessarily has to be the person that produced
|
This does not necessarily has to be the person that produced
|
||||||
the action in the real world. For that purpose see
|
the action in the real world. For that purpose see
|
||||||
|
@ -136,8 +129,8 @@ class Action(Thing):
|
||||||
agent = relationship(Agent,
|
agent = relationship(Agent,
|
||||||
backref=backref('actions_agent', lazy=True, **_sorted_actions),
|
backref=backref('actions_agent', lazy=True, **_sorted_actions),
|
||||||
primaryjoin=agent_id == Agent.id)
|
primaryjoin=agent_id == Agent.id)
|
||||||
agent_id.comment = """
|
agent_id.comment = """The direct performer or driver of the action.
|
||||||
The direct performer or driver of the action. e.g. John wrote a book.
|
e.g. John wrote a book.
|
||||||
|
|
||||||
It can differ with the user that registered the action in the
|
It can differ with the user that registered the action in the
|
||||||
system, which can be in their behalf.
|
system, which can be in their behalf.
|
||||||
|
@ -148,8 +141,7 @@ class Action(Thing):
|
||||||
secondary=lambda: ActionComponent.__table__,
|
secondary=lambda: ActionComponent.__table__,
|
||||||
order_by=lambda: Component.id,
|
order_by=lambda: Component.id,
|
||||||
collection_class=OrderedSet)
|
collection_class=OrderedSet)
|
||||||
components.comment = """
|
components.comment = """The components that are affected by the action.
|
||||||
The components that are affected by the action.
|
|
||||||
|
|
||||||
When performing actions to parent devices their components are
|
When performing actions to parent devices their components are
|
||||||
affected too.
|
affected too.
|
||||||
|
@ -165,9 +157,8 @@ class Action(Thing):
|
||||||
parent = relationship(Computer,
|
parent = relationship(Computer,
|
||||||
backref=backref('actions_parent', lazy=True, **_sorted_actions),
|
backref=backref('actions_parent', lazy=True, **_sorted_actions),
|
||||||
primaryjoin=parent_id == Computer.id)
|
primaryjoin=parent_id == Computer.id)
|
||||||
parent_id.comment = """
|
parent_id.comment = """For actions that are performed to components,
|
||||||
For actions that are performed to components, the device parent
|
the device parent at that time.
|
||||||
at that time.
|
|
||||||
|
|
||||||
For example: for a ``EraseBasic`` performed on a data storage, this
|
For example: for a ``EraseBasic`` performed on a data storage, this
|
||||||
would point to the computer that contained this data storage, if any.
|
would point to the computer that contained this data storage, if any.
|
||||||
|
@ -197,8 +188,7 @@ class Action(Thing):
|
||||||
# noinspection PyMethodParameters
|
# noinspection PyMethodParameters
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -276,8 +266,7 @@ class ActionWithOneDevice(JoinedTableMixin, Action):
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -365,7 +354,7 @@ class EraseBasic(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
@property
|
@property
|
||||||
def certificate(self):
|
def certificate(self):
|
||||||
"""The URL of this erasure certificate."""
|
"""The URL of this erasure certificate."""
|
||||||
# todo will this url_for_resoure work for other resources?
|
# todo will this url_for_resource work for other resources?
|
||||||
return urlutils.URL(url_for_resource('Document', item_id=self.id))
|
return urlutils.URL(url_for_resource('Document', item_id=self.id))
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
@ -430,8 +419,7 @@ class Step(db.Model):
|
||||||
# noinspection PyMethodParameters
|
# noinspection PyMethodParameters
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -545,9 +533,8 @@ class Snapshot(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
version = Column(StrictVersionType(STR_SM_SIZE), nullable=False)
|
version = Column(StrictVersionType(STR_SM_SIZE), nullable=False)
|
||||||
software = Column(DBEnum(SnapshotSoftware), nullable=False)
|
software = Column(DBEnum(SnapshotSoftware), nullable=False)
|
||||||
elapsed = Column(Interval)
|
elapsed = Column(Interval)
|
||||||
elapsed.comment = """
|
elapsed.comment = """For Snapshots made with Workbench, the total amount
|
||||||
For Snapshots made with Workbench, the total amount of time
|
of time it took to complete.
|
||||||
it took to complete.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
@ -578,8 +565,7 @@ class Benchmark(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -656,8 +642,7 @@ class Test(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -684,6 +669,11 @@ class MeasureBattery(TestMixin, Test):
|
||||||
|
|
||||||
Operative Systems keep a record of several aspects of a battery.
|
Operative Systems keep a record of several aspects of a battery.
|
||||||
This is a sample of those.
|
This is a sample of those.
|
||||||
|
|
||||||
|
Failing and warning conditions are as follows:
|
||||||
|
|
||||||
|
* :attr:`Severity.Error`: whether the health are Dead, Overheat or OverVoltage.
|
||||||
|
* :attr:`Severity.Warning`: whether the health are UnspecifiedValue or Cold.
|
||||||
"""
|
"""
|
||||||
size = db.Column(db.Integer, nullable=False)
|
size = db.Column(db.Integer, nullable=False)
|
||||||
size.comment = """Maximum battery capacity, in mAh."""
|
size.comment = """Maximum battery capacity, in mAh."""
|
||||||
|
@ -712,7 +702,7 @@ class TestDataStorage(TestMixin, Test):
|
||||||
|
|
||||||
Failing and warning conditions are as follows:
|
Failing and warning conditions are as follows:
|
||||||
|
|
||||||
* :attr:`Severity.Error`: if the SMART test failed.
|
* :attr:`Severity.Error`: whether the SMART test failed.
|
||||||
* :attr:`Severity.Warning`: if there is a significant chance for
|
* :attr:`Severity.Warning`: if there is a significant chance for
|
||||||
the data storage to fail in the following year.
|
the data storage to fail in the following year.
|
||||||
"""
|
"""
|
||||||
|
@ -765,6 +755,11 @@ class TestDataStorage(TestMixin, Test):
|
||||||
class StressTest(TestMixin, Test):
|
class StressTest(TestMixin, Test):
|
||||||
"""The act of stressing (putting to the maximum capacity)
|
"""The act of stressing (putting to the maximum capacity)
|
||||||
a device for an amount of minutes.
|
a device for an amount of minutes.
|
||||||
|
|
||||||
|
Failing and warning conditions are as follows:
|
||||||
|
|
||||||
|
* :attr:`Severity.Error`: whether failed StressTest.
|
||||||
|
* :attr:`Severity.Warning`: if stress test are less than 5 minutes.
|
||||||
"""
|
"""
|
||||||
elapsed = Column(Interval, nullable=False)
|
elapsed = Column(Interval, nullable=False)
|
||||||
|
|
||||||
|
@ -780,7 +775,13 @@ class StressTest(TestMixin, Test):
|
||||||
|
|
||||||
|
|
||||||
class TestAudio(TestMixin, Test):
|
class TestAudio(TestMixin, Test):
|
||||||
"""The act of checking the audio aspects of the device."""
|
"""The act of checking the audio aspects of the device.
|
||||||
|
|
||||||
|
Failing and warning conditions are as follows:
|
||||||
|
|
||||||
|
* :attr:`Severity.Error`: whether speaker or microphone variables fail.
|
||||||
|
* :attr:`Severity.Warning`: .
|
||||||
|
"""
|
||||||
_speaker = Column('speaker', Boolean)
|
_speaker = Column('speaker', Boolean)
|
||||||
_speaker.comment = """Whether the speaker works as expected."""
|
_speaker.comment = """Whether the speaker works as expected."""
|
||||||
_microphone = Column('microphone', Boolean)
|
_microphone = Column('microphone', Boolean)
|
||||||
|
@ -824,9 +825,9 @@ class TestCamera(TestMixin, Test):
|
||||||
|
|
||||||
Failing and warning conditions are as follows:
|
Failing and warning conditions are as follows:
|
||||||
|
|
||||||
* :attr:`Severity.Error`: if the camera cannot turn on or
|
* :attr:`Severity.Error`: whether the camera cannot turn on or
|
||||||
has significant visual problems.
|
has significant visual problems.
|
||||||
* :attr:`Severity.Warning`: if there are small visual problems
|
* :attr:`Severity.Warning`: whether there are small visual problems
|
||||||
with the camera (like dust) that it still allows it to be used.
|
with the camera (like dust) that it still allows it to be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -856,7 +857,7 @@ class TestDisplayHinge(TestMixin, Test):
|
||||||
|
|
||||||
Failing and warning conditions are as follows:
|
Failing and warning conditions are as follows:
|
||||||
|
|
||||||
* :attr:`Severity.Error`: if the laptop does not stay open
|
* :attr:`Severity.Error`: whether the laptop does not stay open
|
||||||
or closed at desired angles. From R2 Provision 6 pag.22.
|
or closed at desired angles. From R2 Provision 6 pag.22.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -872,7 +873,13 @@ class TestPowerAdapter(TestMixin, Test):
|
||||||
|
|
||||||
|
|
||||||
class TestBios(TestMixin, Test):
|
class TestBios(TestMixin, Test):
|
||||||
"""Tests the working condition and grades the usability of the BIOS."""
|
"""Tests the working condition and grades the usability of the BIOS.
|
||||||
|
|
||||||
|
Failing and warning conditions are as follows:
|
||||||
|
|
||||||
|
* :attr:`Severity.Error`: whether Bios beeps or access range is D or E.
|
||||||
|
* :attr:`Severity.Warning`: whether access range is B or C.
|
||||||
|
"""
|
||||||
beeps_power_on = Column(Boolean)
|
beeps_power_on = Column(Boolean)
|
||||||
beeps_power_on.comment = """Whether there are no beeps or error
|
beeps_power_on.comment = """Whether there are no beeps or error
|
||||||
codes when booting up.
|
codes when booting up.
|
||||||
|
@ -884,7 +891,8 @@ class TestBios(TestMixin, Test):
|
||||||
|
|
||||||
This is used as an usability measure for accessing and modifying
|
This is used as an usability measure for accessing and modifying
|
||||||
a bios, specially as something as important as modifying the boot
|
a bios, specially as something as important as modifying the boot
|
||||||
menu."""
|
menu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class VisualTest(TestMixin, Test):
|
class VisualTest(TestMixin, Test):
|
||||||
|
@ -893,7 +901,16 @@ class VisualTest(TestMixin, Test):
|
||||||
|
|
||||||
Reference R2 provision 6 Templates Ready for Resale Checklist (Desktop)
|
Reference R2 provision 6 Templates Ready for Resale Checklist (Desktop)
|
||||||
https://sustainableelectronics.org/sites/default/files/6.c.2%20Desktop%20R2-Ready%20for%20Resale%20Checklist.docx
|
https://sustainableelectronics.org/sites/default/files/6.c.2%20Desktop%20R2-Ready%20for%20Resale%20Checklist.docx
|
||||||
Physical condition grade
|
Physical condition grade.
|
||||||
|
|
||||||
|
Failing and warning conditions are as follows:
|
||||||
|
|
||||||
|
* :attr:`Severity.Error`: whether appearance range is less than B or
|
||||||
|
functionality range is less than B.
|
||||||
|
* :attr:`Severity.Warning`: whether appearance range is B or A and
|
||||||
|
functionality range is B.
|
||||||
|
* :attr:`Severity.Info`: whether appearance range is B or A and
|
||||||
|
functionality range is A.
|
||||||
"""
|
"""
|
||||||
appearance_range = Column(DBEnum(AppearanceRange), nullable=False)
|
appearance_range = Column(DBEnum(AppearanceRange), nullable=False)
|
||||||
appearance_range.comment = AppearanceRange.__doc__
|
appearance_range.comment = AppearanceRange.__doc__
|
||||||
|
@ -916,7 +933,6 @@ class Rate(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
* Appearance (A). Visual evaluation, surface deterioration.
|
* Appearance (A). Visual evaluation, surface deterioration.
|
||||||
* Performance (Q). Components characteristics and components benchmarks.
|
* Performance (Q). Components characteristics and components benchmarks.
|
||||||
"""
|
"""
|
||||||
# todo jn: explain in each comment what the rate considers.
|
|
||||||
N = 2
|
N = 2
|
||||||
"""The number of significant digits for rates.
|
"""The number of significant digits for rates.
|
||||||
Values are rounded and stored to it.
|
Values are rounded and stored to it.
|
||||||
|
@ -929,11 +945,11 @@ class Rate(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
_appearance = Column('appearance',
|
_appearance = Column('appearance',
|
||||||
Float(decimal_return_scale=N),
|
Float(decimal_return_scale=N),
|
||||||
check_range('appearance', *R_NEGATIVE))
|
check_range('appearance', *R_NEGATIVE))
|
||||||
_appearance.comment = """"""
|
_appearance.comment = """Subjective value representing aesthetic aspects."""
|
||||||
_functionality = Column('functionality',
|
_functionality = Column('functionality',
|
||||||
Float(decimal_return_scale=N),
|
Float(decimal_return_scale=N),
|
||||||
check_range('functionality', *R_NEGATIVE))
|
check_range('functionality', *R_NEGATIVE))
|
||||||
_functionality.comment = """"""
|
_functionality.comment = """Subjective value representing usage aspects."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rating(self):
|
def rating(self):
|
||||||
|
@ -966,8 +982,7 @@ class Rate(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -993,10 +1008,9 @@ class RateMixin:
|
||||||
|
|
||||||
|
|
||||||
class RateComputer(RateMixin, Rate):
|
class RateComputer(RateMixin, Rate):
|
||||||
"""
|
"""The act of rating a computer type devices.
|
||||||
The act of rating a computer type devices.
|
|
||||||
It's the starting point for calculating the rate.
|
It's the starting point for calculating the rate.
|
||||||
Algorithm explained in v1.0 file
|
Algorithm explained in v1.0 file.
|
||||||
"""
|
"""
|
||||||
_processor = Column('processor',
|
_processor = Column('processor',
|
||||||
Float(decimal_return_scale=Rate.N),
|
Float(decimal_return_scale=Rate.N),
|
||||||
|
@ -1063,9 +1077,7 @@ class RateComputer(RateMixin, Rate):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def compute(cls, device):
|
def compute(cls, device):
|
||||||
"""
|
"""The act of compute general computer rate."""
|
||||||
The act of compute general computer rate
|
|
||||||
"""
|
|
||||||
from ereuse_devicehub.resources.action.rate.v1_0 import rate_algorithm
|
from ereuse_devicehub.resources.action.rate.v1_0 import rate_algorithm
|
||||||
rate = rate_algorithm.compute(device)
|
rate = rate_algorithm.compute(device)
|
||||||
price = None
|
price = None
|
||||||
|
@ -1075,6 +1087,7 @@ class RateComputer(RateMixin, Rate):
|
||||||
|
|
||||||
|
|
||||||
class Price(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
class Price(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
|
# TODO rewrite Class comment change AggregateRate..
|
||||||
"""The act of setting a trading price for the device.
|
"""The act of setting a trading price for the device.
|
||||||
|
|
||||||
This does not imply that the device is ultimately traded for that
|
This does not imply that the device is ultimately traded for that
|
||||||
|
@ -1101,7 +1114,8 @@ class Price(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
version.comment = """The version of the software, or None."""
|
version.comment = """The version of the software, or None."""
|
||||||
rating_id = Column(UUID(as_uuid=True), ForeignKey(Rate.id))
|
rating_id = Column(UUID(as_uuid=True), ForeignKey(Rate.id))
|
||||||
rating_id.comment = """The Rate used to auto-compute
|
rating_id.comment = """The Rate used to auto-compute
|
||||||
this price, if it has not been set manually."""
|
this price, if it has not been set manually.
|
||||||
|
"""
|
||||||
rating = relationship(Rate,
|
rating = relationship(Rate,
|
||||||
backref=backref('price',
|
backref=backref('price',
|
||||||
lazy=True,
|
lazy=True,
|
||||||
|
@ -1125,8 +1139,7 @@ class Price(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -1216,9 +1229,8 @@ class EreusePrice(Price):
|
||||||
|
|
||||||
@orm.reconstructor
|
@orm.reconstructor
|
||||||
def _compute(self):
|
def _compute(self):
|
||||||
"""
|
"""Calculates eReuse.org prices when initializing the instance
|
||||||
Calculates eReuse.org prices when initializing the
|
from the price and other properties.
|
||||||
instance from the price and other properties.
|
|
||||||
"""
|
"""
|
||||||
self.refurbisher = self._service(self.Service.REFURBISHER)
|
self.refurbisher = self._service(self.Service.REFURBISHER)
|
||||||
self.retailer = self._service(self.Service.RETAILER)
|
self.retailer = self._service(self.Service.RETAILER)
|
||||||
|
@ -1279,7 +1291,7 @@ class Prepare(ActionWithMultipleDevices):
|
||||||
class Live(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
class Live(JoinedWithOneDeviceMixin, ActionWithOneDevice):
|
||||||
"""A keep-alive from a device connected to the Internet with
|
"""A keep-alive from a device connected to the Internet with
|
||||||
information about its state (in the form of a ``Snapshot`` action)
|
information about its state (in the form of a ``Snapshot`` action)
|
||||||
and usage statistics.
|
and usage statistics.
|
||||||
"""
|
"""
|
||||||
ip = Column(IP, nullable=False,
|
ip = Column(IP, nullable=False,
|
||||||
comment='The IP where the live was triggered.')
|
comment='The IP where the live was triggered.')
|
||||||
|
@ -1335,41 +1347,34 @@ class Trade(JoinedTableMixin, ActionWithMultipleDevices):
|
||||||
extend `Schema's Trade <http://schema.org/TradeAction>`_.
|
extend `Schema's Trade <http://schema.org/TradeAction>`_.
|
||||||
"""
|
"""
|
||||||
shipping_date = Column(db.TIMESTAMP(timezone=True))
|
shipping_date = Column(db.TIMESTAMP(timezone=True))
|
||||||
shipping_date.comment = """
|
shipping_date.comment = """When are the devices going to be ready
|
||||||
When are the devices going to be ready for shipping?
|
for shipping?
|
||||||
"""
|
"""
|
||||||
invoice_number = Column(CIText())
|
invoice_number = Column(CIText())
|
||||||
invoice_number.comment = """
|
invoice_number.comment = """The id of the invoice so they can be linked."""
|
||||||
The id of the invoice so they can be linked.
|
|
||||||
"""
|
|
||||||
price_id = Column(UUID(as_uuid=True), ForeignKey(Price.id))
|
price_id = Column(UUID(as_uuid=True), ForeignKey(Price.id))
|
||||||
price = relationship(Price,
|
price = relationship(Price,
|
||||||
backref=backref('trade', lazy=True, uselist=False),
|
backref=backref('trade', lazy=True, uselist=False),
|
||||||
primaryjoin=price_id == Price.id)
|
primaryjoin=price_id == Price.id)
|
||||||
price_id.comment = """
|
price_id.comment = """The price set for this trade.
|
||||||
The price set for this trade.
|
If no price is set it is supposed that the trade was
|
||||||
|
not payed, usual in donations.
|
||||||
If no price is set it is supposed that the trade was
|
|
||||||
not payed, usual in donations.
|
|
||||||
"""
|
"""
|
||||||
to_id = Column(UUID(as_uuid=True), ForeignKey(Agent.id), nullable=False)
|
to_id = Column(UUID(as_uuid=True), ForeignKey(Agent.id), nullable=False)
|
||||||
# todo compute the org
|
# todo compute the org
|
||||||
to = relationship(Agent,
|
to = relationship(Agent,
|
||||||
backref=backref('actions_to', lazy=True, **_sorted_actions),
|
backref=backref('actions_to', lazy=True, **_sorted_actions),
|
||||||
primaryjoin=to_id == Agent.id)
|
primaryjoin=to_id == Agent.id)
|
||||||
to_comment = """
|
to_comment = """The agent that gets the device due this deal."""
|
||||||
The agent that gets the device due this deal.
|
|
||||||
"""
|
|
||||||
confirms_id = Column(UUID(as_uuid=True), ForeignKey(Organize.id))
|
confirms_id = Column(UUID(as_uuid=True), ForeignKey(Organize.id))
|
||||||
confirms = relationship(Organize,
|
confirms = relationship(Organize,
|
||||||
backref=backref('confirmation', lazy=True, uselist=False),
|
backref=backref('confirmation', lazy=True, uselist=False),
|
||||||
primaryjoin=confirms_id == Organize.id)
|
primaryjoin=confirms_id == Organize.id)
|
||||||
confirms_id.comment = """
|
confirms_id.comment = """An organize action that this association confirms.
|
||||||
An organize action that this association confirms.
|
|
||||||
|
|
||||||
For example, a ``Sell`` or ``Rent``
|
For example, a ``Sell`` or ``Rent``
|
||||||
can confirm a ``Reserve`` action.
|
can confirm a ``Reserve`` action.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Sell(Trade):
|
class Sell(Trade):
|
||||||
|
@ -1482,8 +1487,7 @@ def actions_not_for_components(target: Action, value: Device, old_value, initiat
|
||||||
|
|
||||||
@event.listens_for(ActionWithOneDevice.device, Events.set.__name__, propagate=True)
|
@event.listens_for(ActionWithOneDevice.device, Events.set.__name__, propagate=True)
|
||||||
def update_components_action_one(target: ActionWithOneDevice, device: Device, __, ___):
|
def update_components_action_one(target: ActionWithOneDevice, device: Device, __, ___):
|
||||||
"""
|
"""Syncs the :attr:`.Action.components` with the components in
|
||||||
Syncs the :attr:`.Action.components` with the components in
|
|
||||||
:attr:`ereuse_devicehub.resources.device.models.Computer.components`.
|
:attr:`ereuse_devicehub.resources.device.models.Computer.components`.
|
||||||
"""
|
"""
|
||||||
# For Add and Remove, ``components`` have different meanings
|
# For Add and Remove, ``components`` have different meanings
|
||||||
|
@ -1500,8 +1504,7 @@ def update_components_action_one(target: ActionWithOneDevice, device: Device, __
|
||||||
@event.listens_for(ActionWithMultipleDevices.devices, Events.append.__name__, propagate=True)
|
@event.listens_for(ActionWithMultipleDevices.devices, Events.append.__name__, propagate=True)
|
||||||
def update_components_action_multiple(target: ActionWithMultipleDevices,
|
def update_components_action_multiple(target: ActionWithMultipleDevices,
|
||||||
value: Union[Set[Device], Device], _):
|
value: Union[Set[Device], Device], _):
|
||||||
"""
|
"""Syncs the :attr:`.Action.components` with the components in
|
||||||
Syncs the :attr:`.Action.components` with the components in
|
|
||||||
:attr:`ereuse_devicehub.resources.device.models.Computer.components`.
|
:attr:`ereuse_devicehub.resources.device.models.Computer.components`.
|
||||||
"""
|
"""
|
||||||
target.components.clear()
|
target.components.clear()
|
||||||
|
@ -1513,8 +1516,7 @@ def update_components_action_multiple(target: ActionWithMultipleDevices,
|
||||||
|
|
||||||
@event.listens_for(ActionWithMultipleDevices.devices, Events.remove.__name__, propagate=True)
|
@event.listens_for(ActionWithMultipleDevices.devices, Events.remove.__name__, propagate=True)
|
||||||
def remove_components_action_multiple(target: ActionWithMultipleDevices, device: Device, __):
|
def remove_components_action_multiple(target: ActionWithMultipleDevices, device: Device, __):
|
||||||
"""
|
"""Syncs the :attr:`.Action.components` with the components in
|
||||||
Syncs the :attr:`.Action.components` with the components in
|
|
||||||
:attr:`ereuse_devicehub.resources.device.models.Computer.components`.
|
:attr:`ereuse_devicehub.resources.device.models.Computer.components`.
|
||||||
"""
|
"""
|
||||||
target.components.clear()
|
target.components.clear()
|
||||||
|
@ -1528,9 +1530,7 @@ def remove_components_action_multiple(target: ActionWithMultipleDevices, device:
|
||||||
@event.listens_for(Install.device, Events.set.__name__, propagate=True)
|
@event.listens_for(Install.device, Events.set.__name__, propagate=True)
|
||||||
@event.listens_for(Benchmark.device, Events.set.__name__, propagate=True)
|
@event.listens_for(Benchmark.device, Events.set.__name__, propagate=True)
|
||||||
def update_parent(target: Union[EraseBasic, Test, Install], device: Device, _, __):
|
def update_parent(target: Union[EraseBasic, Test, Install], device: Device, _, __):
|
||||||
"""
|
"""Syncs the :attr:`Action.parent` with the parent of the device."""
|
||||||
Syncs the :attr:`Action.parent` with the parent of the device.
|
|
||||||
"""
|
|
||||||
target.parent = None
|
target.parent = None
|
||||||
if isinstance(device, Component):
|
if isinstance(device, Component):
|
||||||
target.parent = device.parent
|
target.parent = device.parent
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
import math
|
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
|
|
||||||
|
|
||||||
class BaseRate:
|
class BaseRate:
|
||||||
"""growing exponential from this value"""
|
"""Growing exponential from this value."""
|
||||||
CEXP = 0
|
CEXP = 0
|
||||||
"""growing lineal starting on this value"""
|
"""Growing lineal starting on this value."""
|
||||||
CLIN = 242
|
CLIN = 242
|
||||||
"""growing logarithmic starting on this value"""
|
"""Growing logarithmic starting on this value."""
|
||||||
CLOG = 0.5
|
CLOG = 0.5
|
||||||
|
|
||||||
"""Processor has 50% of weight over total score, used in harmonic mean"""
|
"""Processor has 50% of weight over total score, used in harmonic mean."""
|
||||||
PROCESSOR_WEIGHT = 0.5
|
PROCESSOR_WEIGHT = 0.5
|
||||||
"""Storage has 20% of weight over total score, used in harmonic mean"""
|
"""Storage has 20% of weight over total score, used in harmonic mean."""
|
||||||
DATA_STORAGE_WEIGHT = 0.2
|
DATA_STORAGE_WEIGHT = 0.2
|
||||||
"""Ram has 30% of weight over total score, used in harmonic mean"""
|
"""Ram has 30% of weight over total score, used in harmonic mean."""
|
||||||
RAM_WEIGHT = 0.3
|
RAM_WEIGHT = 0.3
|
||||||
|
|
||||||
def compute(self, device: Device):
|
def compute(self, device: Device):
|
||||||
|
@ -43,9 +44,7 @@ class BaseRate:
|
||||||
return sum(weights) / sum(char / rate for char, rate in zip(weights, rates))
|
return sum(weights) / sum(char / rate for char, rate in zip(weights, rates))
|
||||||
|
|
||||||
def harmonic_mean_rates(self, rate_processor, rate_storage, rate_ram):
|
def harmonic_mean_rates(self, rate_processor, rate_storage, rate_ram):
|
||||||
"""
|
"""Merging components using harmonic formula."""
|
||||||
Merging components
|
|
||||||
"""
|
|
||||||
total_weights = self.PROCESSOR_WEIGHT + self.DATA_STORAGE_WEIGHT + self.RAM_WEIGHT
|
total_weights = self.PROCESSOR_WEIGHT + self.DATA_STORAGE_WEIGHT + self.RAM_WEIGHT
|
||||||
total_rate = self.PROCESSOR_WEIGHT / rate_processor \
|
total_rate = self.PROCESSOR_WEIGHT / rate_processor \
|
||||||
+ self.DATA_STORAGE_WEIGHT / rate_storage \
|
+ self.DATA_STORAGE_WEIGHT / rate_storage \
|
||||||
|
|
|
@ -13,14 +13,15 @@ class RateAlgorithm(BaseRate):
|
||||||
"""The algorithm that generates the Rate v1.0.
|
"""The algorithm that generates the Rate v1.0.
|
||||||
|
|
||||||
Rate v1.0 rates only computers, counting their processor, ram,
|
Rate v1.0 rates only computers, counting their processor, ram,
|
||||||
data storage, appearance, and functionality. This rate is only
|
data storage, appearance and functionality. This rate is only
|
||||||
triggered by a Snapshot from Workbench that has a VisualTest.
|
triggered by a Snapshot from Workbench that has a VisualTest.
|
||||||
The algorithm is as follows:
|
The algorithm is as follows:
|
||||||
|
|
||||||
1. Specialized subclasses of :class:`BaseRate` compute a rating
|
1. Specialized subclasses of :class:`BaseRate` compute a rating
|
||||||
for each component. To perform this, each class normalizes first
|
for each component. To perform this, each class normalizes first
|
||||||
the characteristics and benchmarks of the components between
|
the characteristics and benchmarks of the components between
|
||||||
0 and 1, and then they merge the values to a resulting score.
|
0 and 1, and then they merge the values with specific formulas for
|
||||||
|
each components to get a resulting score.
|
||||||
The classes are:
|
The classes are:
|
||||||
|
|
||||||
* :class:`ProcessorRate`, using cores, speed, and ``BenchmarkProcessor``.
|
* :class:`ProcessorRate`, using cores, speed, and ``BenchmarkProcessor``.
|
||||||
|
@ -103,9 +104,7 @@ class RateAlgorithm(BaseRate):
|
||||||
|
|
||||||
|
|
||||||
class ProcessorRate(BaseRate):
|
class ProcessorRate(BaseRate):
|
||||||
"""
|
"""Calculate a ProcessorRate of all Processor devices."""
|
||||||
Calculate a ProcessorRate of all Processor devices
|
|
||||||
"""
|
|
||||||
# processor.xMin, processor.xMax
|
# processor.xMin, processor.xMax
|
||||||
PROCESSOR_NORM = 3196.17, 17503.81
|
PROCESSOR_NORM = 3196.17, 17503.81
|
||||||
|
|
||||||
|
@ -115,10 +114,10 @@ class ProcessorRate(BaseRate):
|
||||||
DEFAULT_SCORE = 4000
|
DEFAULT_SCORE = 4000
|
||||||
|
|
||||||
def compute(self, processor: Processor):
|
def compute(self, processor: Processor):
|
||||||
""" Compute processor rate
|
"""Compute processor rate
|
||||||
We assume always exists a Benchmark Processor
|
We assume always exists a Benchmark Processor.
|
||||||
Obs: cores and speed are possible NULL value
|
Obs: cores and speed are possible NULL value
|
||||||
:return: result is a rate (score) of Processor characteristics
|
:return: result is a rate (score) of Processor characteristics
|
||||||
"""
|
"""
|
||||||
cores = processor.cores or self.DEFAULT_CORES
|
cores = processor.cores or self.DEFAULT_CORES
|
||||||
speed = processor.speed or self.DEFAULT_SPEED
|
speed = processor.speed or self.DEFAULT_SPEED
|
||||||
|
@ -146,9 +145,7 @@ class ProcessorRate(BaseRate):
|
||||||
|
|
||||||
|
|
||||||
class RamRate(BaseRate):
|
class RamRate(BaseRate):
|
||||||
"""
|
"""Calculate a RamRate of all RamModule devices."""
|
||||||
Calculate a RamRate of all RamModule devices
|
|
||||||
"""
|
|
||||||
# ram.size.xMin; ram.size.xMax
|
# ram.size.xMin; ram.size.xMax
|
||||||
SIZE_NORM = 256, 8192
|
SIZE_NORM = 256, 8192
|
||||||
RAM_SPEED_NORM = 133, 1333
|
RAM_SPEED_NORM = 133, 1333
|
||||||
|
@ -158,8 +155,7 @@ class RamRate(BaseRate):
|
||||||
RAM_WEIGHTS = 0.7, 0.3
|
RAM_WEIGHTS = 0.7, 0.3
|
||||||
|
|
||||||
def compute(self, ram_devices: Iterable[RamModule]):
|
def compute(self, ram_devices: Iterable[RamModule]):
|
||||||
"""
|
"""If ram speed or ram size, we assume default values before declared.
|
||||||
If ram speed or ram size, we assume default values before declared
|
|
||||||
:return: result is a rate (score) of all RamModule components
|
:return: result is a rate (score) of all RamModule components
|
||||||
"""
|
"""
|
||||||
size = 0.0
|
size = 0.0
|
||||||
|
@ -204,9 +200,7 @@ class RamRate(BaseRate):
|
||||||
|
|
||||||
|
|
||||||
class DataStorageRate(BaseRate):
|
class DataStorageRate(BaseRate):
|
||||||
"""
|
"""Calculate the rate of all DataStorage devices."""
|
||||||
Calculate the rate of all DataStorage devices
|
|
||||||
"""
|
|
||||||
# drive.size.xMin; drive.size.xMax
|
# drive.size.xMin; drive.size.xMax
|
||||||
SIZE_NORM = 4, 265000
|
SIZE_NORM = 4, 265000
|
||||||
READ_SPEED_NORM = 2.7, 109.5
|
READ_SPEED_NORM = 2.7, 109.5
|
||||||
|
@ -215,8 +209,7 @@ class DataStorageRate(BaseRate):
|
||||||
DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25
|
DATA_STORAGE_WEIGHTS = 0.5, 0.25, 0.25
|
||||||
|
|
||||||
def compute(self, data_storage_devices: Iterable[DataStorage]):
|
def compute(self, data_storage_devices: Iterable[DataStorage]):
|
||||||
"""
|
"""Obs: size != NULL and 0 value & read_speed and write_speed != NULL
|
||||||
Obs: size != NULL and 0 value & read_speed and write_speed != NULL
|
|
||||||
:return: result is a rate (score) of all DataStorage devices
|
:return: result is a rate (score) of all DataStorage devices
|
||||||
"""
|
"""
|
||||||
size = 0
|
size = 0
|
||||||
|
|
|
@ -46,8 +46,7 @@ class ActionView(View):
|
||||||
return self.schema.jsonify(action)
|
return self.schema.jsonify(action)
|
||||||
|
|
||||||
def snapshot(self, snapshot_json: dict, resource_def):
|
def snapshot(self, snapshot_json: dict, resource_def):
|
||||||
"""
|
"""Performs a Snapshot.
|
||||||
Performs a Snapshot.
|
|
||||||
|
|
||||||
See `Snapshot` section in docs for more info.
|
See `Snapshot` section in docs for more info.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -29,18 +29,13 @@ class Agent(Thing):
|
||||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
|
||||||
type = Column(Unicode, nullable=False)
|
type = Column(Unicode, nullable=False)
|
||||||
name = Column(CIText())
|
name = Column(CIText())
|
||||||
name.comment = """
|
name.comment = """The name of the organization or person."""
|
||||||
The name of the organization or person.
|
|
||||||
"""
|
|
||||||
tax_id = Column(Unicode(length=STR_SM_SIZE), check_lower('tax_id'))
|
tax_id = Column(Unicode(length=STR_SM_SIZE), check_lower('tax_id'))
|
||||||
tax_id.comment = """
|
tax_id.comment = """The Tax / Fiscal ID of the organization,
|
||||||
The Tax / Fiscal ID of the organization,
|
e.g. the TIN in the US or the CIF/NIF in Spain.
|
||||||
e.g. the TIN in the US or the CIF/NIF in Spain.
|
|
||||||
"""
|
"""
|
||||||
country = Column(DBEnum(enums.Country))
|
country = Column(DBEnum(enums.Country))
|
||||||
country.comment = """
|
country.comment = """Country issuing the tax_id number."""
|
||||||
Country issuing the tax_id number.
|
|
||||||
"""
|
|
||||||
telephone = Column(PhoneNumberType())
|
telephone = Column(PhoneNumberType())
|
||||||
email = Column(EmailType, unique=True)
|
email = Column(EmailType, unique=True)
|
||||||
|
|
||||||
|
@ -52,8 +47,7 @@ class Agent(Thing):
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -137,8 +131,7 @@ class Membership(Thing):
|
||||||
|
|
||||||
|
|
||||||
class Person(Individual):
|
class Person(Individual):
|
||||||
"""
|
"""A person in the system. There can be several persons pointing to
|
||||||
A person in the system. There can be several persons pointing to
|
|
||||||
a real.
|
a real.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -44,18 +44,15 @@ class Device(Thing):
|
||||||
(it is a recursive relationship).
|
(it is a recursive relationship).
|
||||||
"""
|
"""
|
||||||
id = Column(BigInteger, Sequence('device_seq'), primary_key=True)
|
id = Column(BigInteger, Sequence('device_seq'), primary_key=True)
|
||||||
id.comment = """
|
id.comment = """The identifier of the device for this database. Used only
|
||||||
The identifier of the device for this database. Used only
|
internally for software; users should not use this.
|
||||||
internally for software; users should not use this.
|
|
||||||
"""
|
"""
|
||||||
type = Column(Unicode(STR_SM_SIZE), nullable=False)
|
type = Column(Unicode(STR_SM_SIZE), nullable=False)
|
||||||
hid = Column(Unicode(), check_lower('hid'), unique=True)
|
hid = Column(Unicode(), check_lower('hid'), unique=True)
|
||||||
hid.comment = """
|
hid.comment = """The Hardware ID (HID) is the unique ID traceability
|
||||||
The Hardware ID (HID) is the unique ID traceability systems
|
systems use to ID a device globally. This field is auto-generated
|
||||||
use to ID a device globally. This field is auto-generated
|
from Devicehub using literal identifiers from the device,
|
||||||
from Devicehub using literal identifiers from the device,
|
so it can re-generated *offline*.
|
||||||
so it can re-generated *offline*.
|
|
||||||
|
|
||||||
""" + HID_CONVERSION_DOC
|
""" + HID_CONVERSION_DOC
|
||||||
model = Column(Unicode, check_lower('model'))
|
model = Column(Unicode, check_lower('model'))
|
||||||
model.comment = """The model of the device in lower case.
|
model.comment = """The model of the device in lower case.
|
||||||
|
@ -84,21 +81,13 @@ class Device(Thing):
|
||||||
version = db.Column(db.CIText())
|
version = db.Column(db.CIText())
|
||||||
version.comment = """The version code this device, like v1 or A001."""
|
version.comment = """The version code this device, like v1 or A001."""
|
||||||
weight = Column(Float(decimal_return_scale=3), check_range('weight', 0.1, 5))
|
weight = Column(Float(decimal_return_scale=3), check_range('weight', 0.1, 5))
|
||||||
weight.comment = """
|
weight.comment = """The weight of the device."""
|
||||||
The weight of the device.
|
|
||||||
"""
|
|
||||||
width = Column(Float(decimal_return_scale=3), check_range('width', 0.1, 5))
|
width = Column(Float(decimal_return_scale=3), check_range('width', 0.1, 5))
|
||||||
width.comment = """
|
width.comment = """The width of the device in meters."""
|
||||||
The width of the device in meters.
|
|
||||||
"""
|
|
||||||
height = Column(Float(decimal_return_scale=3), check_range('height', 0.1, 5))
|
height = Column(Float(decimal_return_scale=3), check_range('height', 0.1, 5))
|
||||||
height.comment = """
|
height.comment = """The height of the device in meters."""
|
||||||
The height of the device in meters.
|
|
||||||
"""
|
|
||||||
depth = Column(Float(decimal_return_scale=3), check_range('depth', 0.1, 5))
|
depth = Column(Float(decimal_return_scale=3), check_range('depth', 0.1, 5))
|
||||||
depth.comment = """
|
depth.comment = """The depth of the device in meters."""
|
||||||
The depth of the device in meters.
|
|
||||||
"""
|
|
||||||
color = Column(ColorType)
|
color = Column(ColorType)
|
||||||
color.comment = """The predominant color of the device."""
|
color.comment = """The predominant color of the device."""
|
||||||
production_date = Column(db.DateTime)
|
production_date = Column(db.DateTime)
|
||||||
|
@ -146,8 +135,7 @@ class Device(Thing):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def actions(self) -> list:
|
def actions(self) -> list:
|
||||||
"""
|
"""All the actions where the device participated, including:
|
||||||
All the actions where the device participated, including:
|
|
||||||
|
|
||||||
1. Actions performed directly to the device.
|
1. Actions performed directly to the device.
|
||||||
2. Actions performed to a component.
|
2. Actions performed to a component.
|
||||||
|
@ -177,8 +165,7 @@ class Device(Thing):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def physical_properties(self) -> Dict[str, object or None]:
|
def physical_properties(self) -> Dict[str, object or None]:
|
||||||
"""
|
"""Fields that describe the physical properties of a device.
|
||||||
Fields that describe the physical properties of a device.
|
|
||||||
|
|
||||||
:return A generator where each value is a tuple with tho fields:
|
:return A generator where each value is a tuple with tho fields:
|
||||||
- Column.
|
- Column.
|
||||||
|
@ -266,8 +253,7 @@ class Device(Thing):
|
||||||
|
|
||||||
@declared_attr
|
@declared_attr
|
||||||
def __mapper_args__(cls):
|
def __mapper_args__(cls):
|
||||||
"""
|
"""Defines inheritance.
|
||||||
Defines inheritance.
|
|
||||||
|
|
||||||
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
From `the guide <http://docs.sqlalchemy.org/en/latest/orm/
|
||||||
extensions/declarative/api.html
|
extensions/declarative/api.html
|
||||||
|
@ -314,24 +300,20 @@ class Device(Thing):
|
||||||
class DisplayMixin:
|
class DisplayMixin:
|
||||||
"""Base class for the Display Component and the Monitor Device."""
|
"""Base class for the Display Component and the Monitor Device."""
|
||||||
size = Column(Float(decimal_return_scale=1), check_range('size', 2, 150), nullable=False)
|
size = Column(Float(decimal_return_scale=1), check_range('size', 2, 150), nullable=False)
|
||||||
size.comment = """
|
size.comment = """The size of the monitor in inches."""
|
||||||
The size of the monitor in inches.
|
|
||||||
"""
|
|
||||||
technology = Column(DBEnum(DisplayTech))
|
technology = Column(DBEnum(DisplayTech))
|
||||||
technology.comment = """
|
technology.comment = """The technology the monitor uses to display
|
||||||
The technology the monitor uses to display the image.
|
the image.
|
||||||
"""
|
"""
|
||||||
resolution_width = Column(SmallInteger, check_range('resolution_width', 10, 20000),
|
resolution_width = Column(SmallInteger, check_range('resolution_width', 10, 20000),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
resolution_width.comment = """
|
resolution_width.comment = """The maximum horizontal resolution the
|
||||||
The maximum horizontal resolution the monitor can natively support
|
monitor can natively support in pixels.
|
||||||
in pixels.
|
|
||||||
"""
|
"""
|
||||||
resolution_height = Column(SmallInteger, check_range('resolution_height', 10, 20000),
|
resolution_height = Column(SmallInteger, check_range('resolution_height', 10, 20000),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
resolution_height.comment = """
|
resolution_height.comment = """The maximum vertical resolution the
|
||||||
The maximum vertical resolution the monitor can natively support
|
monitor can natively support in pixels.
|
||||||
in pixels.
|
|
||||||
"""
|
"""
|
||||||
refresh_rate = Column(SmallInteger, check_range('refresh_rate', 10, 1000))
|
refresh_rate = Column(SmallInteger, check_range('refresh_rate', 10, 1000))
|
||||||
contrast_ratio = Column(SmallInteger, check_range('contrast_ratio', 100, 100000))
|
contrast_ratio = Column(SmallInteger, check_range('contrast_ratio', 100, 100000))
|
||||||
|
@ -467,7 +449,8 @@ class Desktop(Computer):
|
||||||
class Laptop(Computer):
|
class Laptop(Computer):
|
||||||
layout = Column(DBEnum(Layouts))
|
layout = Column(DBEnum(Layouts))
|
||||||
layout.comment = """Layout of a built-in keyboard of the computer,
|
layout.comment = """Layout of a built-in keyboard of the computer,
|
||||||
if any."""
|
if any.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Server(Computer):
|
class Server(Computer):
|
||||||
|
@ -549,11 +532,11 @@ class Component(Device):
|
||||||
)
|
)
|
||||||
|
|
||||||
def similar_one(self, parent: Computer, blacklist: Set[int]) -> 'Component':
|
def similar_one(self, parent: Computer, blacklist: Set[int]) -> 'Component':
|
||||||
"""
|
"""Gets a component that:
|
||||||
Gets a component that:
|
|
||||||
- has the same parent.
|
* has the same parent.
|
||||||
- Doesn't generate HID.
|
* Doesn't generate HID.
|
||||||
- Has same physical properties.
|
* Has same physical properties.
|
||||||
:param parent:
|
:param parent:
|
||||||
:param blacklist: A set of components to not to consider
|
:param blacklist: A set of components to not to consider
|
||||||
when looking for similar ones.
|
when looking for similar ones.
|
||||||
|
@ -580,17 +563,13 @@ class JoinedComponentTableMixin:
|
||||||
|
|
||||||
class GraphicCard(JoinedComponentTableMixin, Component):
|
class GraphicCard(JoinedComponentTableMixin, Component):
|
||||||
memory = Column(SmallInteger, check_range('memory', min=1, max=10000))
|
memory = Column(SmallInteger, check_range('memory', min=1, max=10000))
|
||||||
memory.comment = """
|
memory.comment = """The amount of memory of the Graphic Card in MB."""
|
||||||
The amount of memory of the Graphic Card in MB.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class DataStorage(JoinedComponentTableMixin, Component):
|
class DataStorage(JoinedComponentTableMixin, Component):
|
||||||
"""A device that stores information."""
|
"""A device that stores information."""
|
||||||
size = Column(Integer, check_range('size', min=1, max=10 ** 8))
|
size = Column(Integer, check_range('size', min=1, max=10 ** 8))
|
||||||
size.comment = """
|
size.comment = """The size of the data-storage in MB."""
|
||||||
The size of the data-storage in MB.
|
|
||||||
"""
|
|
||||||
interface = Column(DBEnum(DataStorageInterface))
|
interface = Column(DBEnum(DataStorageInterface))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -623,9 +602,7 @@ class SolidStateDrive(DataStorage):
|
||||||
|
|
||||||
class Motherboard(JoinedComponentTableMixin, Component):
|
class Motherboard(JoinedComponentTableMixin, Component):
|
||||||
slots = Column(SmallInteger, check_range('slots', min=0))
|
slots = Column(SmallInteger, check_range('slots', min=0))
|
||||||
slots.comment = """
|
slots.comment = """PCI slots the motherboard has."""
|
||||||
PCI slots the motherboard has.
|
|
||||||
"""
|
|
||||||
usb = Column(SmallInteger, check_range('usb', min=0))
|
usb = Column(SmallInteger, check_range('usb', min=0))
|
||||||
firewire = Column(SmallInteger, check_range('firewire', min=0))
|
firewire = Column(SmallInteger, check_range('firewire', min=0))
|
||||||
serial = Column(SmallInteger, check_range('serial', min=0))
|
serial = Column(SmallInteger, check_range('serial', min=0))
|
||||||
|
@ -638,13 +615,11 @@ class Motherboard(JoinedComponentTableMixin, Component):
|
||||||
|
|
||||||
class NetworkMixin:
|
class NetworkMixin:
|
||||||
speed = Column(SmallInteger, check_range('speed', min=10, max=10000))
|
speed = Column(SmallInteger, check_range('speed', min=10, max=10000))
|
||||||
speed.comment = """
|
speed.comment = """The maximum speed this network adapter can handle,
|
||||||
The maximum speed this network adapter can handle, in mbps.
|
in mbps.
|
||||||
"""
|
"""
|
||||||
wireless = Column(Boolean, nullable=False, default=False)
|
wireless = Column(Boolean, nullable=False, default=False)
|
||||||
wireless.comment = """
|
wireless.comment = """Whether it is a wireless interface."""
|
||||||
Whether it is a wireless interface.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __format__(self, format_spec):
|
def __format__(self, format_spec):
|
||||||
v = super().__format__(format_spec)
|
v = super().__format__(format_spec)
|
||||||
|
@ -685,8 +660,7 @@ class SoundCard(JoinedComponentTableMixin, Component):
|
||||||
|
|
||||||
|
|
||||||
class Display(JoinedComponentTableMixin, DisplayMixin, Component):
|
class Display(JoinedComponentTableMixin, DisplayMixin, Component):
|
||||||
"""
|
"""The display of a device. This is used in all devices that have
|
||||||
The display of a device. This is used in all devices that have
|
|
||||||
displays but that it is not their main part, like laptops,
|
displays but that it is not their main part, like laptops,
|
||||||
mobiles, smart-watches, and so on; excluding ``ComputerMonitor``
|
mobiles, smart-watches, and so on; excluding ``ComputerMonitor``
|
||||||
and ``TelevisionSet``.
|
and ``TelevisionSet``.
|
||||||
|
|
|
@ -20,8 +20,7 @@ class State(Enum):
|
||||||
|
|
||||||
|
|
||||||
class Trading(State):
|
class Trading(State):
|
||||||
"""
|
"""Trading states.
|
||||||
Trading states.
|
|
||||||
|
|
||||||
:cvar Reserved: The device has been reserved.
|
:cvar Reserved: The device has been reserved.
|
||||||
:cvar Cancelled: The device has been cancelled.
|
:cvar Cancelled: The device has been cancelled.
|
||||||
|
@ -44,8 +43,7 @@ class Trading(State):
|
||||||
|
|
||||||
|
|
||||||
class Physical(State):
|
class Physical(State):
|
||||||
"""
|
"""Physical states.
|
||||||
Physical states.
|
|
||||||
|
|
||||||
:cvar ToBeRepaired: The device has been selected for reparation.
|
:cvar ToBeRepaired: The device has been selected for reparation.
|
||||||
:cvar Repaired: The device has been repaired.
|
:cvar Repaired: The device has been repaired.
|
||||||
|
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 24 KiB |
|
@ -1,3 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC
|
||||||
"-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
"-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
<svg xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 46 46" version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;"><rect id="Photochromic-alone" serif:id="Photochromic alone" x="0" y="0" width="45.84" height="45.84" style="fill:none;"/><path d="M43.96,23.198c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z" style="fill:#fff;"/><clipPath id="_clip1"><path d="M43.96,23.198c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"/></clipPath><g clip-path="url(#_clip1)"><clipPath id="_clip2"><rect x="4.275" y="4.975" width="36.85" height="36.85"/></clipPath><g clip-path="url(#_clip2)"><g id="Artboard1"><rect x="4.275" y="4.975" width="36.71" height="36.701" style="fill:none;"/><g id="Logo-01"><path d="M6.749,38.053c4.02,-13.193 12.218,-7.047 23.011,-10.325c17.626,-5.353 -0.052,-29.186 -15.117,-16.725c-15.241,12.606 -3.74,38.876 19.597,23.634" style="fill:none;stroke:#bfff04;stroke-width:4.6px;"/><path d="M36.496,29.706l2.816,5.333l-6.526,4.015c0,0 -1.033,0.738 -1.598,-0.147c-0.565,-0.887 -1.553,-2.671 -2.028,-3.741c-0.364,-0.817 0.661,-1.324 0.661,-1.324l6.675,-4.136Z" style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-miterlimit:1.41421;"/><path d="M39.031,33.89l-2.012,-3.698l1.953,-1.153l2.012,3.699l-1.953,1.152Z" style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-miterlimit:1.41421;"/></g></g></g></g><path d="M43.96,23.198c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z" style="fill:none;stroke:#333;stroke-width:0.1px;stroke-linejoin:miter;stroke-miterlimit:11;"/></svg>
|
<svg xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 46 46"
|
||||||
|
version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve"
|
||||||
|
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;"><rect id="Photochromic-alone" serif:id="Photochromic alone" x="0" y="0" width="45.84" height="45.84" style="fill:none;"/>
|
||||||
|
<path d="M43.96,23.198c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"
|
||||||
|
style="fill:#fff;"/>
|
||||||
|
<clipPath id="_clip1"><path d="M43.96,23.198c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"/></clipPath>
|
||||||
|
<g clip-path="url(#_clip1)"><clipPath id="_clip2"><rect x="4.275" y="4.975" width="36.85" height="36.85"/></clipPath>
|
||||||
|
<g clip-path="url(#_clip2)"><g id="Artboard1"><rect x="4.275" y="4.975" width="36.71" height="36.701" style="fill:none;"/><g
|
||||||
|
id="Logo-01"><path d="M6.749,38.053c4.02,-13.193 12.218,-7.047 23.011,-10.325c17.626,-5.353 -0.052,-29.186 -15.117,-16.725c-15.241,12.606 -3.74,38.876 19.597,23.634" style="fill:none;stroke:#bfff04;stroke-width:4.6px;"/>
|
||||||
|
<path d="M36.496,29.706l2.816,5.333l-6.526,4.015c0,0 -1.033,0.738 -1.598,-0.147c-0.565,-0.887 -1.553,-2.671 -2.028,-3.741c-0.364,-0.817 0.661,-1.324 0.661,-1.324l6.675,-4.136Z"
|
||||||
|
style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-miterlimit:1.41421;"/>
|
||||||
|
<path d="M39.031,33.89l-2.012,-3.698l1.953,-1.153l2.012,3.699l-1.953,1.152Z"
|
||||||
|
style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-miterlimit:1.41421;"/></g></g></g></g>
|
||||||
|
<path d="M43.96,23.198c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"
|
||||||
|
style="fill:none;stroke:#333;stroke-width:0.1px;stroke-linejoin:miter;stroke-miterlimit:11;"/></svg>
|
||||||
|
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.4 KiB |
|
@ -1,3 +1,34 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC
|
||||||
"-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
"-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
<svg xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 124 44" version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-miterlimit:11;"><rect id="Photochromic-Tag-web" serif:id="Photochromic Tag web" x="0" y="0" width="123.413" height="43.733" style="fill:none;"/><clipPath id="_clip1"><rect x="0" y="0" width="123.413" height="43.733"/></clipPath><g clip-path="url(#_clip1)"><g><path d="M44.175,28.681l0,-4.925l24.55,0l0,-2.496l10.513,4.959l-10.513,4.958l0,-2.496l-24.55,0Z" style="fill:#333;stroke:#333;stroke-width:0.85px;"/><text x="49.508px" y="19.727px" style="font-family:'Lato-Regular', 'Lato', sans-serif;font-size:24.107px;fill:#231f20;">6s</text></g><path d="M42.52,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z" style="fill:#fff;"/><clipPath id="_clip2"><path d="M42.52,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"/></clipPath><g clip-path="url(#_clip2)"><clipPath id="_clip3"><rect x="2.835" y="2.835" width="36.85" height="36.85"/></clipPath><g clip-path="url(#_clip3)"><g id="Artboard1"><rect x="2.835" y="2.835" width="36.71" height="36.701" style="fill:none;"/><g id="Logo-01"><path d="M5.309,35.913c4.02,-13.194 12.218,-7.047 23.011,-10.325c17.626,-5.353 -0.052,-29.186 -15.117,-16.725c-15.241,12.605 -3.74,38.876 19.597,23.634" style="fill:none;stroke:#bfff04;stroke-width:4.6px;stroke-linejoin:round;stroke-miterlimit:10;"/><path d="M35.056,27.566l2.816,5.333l-6.526,4.014c0,0 -1.033,0.739 -1.598,-0.147c-0.565,-0.886 -1.553,-2.67 -2.028,-3.74c-0.364,-0.817 0.661,-1.324 0.661,-1.324l6.675,-4.136Z" style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.41421;"/><path d="M37.591,31.75l-2.012,-3.699l1.953,-1.152l2.012,3.699l-1.953,1.152Z" style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.41421;"/></g></g></g></g><path d="M42.52,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z" style="fill:none;stroke:#333;stroke-width:0.1px;"/><path d="M123.005,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z" style="fill:#fff;"/><clipPath id="_clip4"><path d="M123.005,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"/></clipPath><g clip-path="url(#_clip4)"><clipPath id="_clip5"><rect x="83.32" y="2.835" width="36.85" height="36.85"/></clipPath><g clip-path="url(#_clip5)"><g id="Artboard11" serif:id="Artboard1"><rect x="83.32" y="2.835" width="36.71" height="36.701" style="fill:none;"/><path d="M114.217,23.7c0.896,-2.192 1.142,-4.517 0.713,-6.743c-1.347,-6.998 -8.906,-11.33 -16.87,-9.668c-7.965,1.662 -13.337,8.693 -11.99,15.691c0.428,2.225 1.516,4.273 3.153,5.936l24.994,-5.216Z" style="fill:#e5f20d;"/><g id="Logo-011" serif:id="Logo-01"><path d="M85.795,35.92c4.019,-13.193 12.217,-7.047 23.01,-10.325c17.626,-5.353 -0.052,-29.186 -15.117,-16.725c-15.241,12.606 -3.74,38.876 19.597,23.634" style="fill:none;stroke:#bfff04;stroke-width:4.6px;stroke-linejoin:round;stroke-miterlimit:10;"/><path d="M115.541,27.573l2.816,5.333l-6.526,4.015c0,0 -1.033,0.738 -1.597,-0.147c-0.565,-0.887 -1.553,-2.671 -2.029,-3.741c-0.363,-0.817 0.661,-1.324 0.661,-1.324l6.675,-4.136Z" style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.41421;"/><path d="M118.077,31.757l-2.013,-3.698l1.953,-1.152l2.013,3.698l-1.953,1.152Z" style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.41421;"/></g></g></g></g><path d="M123.005,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z" style="fill:none;stroke:#333;stroke-width:0.1px;"/></g></svg>
|
<svg xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 124 44"
|
||||||
|
version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve"
|
||||||
|
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-miterlimit:11;"><rect id="Photochromic-Tag-web" serif:id="Photochromic Tag web" x="0" y="0" width="123.413" height="43.733" style="fill:none;"/>
|
||||||
|
<clipPath id="_clip1"><rect x="0" y="0" width="123.413" height="43.733"/></clipPath>
|
||||||
|
<g clip-path="url(#_clip1)"><g><path d="M44.175,28.681l0,-4.925l24.55,0l0,-2.496l10.513,4.959l-10.513,4.958l0,-2.496l-24.55,0Z" style="fill:#333;stroke:#333;stroke-width:0.85px;"/><text
|
||||||
|
x="49.508px" y="19.727px"
|
||||||
|
style="font-family:'Lato-Regular', 'Lato', sans-serif;font-size:24.107px;fill:#231f20;">6s</text></g>
|
||||||
|
<path d="M42.52,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"
|
||||||
|
style="fill:#fff;"/>
|
||||||
|
<clipPath id="_clip2"><path d="M42.52,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"/></clipPath>
|
||||||
|
<g clip-path="url(#_clip2)"><clipPath id="_clip3"><rect x="2.835" y="2.835" width="36.85" height="36.85"/></clipPath>
|
||||||
|
<g clip-path="url(#_clip3)"><g id="Artboard1"><rect x="2.835" y="2.835" width="36.71" height="36.701" style="fill:none;"/><g
|
||||||
|
id="Logo-01"><path d="M5.309,35.913c4.02,-13.194 12.218,-7.047 23.011,-10.325c17.626,-5.353 -0.052,-29.186 -15.117,-16.725c-15.241,12.605 -3.74,38.876 19.597,23.634" style="fill:none;stroke:#bfff04;stroke-width:4.6px;stroke-linejoin:round;stroke-miterlimit:10;"/>
|
||||||
|
<path d="M35.056,27.566l2.816,5.333l-6.526,4.014c0,0 -1.033,0.739 -1.598,-0.147c-0.565,-0.886 -1.553,-2.67 -2.028,-3.74c-0.364,-0.817 0.661,-1.324 0.661,-1.324l6.675,-4.136Z"
|
||||||
|
style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.41421;"/>
|
||||||
|
<path d="M37.591,31.75l-2.012,-3.699l1.953,-1.152l2.012,3.699l-1.953,1.152Z"
|
||||||
|
style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.41421;"/></g></g></g></g>
|
||||||
|
<path d="M42.52,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"
|
||||||
|
style="fill:none;stroke:#333;stroke-width:0.1px;"/>
|
||||||
|
<path d="M123.005,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"
|
||||||
|
style="fill:#fff;"/>
|
||||||
|
<clipPath id="_clip4"><path d="M123.005,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"/></clipPath>
|
||||||
|
<g clip-path="url(#_clip4)"><clipPath id="_clip5"><rect x="83.32" y="2.835" width="36.85" height="36.85"/></clipPath>
|
||||||
|
<g clip-path="url(#_clip5)"><g id="Artboard11" serif:id="Artboard1"><rect x="83.32" y="2.835" width="36.71" height="36.701" style="fill:none;"/><path
|
||||||
|
d="M114.217,23.7c0.896,-2.192 1.142,-4.517 0.713,-6.743c-1.347,-6.998 -8.906,-11.33 -16.87,-9.668c-7.965,1.662 -13.337,8.693 -11.99,15.691c0.428,2.225 1.516,4.273 3.153,5.936l24.994,-5.216Z"
|
||||||
|
style="fill:#e5f20d;"/><g id="Logo-011" serif:id="Logo-01"><path d="M85.795,35.92c4.019,-13.193 12.217,-7.047 23.01,-10.325c17.626,-5.353 -0.052,-29.186 -15.117,-16.725c-15.241,12.606 -3.74,38.876 19.597,23.634" style="fill:none;stroke:#bfff04;stroke-width:4.6px;stroke-linejoin:round;stroke-miterlimit:10;"/>
|
||||||
|
<path d="M115.541,27.573l2.816,5.333l-6.526,4.015c0,0 -1.033,0.738 -1.597,-0.147c-0.565,-0.887 -1.553,-2.671 -2.029,-3.741c-0.363,-0.817 0.661,-1.324 0.661,-1.324l6.675,-4.136Z"
|
||||||
|
style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.41421;"/>
|
||||||
|
<path d="M118.077,31.757l-2.013,-3.698l1.953,-1.152l2.013,3.698l-1.953,1.152Z"
|
||||||
|
style="fill:#bfff04;fill-rule:nonzero;stroke:#bfff04;stroke-width:0.17px;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:1.41421;"/></g></g></g></g>
|
||||||
|
<path d="M123.005,21.058c0,-11.622 -9.436,-21.058 -21.058,-21.058l-0.404,0c-11.622,0 -21.058,9.436 -21.058,21.058l0,0.404c0,11.622 9.436,21.058 21.058,21.058l0.404,0c11.622,0 21.058,-9.436 21.058,-21.058l0,-0.404Z"
|
||||||
|
style="fill:none;stroke:#333;stroke-width:0.1px;"/></g></svg>
|
||||||
|
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.1 KiB |
|
@ -23,8 +23,7 @@ class Sync:
|
||||||
def run(self,
|
def run(self,
|
||||||
device: Device,
|
device: Device,
|
||||||
components: Iterable[Component] or None) -> (Device, OrderedSet):
|
components: Iterable[Component] or None) -> (Device, OrderedSet):
|
||||||
"""
|
"""Synchronizes the device and components with the database.
|
||||||
Synchronizes the device and components with the database.
|
|
||||||
|
|
||||||
Identifies if the device and components exist in the database
|
Identifies if the device and components exist in the database
|
||||||
and updates / inserts them as necessary.
|
and updates / inserts them as necessary.
|
||||||
|
@ -79,8 +78,7 @@ class Sync:
|
||||||
component: Component,
|
component: Component,
|
||||||
blacklist: Set[int],
|
blacklist: Set[int],
|
||||||
parent: Computer):
|
parent: Computer):
|
||||||
"""
|
"""Synchronizes one component to the DB.
|
||||||
Synchronizes one component to the DB.
|
|
||||||
|
|
||||||
This method is a specialization of :meth:`.execute_register`
|
This method is a specialization of :meth:`.execute_register`
|
||||||
but for components that are inside parents.
|
but for components that are inside parents.
|
||||||
|
@ -125,8 +123,7 @@ class Sync:
|
||||||
return db_component, is_new
|
return db_component, is_new
|
||||||
|
|
||||||
def execute_register(self, device: Device) -> Device:
|
def execute_register(self, device: Device) -> Device:
|
||||||
"""
|
"""Synchronizes one device to the DB.
|
||||||
Synchronizes one device to the DB.
|
|
||||||
|
|
||||||
This method tries to get an existing device using the HID
|
This method tries to get an existing device using the HID
|
||||||
or one of the tags, and...
|
or one of the tags, and...
|
||||||
|
@ -205,8 +202,7 @@ class Sync:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def merge(device: Device, db_device: Device):
|
def merge(device: Device, db_device: Device):
|
||||||
"""
|
"""Copies the physical properties of the device to the db_device.
|
||||||
Copies the physical properties of the device to the db_device.
|
|
||||||
|
|
||||||
This method mutates db_device.
|
This method mutates db_device.
|
||||||
"""
|
"""
|
||||||
|
@ -217,8 +213,7 @@ class Sync:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_remove(device: Computer,
|
def add_remove(device: Computer,
|
||||||
components: Set[Component]) -> OrderedSet:
|
components: Set[Component]) -> OrderedSet:
|
||||||
"""
|
"""Generates the Add and Remove actions (but doesn't add them to
|
||||||
Generates the Add and Remove actions (but doesn't add them to
|
|
||||||
session).
|
session).
|
||||||
|
|
||||||
:param device: A device which ``components`` attribute contains
|
:param device: A device which ``components`` attribute contains
|
||||||
|
|
|
@ -2,222 +2,222 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<link href="https://stackpath.bootstrapcdn.com/bootswatch/3.3.7/flatly/bootstrap.min.css"
|
<link href="https://stackpath.bootstrapcdn.com/bootswatch/3.3.7/flatly/bootstrap.min.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
integrity="sha384-+ENW/yibaokMnme+vBLnHMphUYxHs34h9lpdbSLuAwGkOKFRl4C34WkjazBtb7eT"
|
integrity="sha384-+ENW/yibaokMnme+vBLnHMphUYxHs34h9lpdbSLuAwGkOKFRl4C34WkjazBtb7eT"
|
||||||
crossorigin="anonymous">
|
crossorigin="anonymous">
|
||||||
<title>Devicehub | {{ device.__format__('t') }}</title>
|
<title>Devicehub | {{ device.__format__('t') }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-default" style="background-color: gainsboro; margin: 0 !important">
|
<nav class="navbar navbar-default" style="background-color: gainsboro; margin: 0 !important">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a href="https://www.ereuse.org/" target="_blank">
|
<a href="https://www.ereuse.org/" target="_blank">
|
||||||
<img alt="Brand"
|
<img alt="Brand"
|
||||||
class="center-block"
|
class="center-block"
|
||||||
style="height: 4em; padding-bottom: 0.1em"
|
style="height: 4em; padding-bottom: 0.1em"
|
||||||
src="{{ url_for('Device.static', filename='ereuse-logo.svg') }}">
|
src="{{ url_for('Device.static', filename='ereuse-logo.svg') }}">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="jumbotron">
|
<div class="jumbotron">
|
||||||
<img class="center-block"
|
<img class="center-block"
|
||||||
style="height: 13em; padding-bottom: 0.1em"
|
style="height: 13em; padding-bottom: 0.1em"
|
||||||
src="{{ url_for('Device.static', filename='magrama.svg') }}">
|
src="{{ url_for('Device.static', filename='magrama.svg') }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>{{ device.__format__('t') }}<br>
|
<h1>{{ device.__format__('t') }}<br>
|
||||||
<small>{{ device.__format__('s') }}</small>
|
<small>{{ device.__format__('s') }}</small>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class='text-center'>
|
<h2 class='text-center'>
|
||||||
This is your {{ device.t }}.
|
This is your {{ device.t }}.
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p class="text-center">
|
<p class="text-center">
|
||||||
{% if device.trading %}
|
{% if device.trading %}
|
||||||
{{ device.trading }}
|
{{ device.trading }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.trading and device.physical %}
|
{% if device.trading and device.physical %}
|
||||||
and
|
and
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.physical %}
|
{% if device.physical %}
|
||||||
{{ device.physical }}
|
{{ device.physical }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<article class="col-md-6">
|
<article class="col-md-6">
|
||||||
<h3>You can verify the originality of your device.</h3>
|
<h3>You can verify the originality of your device.</h3>
|
||||||
<p>
|
<p>
|
||||||
If your device comes with the following tag
|
If your device comes with the following tag
|
||||||
<img class="img-responsive center-block" style="width: 12em;"
|
<img class="img-responsive center-block" style="width: 12em;"
|
||||||
src="{{ url_for('Device.static', filename='photochromic-alone.svg') }}">
|
src="{{ url_for('Device.static', filename='photochromic-alone.svg') }}">
|
||||||
it means it has been refurbished by an eReuse.org
|
it means it has been refurbished by an eReuse.org
|
||||||
certified organization.
|
certified organization.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The tag is special –illuminate it with the torch of
|
The tag is special –illuminate it with the torch of
|
||||||
your phone for 6 seconds and it will react like in
|
your phone for 6 seconds and it will react like in
|
||||||
the following image:
|
the following image:
|
||||||
<img class="img-responsive center-block" style="width: 30em;"
|
<img class="img-responsive center-block" style="width: 30em;"
|
||||||
src="{{ url_for('Device.static', filename='photochromic-tag-web.svg') }}">
|
src="{{ url_for('Device.static', filename='photochromic-tag-web.svg') }}">
|
||||||
This is proof that this device is genuine.
|
This is proof that this device is genuine.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
<article class="col-md-6">
|
<article class="col-md-6">
|
||||||
<h3>These are the specifications</h3>
|
<h3>These are the specifications</h3>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>Range</th>
|
<th>Range</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% if device.processor_model %}
|
{% if device.processor_model %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
CPU – {{ device.processor_model }}
|
CPU – {{ device.processor_model }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if device.rate %}
|
{% if device.rate %}
|
||||||
{{ device.rate.processor_range }}
|
{{ device.rate.processor_range }}
|
||||||
({{ device.rate.processor }})
|
({{ device.rate.processor }})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.ram_size %}
|
{% if device.ram_size %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
RAM – {{ device.ram_size // 1000 }} GB
|
RAM – {{ device.ram_size // 1000 }} GB
|
||||||
{{ macros.component_type(device.components, 'RamModule') }}
|
{{ macros.component_type(device.components, 'RamModule') }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if device.rate %}
|
{% if device.rate %}
|
||||||
{{ device.rate.ram_range }}
|
{{ device.rate.ram_range }}
|
||||||
({{ device.rate.ram }})
|
({{ device.rate.ram }})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.data_storage_size %}
|
{% if device.data_storage_size %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Data Storage – {{ device.data_storage_size // 1000 }} GB
|
Data Storage – {{ device.data_storage_size // 1000 }} GB
|
||||||
{{ macros.component_type(device.components, 'SolidStateDrive') }}
|
{{ macros.component_type(device.components, 'SolidStateDrive') }}
|
||||||
{{ macros.component_type(device.components, 'HardDrive') }}
|
{{ macros.component_type(device.components, 'HardDrive') }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if device.rate %}
|
{% if device.rate %}
|
||||||
{{ device.rate.data_storage_range }}
|
{{ device.rate.data_storage_range }}
|
||||||
({{ device.rate.data_storage }})
|
({{ device.rate.data_storage }})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.graphic_card_model %}
|
{% if device.graphic_card_model %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Graphics – {{ device.graphic_card_model }}
|
Graphics – {{ device.graphic_card_model }}
|
||||||
{{ macros.component_type(device.components, 'GraphicCard') }}
|
{{ macros.component_type(device.components, 'GraphicCard') }}
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.network_speeds %}
|
{% if device.network_speeds %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
Network –
|
Network –
|
||||||
{% if device.network_speeds[0] %}
|
{% if device.network_speeds[0] %}
|
||||||
Ethernet
|
Ethernet
|
||||||
{% if device.network_speeds[0] != None %}
|
{% if device.network_speeds[0] != None %}
|
||||||
max. {{ device.network_speeds[0] }} Mbps
|
max. {{ device.network_speeds[0] }} Mbps
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.network_speeds[0] and device.network_speeds[1] %}
|
{% if device.network_speeds[0] and device.network_speeds[1] %}
|
||||||
+
|
+
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.network_speeds[1] %}
|
{% if device.network_speeds[1] %}
|
||||||
WiFi
|
WiFi
|
||||||
{% if device.network_speeds[1] != None %}
|
{% if device.network_speeds[1] != None %}
|
||||||
max. {{ device.network_speeds[1] }} Mbps
|
max. {{ device.network_speeds[1] }} Mbps
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ macros.component_type(device.components, 'NetworkAdapter') }}
|
{{ macros.component_type(device.components, 'NetworkAdapter') }}
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.rate %}
|
{% if device.rate %}
|
||||||
<tr class="active">
|
<tr class="active">
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
Total rate
|
Total rate
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ device.rate.rating_range }}
|
{{ device.rate.rating_range }}
|
||||||
({{ device.rate.rating }})
|
({{ device.rate.rating }})
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.rate and device.rate.price %}
|
{% if device.rate and device.rate.price %}
|
||||||
<tr class="active">
|
<tr class="active">
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
Algorithm price
|
Algorithm price
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ device.rate.price }}
|
{{ device.rate.price }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if device.price %}
|
{% if device.price %}
|
||||||
<tr class="active">
|
<tr class="active">
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
Actual price
|
Actual price
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ device.price }}
|
{{ device.price }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
<h3>This is the traceability log of your device</h3>
|
|
||||||
<div class="text-right">
|
|
||||||
<small>Latest one.</small>
|
|
||||||
</div>
|
|
||||||
<ol>
|
|
||||||
{% for action in device.actions|reverse %}
|
|
||||||
<li>
|
|
||||||
<strong>
|
|
||||||
{{ action.type }}
|
|
||||||
</strong>
|
|
||||||
—
|
|
||||||
{{ action }}
|
|
||||||
<br>
|
|
||||||
<div class="text-muted">
|
|
||||||
<small>
|
|
||||||
{{ action._date_str }}
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
{% if action.certificate %}
|
<h3>This is the traceability log of your device</h3>
|
||||||
<a href="{{ action.certificate.to_text() }}">See the certificate</a>
|
<div class="text-right">
|
||||||
{% endif %}
|
<small>Latest one.</small>
|
||||||
</li>
|
</div>
|
||||||
{% endfor %}
|
<ol>
|
||||||
</ol>
|
{% for action in device.actions|reverse %}
|
||||||
<div class="text-right">
|
<li>
|
||||||
<small>Oldest one.</small>
|
<strong>
|
||||||
</div>
|
{{ action.type }}
|
||||||
</article>
|
</strong>
|
||||||
</div>
|
—
|
||||||
|
{{ action }}
|
||||||
|
<br>
|
||||||
|
<div class="text-muted">
|
||||||
|
<small>
|
||||||
|
{{ action._date_str }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
{% if action.certificate %}
|
||||||
|
<a href="{{ action.certificate.to_text() }}">See the certificate</a>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
<div class="text-right">
|
||||||
|
<small>Oldest one.</small>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
{% macro component_type(components, type) %}
|
{% macro component_type(components, type) %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for c in components if c.t == type %}
|
{% for c in components if c.t == type %}
|
||||||
<li>
|
<li>
|
||||||
{{ c.__format__('t') }}
|
{{ c.__format__('t') }}
|
||||||
<p>
|
<p>
|
||||||
<small class="text-muted">{{ c.__format__('s') }}</small>
|
<small class="text-muted">{{ c.__format__('s') }}</small>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro rate(range) %}
|
{% macro rate(range) %}
|
||||||
<span class="label label-primary">
|
<span class="label label-primary">
|
||||||
{{ range }}
|
{{ range }}
|
||||||
</span>
|
</span>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
|
@ -78,8 +78,7 @@ class DeviceView(View):
|
||||||
page = f.Integer(validate=v.Range(min=1), missing=1)
|
page = f.Integer(validate=v.Range(min=1), missing=1)
|
||||||
|
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""
|
"""Devices view
|
||||||
Devices view
|
|
||||||
---
|
---
|
||||||
description: Gets a device or multiple devices.
|
description: Gets a device or multiple devices.
|
||||||
parameters:
|
parameters:
|
||||||
|
|
|
@ -56,9 +56,7 @@ class DeviceRow(OrderedDict):
|
||||||
self.components()
|
self.components()
|
||||||
|
|
||||||
def components(self):
|
def components(self):
|
||||||
"""
|
"""Function to get all components information of a device."""
|
||||||
Function to get all components information of a device
|
|
||||||
"""
|
|
||||||
assert isinstance(self.device, d.Computer)
|
assert isinstance(self.device, d.Computer)
|
||||||
# todo put an input specific order (non alphabetic) & where are a list of types components
|
# 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
|
for type in sorted(current_app.resources[d.Component.t].subresources_types): # type: str
|
||||||
|
@ -75,8 +73,8 @@ class DeviceRow(OrderedDict):
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
def fill_component(self, type, i, component=None):
|
def fill_component(self, type, i, component=None):
|
||||||
"""
|
"""Function to put specific information of components
|
||||||
Function to put specific information of components in OrderedDict (csv)
|
in OrderedDict (csv)
|
||||||
:param type: type of component
|
:param type: type of component
|
||||||
:param component: device.components
|
:param component: device.components
|
||||||
"""
|
"""
|
||||||
|
@ -85,11 +83,13 @@ class DeviceRow(OrderedDict):
|
||||||
self['{} {} Model'.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 ''
|
self['{} {} Serial Number'.format(type, i)] = component.serial_number if component else ''
|
||||||
|
|
||||||
""" Particular fields for component GraphicCard """
|
"""Particular fields for component GraphicCard."""
|
||||||
if isinstance(component, d.GraphicCard):
|
if isinstance(component, d.GraphicCard):
|
||||||
self['{} {} Memory (MB)'.format(type, i)] = component.memory
|
self['{} {} Memory (MB)'.format(type, i)] = component.memory
|
||||||
|
|
||||||
""" Particular fields for component DataStorage.t -> (HardDrive, SolidStateDrive) """
|
"""Particular fields for component DataStorage.t ->
|
||||||
|
(HardDrive, SolidStateDrive)
|
||||||
|
"""
|
||||||
if isinstance(component, d.DataStorage):
|
if isinstance(component, d.DataStorage):
|
||||||
self['{} {} Size (MB)'.format(type, i)] = component.size
|
self['{} {} Size (MB)'.format(type, i)] = component.size
|
||||||
self['{} {} Privacy'.format(type, i)] = component.privacy
|
self['{} {} Privacy'.format(type, i)] = component.privacy
|
||||||
|
@ -109,16 +109,14 @@ class DeviceRow(OrderedDict):
|
||||||
except:
|
except:
|
||||||
self['{} {} Writing speed'.format(type, i)] = ''
|
self['{} {} Writing speed'.format(type, i)] = ''
|
||||||
|
|
||||||
""" Particular fields for component Processor """
|
"""Particular fields for component Processor."""
|
||||||
if isinstance(component, d.Processor):
|
if isinstance(component, d.Processor):
|
||||||
self['{} {} Number of cores'.format(type, i)] = component.cores
|
self['{} {} Number of cores'.format(type, i)] = component.cores
|
||||||
self['{} {} Speed (GHz)'.format(type, i)] = component.speed
|
self['{} {} Speed (GHz)'.format(type, i)] = component.speed
|
||||||
|
|
||||||
""" Particular fields for component RamModule """
|
"""Particular fields for component RamModule."""
|
||||||
if isinstance(component, d.RamModule):
|
if isinstance(component, d.RamModule):
|
||||||
self['{} {} Size (MB)'.format(type, i)] = component.size
|
self['{} {} Size (MB)'.format(type, i)] = component.size
|
||||||
self['{} {} Speed (MHz)'.format(type, i)] = component.speed
|
self['{} {} Speed (MHz)'.format(type, i)] = component.speed
|
||||||
|
|
||||||
# todo add Display size, ...
|
# todo add Display, NetworkAdapter, etc...
|
||||||
# todo add NetworkAdapter speedLink?
|
|
||||||
# todo add some ComputerAccessories
|
|
||||||
|
|
|
@ -110,11 +110,7 @@ class DevicesDocumentView(DeviceView):
|
||||||
return self.generate_post_csv(query)
|
return self.generate_post_csv(query)
|
||||||
|
|
||||||
def generate_post_csv(self, query):
|
def generate_post_csv(self, query):
|
||||||
"""
|
"""Get device query and put information in csv format."""
|
||||||
Get device query and put information in csv format
|
|
||||||
:param query:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
data = StringIO()
|
data = StringIO()
|
||||||
cw = csv.writer(data)
|
cw = csv.writer(data)
|
||||||
first = True
|
first = True
|
||||||
|
|
|
@ -1,95 +1,95 @@
|
||||||
{% extends "documents/layout.html" %}
|
{% extends "documents/layout.html" %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div>
|
<div>
|
||||||
<h2>Resumé</h2>
|
<h2>Resumé</h2>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>S/N</th>
|
<th>S/N</th>
|
||||||
<th>Tags</th>
|
<th>Tags</th>
|
||||||
<th>S/N Data Storage</th>
|
<th>S/N Data Storage</th>
|
||||||
<th>Type of erasure</th>
|
<th>Type of erasure</th>
|
||||||
<th>Result</th>
|
<th>Result</th>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for erasure in erasures %}
|
{% for erasure in erasures %}
|
||||||
<tr>
|
<tr>
|
||||||
{% if erasure.parent.serial_number %}
|
{% if erasure.parent.serial_number %}
|
||||||
<td>
|
<td>
|
||||||
{{ erasure.parent.serial_number.upper() }}
|
{{ erasure.parent.serial_number.upper() }}
|
||||||
</td>
|
</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td></td>
|
<td></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td>
|
<td>
|
||||||
{{ erasure.parent.tags.__format__('') }}
|
{{ erasure.parent.tags.__format__('') }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ erasure.device.serial_number.upper() }}
|
{{ erasure.device.serial_number.upper() }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ erasure.type }}
|
{{ erasure.type }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ erasure.severity }}
|
{{ erasure.severity }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ erasure.date_str }}
|
{{ erasure.date_str }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-break row">
|
<div class="page-break row">
|
||||||
<h2>Details</h2>
|
<h2>Details</h2>
|
||||||
{% for erasure in erasures %}
|
{% for erasure in erasures %}
|
||||||
<div class="col-md-6 no-page-break">
|
<div class="col-md-6 no-page-break">
|
||||||
<h4>{{ erasure.device.__format__('t') }}</h4>
|
<h4>{{ erasure.device.__format__('t') }}</h4>
|
||||||
|
<dl>
|
||||||
|
<dt>Data storage:</dt>
|
||||||
|
<dd>{{ erasure.device.__format__('ts') }}</dd>
|
||||||
|
<dt>Computer:</dt>
|
||||||
|
<dd>{{ erasure.parent.__format__('ts') }}</dd>
|
||||||
|
<dt>Tags:</dt>
|
||||||
|
<dd>{{ erasure.parent.tags }}</dd>
|
||||||
|
<dt>Erasure:</dt>
|
||||||
|
<dd>{{ erasure.__format__('ts') }}</dd>
|
||||||
|
{% if erasure.steps %}
|
||||||
|
<dt>Erasure steps:</dt>
|
||||||
|
<dd>
|
||||||
|
<ol>
|
||||||
|
{% for step in erasure.steps %}
|
||||||
|
<li>{{ step.__format__('') }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</dd>
|
||||||
|
{% endif %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="no-page-break">
|
||||||
|
<h2>Glossary</h2>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Data storage:</dt>
|
<dt>Erase Basic</dt>
|
||||||
<dd>{{ erasure.device.__format__('ts') }}</dd>
|
|
||||||
<dt>Computer:</dt>
|
|
||||||
<dd>{{ erasure.parent.__format__('ts') }}</dd>
|
|
||||||
<dt>Tags:</dt>
|
|
||||||
<dd>{{ erasure.parent.tags }}</dd>
|
|
||||||
<dt>Erasure:</dt>
|
|
||||||
<dd>{{ erasure.__format__('ts') }}</dd>
|
|
||||||
{% if erasure.steps %}
|
|
||||||
<dt>Erasure steps:</dt>
|
|
||||||
<dd>
|
<dd>
|
||||||
<ol>
|
A software-based fast non-100%-secured way of erasing data storage,
|
||||||
{% for step in erasure.steps %}
|
using <a href="https://en.wikipedia.org/wiki/Shred_(Unix)">shred</a>.
|
||||||
<li>{{ step.__format__('') }}</li>
|
</dd>
|
||||||
{% endfor %}
|
<dt>Erase Sectors</dt>
|
||||||
</ol>
|
<dd>
|
||||||
|
A secured-way of erasing data storages, checking sector-by-sector
|
||||||
|
the erasure, using <a href="https://en.wikipedia.org/wiki/Badblocks">badblocks</a>.
|
||||||
</dd>
|
</dd>
|
||||||
{% endif %}
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
<div class="no-print">
|
||||||
</div>
|
<a href="{{ url_pdf }}">Click here to download the PDF.</a>
|
||||||
<div class="no-page-break">
|
</div>
|
||||||
<h2>Glossary</h2>
|
<div class="print-only">
|
||||||
<dl>
|
<a href="{{ url_web }}">Verify on-line the integrity of this document</a>
|
||||||
<dt>Erase Basic</dt>
|
</div>
|
||||||
<dd>
|
|
||||||
A software-based fast non-100%-secured way of erasing data storage,
|
|
||||||
using <a href="https://en.wikipedia.org/wiki/Shred_(Unix)">shred</a>.
|
|
||||||
</dd>
|
|
||||||
<dt>Erase Sectors</dt>
|
|
||||||
<dd>
|
|
||||||
A secured-way of erasing data storages, checking sector-by-sector
|
|
||||||
the erasure, using <a href="https://en.wikipedia.org/wiki/Badblocks">badblocks</a>.
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
<div class="no-print">
|
|
||||||
<a href="{{ url_pdf }}">Click here to download the PDF.</a>
|
|
||||||
</div>
|
|
||||||
<div class="print-only">
|
|
||||||
<a href="{{ url_web }}">Verify on-line the integrity of this document</a>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,25 +2,25 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<link href="https://stackpath.bootstrapcdn.com/bootswatch/3.3.7/flatly/bootstrap.min.css"
|
<link href="https://stackpath.bootstrapcdn.com/bootswatch/3.3.7/flatly/bootstrap.min.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
integrity="sha384-+ENW/yibaokMnme+vBLnHMphUYxHs34h9lpdbSLuAwGkOKFRl4C34WkjazBtb7eT"
|
integrity="sha384-+ENW/yibaokMnme+vBLnHMphUYxHs34h9lpdbSLuAwGkOKFRl4C34WkjazBtb7eT"
|
||||||
crossorigin="anonymous">
|
crossorigin="anonymous">
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
href="{{ url_for('Document.static', filename='print.css') }}">
|
href="{{ url_for('Document.static', filename='print.css') }}">
|
||||||
<title>Devicehub | {{ title }}</title>
|
<title>Devicehub | {{ title }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<header class="page-header">
|
<header class="page-header">
|
||||||
<h1> {{ title }}</h1>
|
<h1> {{ title }}</h1>
|
||||||
</header>
|
</header>
|
||||||
</div>
|
</div>
|
||||||
{% block body %}{% endblock %}
|
{% block body %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -24,16 +24,13 @@ class Lot(Thing):
|
||||||
description = db.Column(CIText())
|
description = db.Column(CIText())
|
||||||
description.comment = """A comment about the lot."""
|
description.comment = """A comment about the lot."""
|
||||||
closed = db.Column(db.Boolean, default=False, nullable=False)
|
closed = db.Column(db.Boolean, default=False, nullable=False)
|
||||||
closed.comment = """
|
closed.comment = """A closed lot cannot be modified anymore."""
|
||||||
A closed lot cannot be modified anymore.
|
|
||||||
"""
|
|
||||||
devices = db.relationship(Device,
|
devices = db.relationship(Device,
|
||||||
backref=db.backref('lots', lazy=True, collection_class=set),
|
backref=db.backref('lots', lazy=True, collection_class=set),
|
||||||
secondary=lambda: LotDevice.__table__,
|
secondary=lambda: LotDevice.__table__,
|
||||||
lazy=True,
|
lazy=True,
|
||||||
collection_class=set)
|
collection_class=set)
|
||||||
"""
|
"""The **children** devices that the lot has.
|
||||||
The **children** devices that the lot has.
|
|
||||||
|
|
||||||
Note that the lot can have more devices, if they are inside
|
Note that the lot can have more devices, if they are inside
|
||||||
descendant lots.
|
descendant lots.
|
||||||
|
@ -67,8 +64,7 @@ class Lot(Thing):
|
||||||
|
|
||||||
def __init__(self, name: str, closed: bool = closed.default.arg,
|
def __init__(self, name: str, closed: bool = closed.default.arg,
|
||||||
description: str = None) -> None:
|
description: str = None) -> None:
|
||||||
"""
|
"""Initializes a lot
|
||||||
Initializes a lot
|
|
||||||
:param name:
|
:param name:
|
||||||
:param closed:
|
:param closed:
|
||||||
"""
|
"""
|
||||||
|
@ -173,9 +169,7 @@ class LotDevice(db.Model):
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.id)
|
default=lambda: g.user.id)
|
||||||
author = db.relationship(User, primaryjoin=author_id == User.id)
|
author = db.relationship(User, primaryjoin=author_id == User.id)
|
||||||
author_id.comment = """
|
author_id.comment = """The user that put the device in the lot."""
|
||||||
The user that put the device in the lot.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Path(db.Model):
|
class Path(db.Model):
|
||||||
|
@ -191,9 +185,7 @@ class Path(db.Model):
|
||||||
primaryjoin=Lot.id == lot_id)
|
primaryjoin=Lot.id == lot_id)
|
||||||
path = db.Column(LtreeType, nullable=False)
|
path = db.Column(LtreeType, nullable=False)
|
||||||
created = db.Column(db.TIMESTAMP(timezone=True), server_default=db.text('CURRENT_TIMESTAMP'))
|
created = db.Column(db.TIMESTAMP(timezone=True), server_default=db.text('CURRENT_TIMESTAMP'))
|
||||||
created.comment = """
|
created.comment = """When Devicehub created this."""
|
||||||
When Devicehub created this.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
# dag.delete_edge needs to disable internally/temporarily the unique constraint
|
# dag.delete_edge needs to disable internally/temporarily the unique constraint
|
||||||
|
|
|
@ -23,8 +23,7 @@ class LotFormat(Enum):
|
||||||
|
|
||||||
class LotView(View):
|
class LotView(View):
|
||||||
class FindArgs(MarshmallowSchema):
|
class FindArgs(MarshmallowSchema):
|
||||||
"""
|
"""Allowed arguments for the ``find``
|
||||||
Allowed arguments for the ``find``
|
|
||||||
method (GET collection) endpoint
|
method (GET collection) endpoint
|
||||||
"""
|
"""
|
||||||
format = EnumField(LotFormat, missing=None)
|
format = EnumField(LotFormat, missing=None)
|
||||||
|
@ -56,8 +55,7 @@ class LotView(View):
|
||||||
|
|
||||||
@teal.cache.cache(datetime.timedelta(minutes=5))
|
@teal.cache.cache(datetime.timedelta(minutes=5))
|
||||||
def find(self, args: dict):
|
def find(self, args: dict):
|
||||||
"""
|
"""Gets lots.
|
||||||
Gets lots.
|
|
||||||
|
|
||||||
By passing the value `UiTree` in the parameter `format`
|
By passing the value `UiTree` in the parameter `format`
|
||||||
of the query you get a recursive nested suited for ui-tree::
|
of the query you get a recursive nested suited for ui-tree::
|
||||||
|
|
|
@ -20,16 +20,14 @@ class Thing(db.Model):
|
||||||
nullable=False,
|
nullable=False,
|
||||||
index=True,
|
index=True,
|
||||||
server_default=db.text('CURRENT_TIMESTAMP'))
|
server_default=db.text('CURRENT_TIMESTAMP'))
|
||||||
updated.comment = """
|
updated.comment = """The last time Devicehub recorded a change for
|
||||||
The last time Devicehub recorded a change for this thing.
|
this thing.
|
||||||
"""
|
"""
|
||||||
created = db.Column(db.TIMESTAMP(timezone=True),
|
created = db.Column(db.TIMESTAMP(timezone=True),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
index=True,
|
index=True,
|
||||||
server_default=db.text('CURRENT_TIMESTAMP'))
|
server_default=db.text('CURRENT_TIMESTAMP'))
|
||||||
created.comment = """
|
created.comment = """When Devicehub created this."""
|
||||||
When Devicehub created this.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
# We need to set 'created' before sqlalchemy inits the class
|
# We need to set 'created' before sqlalchemy inits the class
|
||||||
|
|
|
@ -39,8 +39,8 @@ class Tag(Thing):
|
||||||
collection_class=set)
|
collection_class=set)
|
||||||
"""The organization that issued the tag."""
|
"""The organization that issued the tag."""
|
||||||
provider = Column(URL())
|
provider = Column(URL())
|
||||||
provider.comment = """
|
provider.comment = """The tag provider URL. If None, the provider is
|
||||||
The tag provider URL. If None, the provider is this Devicehub.
|
this Devicehub.
|
||||||
"""
|
"""
|
||||||
device_id = Column(BigInteger,
|
device_id = Column(BigInteger,
|
||||||
# We don't want to delete the tag on device deletion, only set to null
|
# We don't want to delete the tag on device deletion, only set to null
|
||||||
|
@ -50,9 +50,8 @@ class Tag(Thing):
|
||||||
primaryjoin=Device.id == device_id)
|
primaryjoin=Device.id == device_id)
|
||||||
"""The device linked to this tag."""
|
"""The device linked to this tag."""
|
||||||
secondary = Column(db.CIText(), index=True)
|
secondary = Column(db.CIText(), index=True)
|
||||||
secondary.comment = """
|
secondary.comment = """A secondary identifier for this tag.
|
||||||
A secondary identifier for this tag. It has the same
|
It has the same constraints as the main one. Only needed in special cases.
|
||||||
constraints as the main one. Only needed in special cases.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
|
@ -116,7 +115,7 @@ class Tag(Thing):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_printable_q(cls):
|
def is_printable_q(cls):
|
||||||
"""Return a SQLAlchemy filter expression for printable queries"""
|
"""Return a SQLAlchemy filter expression for printable queries."""
|
||||||
return cls.org_id == Organization.get_default_org_id()
|
return cls.org_id == Organization.get_default_org_id()
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
|
|
@ -50,7 +50,7 @@ class TagView(View):
|
||||||
|
|
||||||
|
|
||||||
class TagDeviceView(View):
|
class TagDeviceView(View):
|
||||||
"""Endpoints to work with the device of the tag; /tags/23/device"""
|
"""Endpoints to work with the device of the tag; /tags/23/device."""
|
||||||
|
|
||||||
def one(self, id):
|
def one(self, id):
|
||||||
"""Gets the device from the tag."""
|
"""Gets the device from the tag."""
|
||||||
|
@ -78,8 +78,7 @@ class TagDeviceView(View):
|
||||||
|
|
||||||
|
|
||||||
def get_device_from_tag(id: str):
|
def get_device_from_tag(id: str):
|
||||||
"""
|
"""Gets the device by passing a tag id.
|
||||||
Gets the device by passing a tag id.
|
|
||||||
|
|
||||||
Example: /tags/23/device.
|
Example: /tags/23/device.
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,7 @@ class User(Thing):
|
||||||
# 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:
|
||||||
"""
|
"""Creates an user.
|
||||||
Creates an user.
|
|
||||||
:param email:
|
:param email:
|
||||||
:param password:
|
:param password:
|
||||||
:param inventories: A set of Inventory where the user has
|
:param inventories: A set of Inventory where the user has
|
||||||
|
|
|
@ -29,8 +29,7 @@ class User(Thing):
|
||||||
load_only=(),
|
load_only=(),
|
||||||
dump_only=(),
|
dump_only=(),
|
||||||
partial=False):
|
partial=False):
|
||||||
"""
|
"""Instantiates the User.
|
||||||
Instantiates the User.
|
|
||||||
|
|
||||||
By default we exclude token from both load/dump
|
By default we exclude token from both load/dump
|
||||||
so they are not taken / set in normal usage by mistake.
|
so they are not taken / set in normal usage by mistake.
|
||||||
|
|
|
@ -5,11 +5,11 @@ device:
|
||||||
model: d1ml
|
model: d1ml
|
||||||
manufacturer: d1mr
|
manufacturer: d1mr
|
||||||
components:
|
components:
|
||||||
- type: GraphicCard
|
- type: GraphicCard
|
||||||
serial_number: gc1s
|
serial_number: gc1s
|
||||||
model: gc1ml
|
model: gc1ml
|
||||||
manufacturer: gc1mr
|
manufacturer: gc1mr
|
||||||
- type: RamModule
|
- type: RamModule
|
||||||
serial_number: rm1s
|
serial_number: rm1s
|
||||||
model: rm1ml
|
model: rm1ml
|
||||||
manufacturer: rm1mr
|
manufacturer: rm1mr
|
||||||
|
|
|
@ -32,8 +32,8 @@ components:
|
||||||
passedLifetime: 16947, powerCycleCount: 1694, reallocatedSectorCount: 0, reportedUncorrectableErrors: 0,
|
passedLifetime: 16947, powerCycleCount: 1694, reallocatedSectorCount: 0, reportedUncorrectableErrors: 0,
|
||||||
status: Completed without error, type: Short offline}
|
status: Completed without error, type: Short offline}
|
||||||
type: HDD
|
type: HDD
|
||||||
- { '@type': GraphicCard, manufacturer: Intel Corporation, memory: 256.0, model: 4
|
- { '@type': GraphicCard, manufacturer: Intel Corporation, memory: 256.0, model: 4
|
||||||
Series Chipset Integrated Graphics Controller, serialNumber: null}
|
Series Chipset Integrated Graphics Controller, serialNumber: null}
|
||||||
- '@type': Motherboard
|
- '@type': Motherboard
|
||||||
connectors: {firewire: 0, pcmcia: 0, serial: 1, usb: 8}
|
connectors: {firewire: 0, pcmcia: 0, serial: 1, usb: 8}
|
||||||
manufacturer: LENOVO
|
manufacturer: LENOVO
|
||||||
|
@ -41,22 +41,22 @@ components:
|
||||||
serialNumber: null
|
serialNumber: null
|
||||||
totalSlots: 0
|
totalSlots: 0
|
||||||
usedSlots: 2
|
usedSlots: 2
|
||||||
- { '@type': NetworkAdapter, manufacturer: Intel Corporation, model: 82567LM-3 Gigabit
|
- { '@type': NetworkAdapter, manufacturer: Intel Corporation, model: 82567LM-3 Gigabit
|
||||||
Network Connection, serialNumber: '00:21:86:2c:5e:d6', speed: 1000}
|
Network Connection, serialNumber: '00:21:86:2c:5e:d6', speed: 1000}
|
||||||
- { '@type': SoundCard, manufacturer: Intel Corporation, model: 82801JD/DO HD Audio
|
- { '@type': SoundCard, manufacturer: Intel Corporation, model: 82801JD/DO HD Audio
|
||||||
Controller, serialNumber: null}
|
Controller, serialNumber: null}
|
||||||
condition:
|
condition:
|
||||||
appearance: {general: B}
|
appearance: {general: B}
|
||||||
functionality: {general: A}
|
functionality: {general: A}
|
||||||
date: '2018-05-09T10:32:15'
|
date: '2018-05-09T10:32:15'
|
||||||
debug:
|
debug:
|
||||||
capabilities: { dmi-2.5: DMI version 2.5, smbios-2.5: SMBIOS version 2.5, smp: Symmetric
|
capabilities: { dmi-2.5: DMI version 2.5, smbios-2.5: SMBIOS version 2.5, smp: Symmetric
|
||||||
Multi-Processing, smp-1.4: SMP specification v1.4}
|
Multi-Processing, smp-1.4: SMP specification v1.4}
|
||||||
children:
|
children:
|
||||||
- children:
|
- children:
|
||||||
- capabilities: { acpi: ACPI, biosbootspecification: BIOS boot specification, cdboot: Booting
|
- capabilities: { acpi: ACPI, biosbootspecification: BIOS boot specification, cdboot: Booting
|
||||||
from CD-ROM/DVD, edd: Enhanced Disk Drive extensions, escd: ESCD, ls120boot: Booting
|
from CD-ROM/DVD, edd: Enhanced Disk Drive extensions, escd: ESCD, ls120boot: Booting
|
||||||
from LS-120, pci: PCI bus, pnp: Plug-and-Play, shadowing: BIOS shadowing,
|
from LS-120, pci: PCI bus, pnp: Plug-and-Play, shadowing: BIOS shadowing,
|
||||||
smartbattery: Smart battery, upgrade: BIOS EEPROM can be upgraded, usb: USB
|
smartbattery: Smart battery, upgrade: BIOS EEPROM can be upgraded, usb: USB
|
||||||
legacy emulation}
|
legacy emulation}
|
||||||
capacity: 4128768
|
capacity: 4128768
|
||||||
|
@ -71,9 +71,9 @@ debug:
|
||||||
vendor: LENOVO
|
vendor: LENOVO
|
||||||
version: 5CKT48AUS
|
version: 5CKT48AUS
|
||||||
- businfo: cpu@0
|
- businfo: cpu@0
|
||||||
capabilities: { acpi: thermal control (ACPI), aperfmperf: true, apic: on-chip
|
capabilities: { acpi: thermal control (ACPI), aperfmperf: true, apic: on-chip
|
||||||
advanced programmable interrupt controller (APIC), arch_perfmon: true, boot: boot
|
advanced programmable interrupt controller (APIC), arch_perfmon: true, boot: boot
|
||||||
processor, bts: true, clflush: true, cmov: conditional move instruction,
|
processor, bts: true, clflush: true, cmov: conditional move instruction,
|
||||||
constant_tsc: true, cpufreq: CPU Frequency scaling, cx16: true, cx8: compare
|
constant_tsc: true, cpufreq: CPU Frequency scaling, cx16: true, cx8: compare
|
||||||
and exchange 8-byte, de: debugging extensions, ds_cpl: true, dtes64: true,
|
and exchange 8-byte, de: debugging extensions, ds_cpl: true, dtes64: true,
|
||||||
dtherm: true, dts: debug trace and EMON store MSRs, eagerfpu: true, est: true,
|
dtherm: true, dts: debug trace and EMON store MSRs, eagerfpu: true, est: true,
|
||||||
|
@ -149,16 +149,16 @@ debug:
|
||||||
version: 6.7.10
|
version: 6.7.10
|
||||||
width: 64
|
width: 64
|
||||||
- children:
|
- children:
|
||||||
- { claimed: true, class: memory, clock: 1067000000, description: DIMM DDR2 Synchronous
|
- { claimed: true, class: memory, clock: 1067000000, description: DIMM DDR2 Synchronous
|
||||||
1067 MHz (0.9 ns), handle: 'DMI:001F', id: 'bank:0', physid: '0', product: '000000000000000000000000000000000000',
|
1067 MHz (0.9 ns), handle: 'DMI:001F', id: 'bank:0', physid: '0', product: '000000000000000000000000000000000000',
|
||||||
serial: '00000000', size: 2147483648, slot: J6G1, units: bytes, vendor: Unknown,
|
serial: '00000000', size: 2147483648, slot: J6G1, units: bytes, vendor: Unknown,
|
||||||
width: 40960}
|
width: 40960}
|
||||||
- {claimed: true, class: memory, clock: 1067000000, description: 'DIMM DDR2
|
- {claimed: true, class: memory, clock: 1067000000, description: 'DIMM DDR2
|
||||||
Synchronous 1067 MHz (0.9 ns) [empty]', handle: 'DMI:0020', id: 'bank:1',
|
Synchronous 1067 MHz (0.9 ns) [empty]', handle: 'DMI:0020', id: 'bank:1',
|
||||||
physid: '1', product: 012345678901234567890123456789012345, serial: '01234567',
|
physid: '1', product: 012345678901234567890123456789012345, serial: '01234567',
|
||||||
slot: J6G2, vendor: 48spaces}
|
slot: J6G2, vendor: 48spaces}
|
||||||
- { claimed: true, class: memory, clock: 1067000000, description: DIMM DDR2 Synchronous
|
- { claimed: true, class: memory, clock: 1067000000, description: DIMM DDR2 Synchronous
|
||||||
1067 MHz (0.9 ns), handle: 'DMI:0021', id: 'bank:2', physid: '2', product: '000000000000000000000000000000000000',
|
1067 MHz (0.9 ns), handle: 'DMI:0021', id: 'bank:2', physid: '2', product: '000000000000000000000000000000000000',
|
||||||
serial: '00000000', size: 2147483648, slot: J6H1, units: bytes, vendor: Unknown,
|
serial: '00000000', size: 2147483648, slot: J6H1, units: bytes, vendor: Unknown,
|
||||||
width: 41984}
|
width: 41984}
|
||||||
- {claimed: true, class: memory, clock: 1067000000, description: 'DIMM DDR2
|
- {claimed: true, class: memory, clock: 1067000000, description: 'DIMM DDR2
|
||||||
|
@ -205,7 +205,7 @@ debug:
|
||||||
- businfo: pci@0000:00:00.0
|
- businfo: pci@0000:00:00.0
|
||||||
children:
|
children:
|
||||||
- businfo: pci@0000:00:02.0
|
- businfo: pci@0000:00:02.0
|
||||||
capabilities: { bus_master: bus mastering, cap_list: PCI capabilities listing,
|
capabilities: { bus_master: bus mastering, cap_list: PCI capabilities listing,
|
||||||
msi: Message Signalled Interrupts, pm: Power Management, rom: extension
|
msi: Message Signalled Interrupts, pm: Power Management, rom: extension
|
||||||
ROM, vga_controller: true}
|
ROM, vga_controller: true}
|
||||||
claimed: true
|
claimed: true
|
||||||
|
@ -265,8 +265,8 @@ debug:
|
||||||
version: '03'
|
version: '03'
|
||||||
width: 32
|
width: 32
|
||||||
- businfo: pci@0000:00:03.3
|
- businfo: pci@0000:00:03.3
|
||||||
capabilities: { '16550': true, bus_master: bus mastering, cap_list: PCI capabilities
|
capabilities: { '16550': true, bus_master: bus mastering, cap_list: PCI capabilities
|
||||||
listing, msi: Message Signalled Interrupts, pm: Power Management}
|
listing, msi: Message Signalled Interrupts, pm: Power Management}
|
||||||
claimed: true
|
claimed: true
|
||||||
class: communication
|
class: communication
|
||||||
clock: 66000000
|
clock: 66000000
|
||||||
|
@ -280,8 +280,8 @@ debug:
|
||||||
version: '03'
|
version: '03'
|
||||||
width: 32
|
width: 32
|
||||||
- businfo: pci@0000:00:19.0
|
- businfo: pci@0000:00:19.0
|
||||||
capabilities: { 1000bt-fd: 1Gbit/s (full duplex), 100bt: 100Mbit/s, 100bt-fd: 100Mbit/s
|
capabilities: { 1000bt-fd: 1Gbit/s (full duplex), 100bt: 100Mbit/s, 100bt-fd: 100Mbit/s
|
||||||
(full duplex), 10bt: 10Mbit/s, 10bt-fd: 10Mbit/s (full duplex), autonegotiation: Auto-negotiation,
|
(full duplex), 10bt: 10Mbit/s, 10bt-fd: 10Mbit/s (full duplex), autonegotiation: Auto-negotiation,
|
||||||
bus_master: bus mastering, cap_list: PCI capabilities listing, ethernet: true,
|
bus_master: bus mastering, cap_list: PCI capabilities listing, ethernet: true,
|
||||||
msi: Message Signalled Interrupts, physical: Physical interface, pm: Power
|
msi: Message Signalled Interrupts, physical: Physical interface, pm: Power
|
||||||
Management, tp: twisted pair}
|
Management, tp: twisted pair}
|
||||||
|
@ -575,8 +575,8 @@ debug:
|
||||||
version: '02'
|
version: '02'
|
||||||
width: 32
|
width: 32
|
||||||
- businfo: pci@0000:00:1f.2
|
- businfo: pci@0000:00:1f.2
|
||||||
capabilities: { ahci_1.0: true, bus_master: bus mastering, cap_list: PCI capabilities
|
capabilities: { ahci_1.0: true, bus_master: bus mastering, cap_list: PCI capabilities
|
||||||
listing, msi: Message Signalled Interrupts, pm: Power Management, storage: true}
|
listing, msi: Message Signalled Interrupts, pm: Power Management, storage: true}
|
||||||
claimed: true
|
claimed: true
|
||||||
class: storage
|
class: storage
|
||||||
clock: 66000000
|
clock: 66000000
|
||||||
|
@ -620,7 +620,7 @@ debug:
|
||||||
table}
|
table}
|
||||||
children:
|
children:
|
||||||
- businfo: scsi@0:0.0.0,1
|
- businfo: scsi@0:0.0.0,1
|
||||||
capabilities: { dir_nlink: directories with 65000+ subdirs, ext2: EXT2/EXT3,
|
capabilities: { dir_nlink: directories with 65000+ subdirs, ext2: EXT2/EXT3,
|
||||||
ext4: true, extended_attributes: Extended Attributes, extents: extent-based
|
ext4: true, extended_attributes: Extended Attributes, extents: extent-based
|
||||||
allocation, huge_files: 16TB+ files, initialized: initialized volume,
|
allocation, huge_files: 16TB+ files, initialized: initialized volume,
|
||||||
journaled: true, large_files: 4GB+ files, primary: Primary partition}
|
journaled: true, large_files: 4GB+ files, primary: Primary partition}
|
||||||
|
@ -672,8 +672,8 @@ debug:
|
||||||
- capabilities: {emulated: Emulated device}
|
- capabilities: {emulated: Emulated device}
|
||||||
children:
|
children:
|
||||||
- businfo: scsi@1:0.0.0
|
- businfo: scsi@1:0.0.0
|
||||||
capabilities: { audio: Audio CD playback, cd-r: CD-R burning, cd-rw: CD-RW
|
capabilities: { audio: Audio CD playback, cd-r: CD-R burning, cd-rw: CD-RW
|
||||||
burning, dvd: DVD playback, dvd-r: DVD-R burning, dvd-ram: DVD-RAM burning,
|
burning, dvd: DVD playback, dvd-r: DVD-R burning, dvd-ram: DVD-RAM burning,
|
||||||
removable: support is removable}
|
removable: support is removable}
|
||||||
claimed: true
|
claimed: true
|
||||||
class: disk
|
class: disk
|
||||||
|
|
|
@ -102,8 +102,7 @@ def test_membership_repeating_id():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(app_context.__name__)
|
@pytest.mark.usefixtures(app_context.__name__)
|
||||||
def test_default_org_exists(config: DevicehubConfig):
|
def test_default_org_exists(config: DevicehubConfig):
|
||||||
"""
|
"""Ensures that the default organization is created on app
|
||||||
Ensures that the default organization is created on app
|
|
||||||
initialization and that is accessible for the method
|
initialization and that is accessible for the method
|
||||||
:meth:`ereuse_devicehub.resources.user.Organization.get_default_org`.
|
:meth:`ereuse_devicehub.resources.user.Organization.get_default_org`.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -31,9 +31,7 @@ from tests.conftest import file
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_device_model():
|
def test_device_model():
|
||||||
"""
|
"""Tests that the correctness of the device model and its relationships."""
|
||||||
Tests that the correctness of the device model and its relationships.
|
|
||||||
"""
|
|
||||||
pc = d.Desktop(model='p1mo',
|
pc = d.Desktop(model='p1mo',
|
||||||
manufacturer='p1ma',
|
manufacturer='p1ma',
|
||||||
serial_number='p1s',
|
serial_number='p1s',
|
||||||
|
@ -182,8 +180,7 @@ def test_add_remove():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_run_components_empty():
|
def test_sync_run_components_empty():
|
||||||
"""
|
"""Syncs a device that has an empty components list. The system should
|
||||||
Syncs a device that has an empty components list. The system should
|
|
||||||
remove all the components from the device.
|
remove all the components from the device.
|
||||||
"""
|
"""
|
||||||
s = conftest.file('pc-components.db')
|
s = conftest.file('pc-components.db')
|
||||||
|
@ -200,8 +197,7 @@ def test_sync_run_components_empty():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_run_components_none():
|
def test_sync_run_components_none():
|
||||||
"""
|
"""Syncs a device that has a None components. The system should
|
||||||
Syncs a device that has a None components. The system should
|
|
||||||
keep all the components from the device.
|
keep all the components from the device.
|
||||||
"""
|
"""
|
||||||
s = conftest.file('pc-components.db')
|
s = conftest.file('pc-components.db')
|
||||||
|
@ -218,10 +214,7 @@ def test_sync_run_components_none():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_desktop_new_desktop_no_tag():
|
def test_sync_execute_register_desktop_new_desktop_no_tag():
|
||||||
"""
|
"""Syncs a new d.Desktop with HID and without a tag, creating it."""
|
||||||
Syncs a new d.Desktop with HID and without a tag, creating it.
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
# Case 1: device does not exist on DB
|
# Case 1: device does not exist on DB
|
||||||
pc = d.Desktop(**conftest.file('pc-components.db')['device'])
|
pc = d.Desktop(**conftest.file('pc-components.db')['device'])
|
||||||
db_pc = Sync().execute_register(pc)
|
db_pc = Sync().execute_register(pc)
|
||||||
|
@ -230,9 +223,7 @@ def test_sync_execute_register_desktop_new_desktop_no_tag():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_desktop_existing_no_tag():
|
def test_sync_execute_register_desktop_existing_no_tag():
|
||||||
"""
|
"""Syncs an existing d.Desktop with HID and without a tag."""
|
||||||
Syncs an existing d.Desktop with HID and without a tag.
|
|
||||||
"""
|
|
||||||
pc = d.Desktop(**conftest.file('pc-components.db')['device'])
|
pc = d.Desktop(**conftest.file('pc-components.db')['device'])
|
||||||
db.session.add(pc)
|
db.session.add(pc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -246,8 +237,7 @@ def test_sync_execute_register_desktop_existing_no_tag():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_desktop_no_hid_no_tag():
|
def test_sync_execute_register_desktop_no_hid_no_tag():
|
||||||
"""
|
"""Syncs a d.Desktop without HID and no tag.
|
||||||
Syncs a d.Desktop without HID and no tag.
|
|
||||||
|
|
||||||
This should fail as we don't have a way to identify it.
|
This should fail as we don't have a way to identify it.
|
||||||
"""
|
"""
|
||||||
|
@ -260,8 +250,7 @@ def test_sync_execute_register_desktop_no_hid_no_tag():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_desktop_tag_not_linked():
|
def test_sync_execute_register_desktop_tag_not_linked():
|
||||||
"""
|
"""Syncs a new d.Desktop with HID and a non-linked tag.
|
||||||
Syncs a new d.Desktop with HID and a non-linked tag.
|
|
||||||
|
|
||||||
It is OK if the tag was not linked, it will be linked in this process.
|
It is OK if the tag was not linked, it will be linked in this process.
|
||||||
"""
|
"""
|
||||||
|
@ -279,8 +268,7 @@ def test_sync_execute_register_desktop_tag_not_linked():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
|
def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
|
||||||
"""
|
"""Validates registering a d.Desktop without HID and a non-linked tag.
|
||||||
Validates registering a d.Desktop without HID and a non-linked tag.
|
|
||||||
|
|
||||||
In this case it is ok still, as the non-linked tag proves that
|
In this case it is ok still, as the non-linked tag proves that
|
||||||
the d.Desktop was not existing before (otherwise the tag would
|
the d.Desktop was not existing before (otherwise the tag would
|
||||||
|
@ -302,8 +290,7 @@ def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str):
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_tag_does_not_exist():
|
def test_sync_execute_register_tag_does_not_exist():
|
||||||
"""
|
"""Ensures not being able to register if the tag does not exist,
|
||||||
Ensures not being able to register if the tag does not exist,
|
|
||||||
even if the device has HID or it existed before.
|
even if the device has HID or it existed before.
|
||||||
|
|
||||||
Tags have to be created before trying to link them through a Snapshot.
|
Tags have to be created before trying to link them through a Snapshot.
|
||||||
|
@ -315,8 +302,7 @@ def test_sync_execute_register_tag_does_not_exist():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_tag_linked_same_device():
|
def test_sync_execute_register_tag_linked_same_device():
|
||||||
"""
|
"""If the tag is linked to the device, regardless if it has HID,
|
||||||
If the tag is linked to the device, regardless if it has HID,
|
|
||||||
the system should match the device through the tag.
|
the system should match the device through the tag.
|
||||||
(If it has HID it validates both HID and tag point at the same
|
(If it has HID it validates both HID and tag point at the same
|
||||||
device, this his checked in ).
|
device, this his checked in ).
|
||||||
|
@ -336,8 +322,7 @@ def test_sync_execute_register_tag_linked_same_device():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags():
|
def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags():
|
||||||
"""
|
"""Checks that sync raises an error if finds that at least two passed-in
|
||||||
Checks that sync raises an error if finds that at least two passed-in
|
|
||||||
tags are not linked to the same device.
|
tags are not linked to the same device.
|
||||||
"""
|
"""
|
||||||
pc1 = d.Desktop(**conftest.file('pc-components.db')['device'])
|
pc1 = d.Desktop(**conftest.file('pc-components.db')['device'])
|
||||||
|
@ -358,8 +343,7 @@ def test_sync_execute_register_tag_linked_other_device_mismatch_between_tags():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_sync_execute_register_mismatch_between_tags_and_hid():
|
def test_sync_execute_register_mismatch_between_tags_and_hid():
|
||||||
"""
|
"""Checks that sync raises an error if it finds that the HID does
|
||||||
Checks that sync raises an error if it finds that the HID does
|
|
||||||
not point at the same device as the tag does.
|
not point at the same device as the tag does.
|
||||||
|
|
||||||
In this case we set HID -> pc1 but tag -> pc2
|
In this case we set HID -> pc1 but tag -> pc2
|
||||||
|
@ -465,7 +449,8 @@ def test_manufacturer(user: UserClient):
|
||||||
@pytest.mark.xfail(reason='Develop functionality')
|
@pytest.mark.xfail(reason='Develop functionality')
|
||||||
def test_manufacturer_enforced():
|
def test_manufacturer_enforced():
|
||||||
"""Ensures that non-computer devices can submit only
|
"""Ensures that non-computer devices can submit only
|
||||||
manufacturers from the Manufacturer table."""
|
manufacturers from the Manufacturer table.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def test_device_properties_format(app: Devicehub, user: UserClient):
|
def test_device_properties_format(app: Devicehub, user: UserClient):
|
||||||
|
|
|
@ -56,8 +56,7 @@ def test_device_sort():
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def device_query_dummy(app: Devicehub):
|
def device_query_dummy(app: Devicehub):
|
||||||
"""
|
"""3 computers, where:
|
||||||
3 computers, where:
|
|
||||||
|
|
||||||
1. s1 Desktop with a Processor
|
1. s1 Desktop with a Processor
|
||||||
2. s2 Desktop with an SSD
|
2. s2 Desktop with an SSD
|
||||||
|
|
|
@ -28,7 +28,7 @@ def test_dispatcher_default(dispatcher: PathDispatcher):
|
||||||
|
|
||||||
|
|
||||||
def test_dispatcher_return_app(dispatcher: PathDispatcher):
|
def test_dispatcher_return_app(dispatcher: PathDispatcher):
|
||||||
"""The dispatcher returns the correct app for the URL"""
|
"""The dispatcher returns the correct app for the URL."""
|
||||||
# Note that the dispatcher does not check if the URL points
|
# Note that the dispatcher does not check if the URL points
|
||||||
# to a well-known endpoint for the app.
|
# to a well-known endpoint for the app.
|
||||||
# Only if can route it to an app. And then the app checks
|
# Only if can route it to an app. And then the app checks
|
||||||
|
@ -39,7 +39,7 @@ def test_dispatcher_return_app(dispatcher: PathDispatcher):
|
||||||
|
|
||||||
|
|
||||||
def test_dispatcher_users(dispatcher: PathDispatcher):
|
def test_dispatcher_users(dispatcher: PathDispatcher):
|
||||||
"""Users special endpoint returns an app"""
|
"""Users special endpoint returns an app."""
|
||||||
# For now returns the first app, as all apps
|
# For now returns the first app, as all apps
|
||||||
# can answer {}/users/login
|
# can answer {}/users/login
|
||||||
app = dispatcher({'SCRIPT_NAME:': '/', 'PATH_INFO': '/users/'}, noop)
|
app = dispatcher({'SCRIPT_NAME:': '/', 'PATH_INFO': '/users/'}, noop)
|
||||||
|
|
|
@ -22,8 +22,7 @@ from tests.conftest import create_user, file
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_author():
|
def test_author():
|
||||||
"""
|
"""Checks the default created author.
|
||||||
Checks the default created author.
|
|
||||||
|
|
||||||
Note that the author can be accessed after inserting the row.
|
Note that the author can be accessed after inserting the row.
|
||||||
"""
|
"""
|
||||||
|
@ -341,7 +340,8 @@ def test_price_custom_client(user: UserClient):
|
||||||
def test_ereuse_price():
|
def test_ereuse_price():
|
||||||
"""Tests the several ways of creating eReuse Price, emulating
|
"""Tests the several ways of creating eReuse Price, emulating
|
||||||
from an AggregateRate and ensuring that the different Range
|
from an AggregateRate and ensuring that the different Range
|
||||||
return correct results."""
|
return correct results.
|
||||||
|
"""
|
||||||
# important to check Range.low no returning warranty2
|
# important to check Range.low no returning warranty2
|
||||||
# Range.verylow not returning nothing
|
# Range.verylow not returning nothing
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,7 @@ from ereuse_devicehub.resources.inventory import Inventory
|
||||||
from ereuse_devicehub.resources.user import User
|
from ereuse_devicehub.resources.user import User
|
||||||
from tests.conftest import TestConfig
|
from tests.conftest import TestConfig
|
||||||
|
|
||||||
"""
|
"""Tests the management of inventories in a multi-inventory environment
|
||||||
Tests the management of inventories in a multi-inventory environment
|
|
||||||
(several Devicehub instances that point at different schemas).
|
(several Devicehub instances that point at different schemas).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,7 @@ from ereuse_devicehub.resources.enums import ComputerChassis
|
||||||
from ereuse_devicehub.resources.lot.models import Lot, LotDevice
|
from ereuse_devicehub.resources.lot.models import Lot, LotDevice
|
||||||
from tests import conftest
|
from tests import conftest
|
||||||
|
|
||||||
"""
|
"""In case of error, debug with:
|
||||||
In case of error, debug with:
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with db.session.begin_nested():
|
with db.session.begin_nested():
|
||||||
|
@ -67,7 +66,7 @@ def test_lot_model_children():
|
||||||
|
|
||||||
|
|
||||||
def test_lot_modify_patch_endpoint_and_delete(user: UserClient):
|
def test_lot_modify_patch_endpoint_and_delete(user: UserClient):
|
||||||
"""Creates and modifies lot properties through the endpoint"""
|
"""Creates and modifies lot properties through the endpoint."""
|
||||||
l, _ = user.post({'name': 'foo', 'description': 'baz'}, res=Lot)
|
l, _ = user.post({'name': 'foo', 'description': 'baz'}, res=Lot)
|
||||||
assert l['name'] == 'foo'
|
assert l['name'] == 'foo'
|
||||||
assert l['description'] == 'baz'
|
assert l['description'] == 'baz'
|
||||||
|
@ -310,7 +309,8 @@ def test_post_get_lot(user: UserClient):
|
||||||
|
|
||||||
def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
|
def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
|
||||||
"""Tests adding children lots to a lot through the view and
|
"""Tests adding children lots to a lot through the view and
|
||||||
GETting the results."""
|
GETting the results.
|
||||||
|
"""
|
||||||
parent, _ = user.post(({'name': 'Parent'}), res=Lot)
|
parent, _ = user.post(({'name': 'Parent'}), res=Lot)
|
||||||
child, _ = user.post(({'name': 'Child'}), res=Lot)
|
child, _ = user.post(({'name': 'Child'}), res=Lot)
|
||||||
parent, _ = user.post({},
|
parent, _ = user.post({},
|
||||||
|
@ -347,7 +347,8 @@ def test_lot_post_add_children_view_ui_tree_normal(user: UserClient):
|
||||||
|
|
||||||
def test_lot_post_add_remove_device_view(app: Devicehub, user: UserClient):
|
def test_lot_post_add_remove_device_view(app: Devicehub, user: UserClient):
|
||||||
"""Tests adding a device to a lot using POST and
|
"""Tests adding a device to a lot using POST and
|
||||||
removing it with DELETE."""
|
removing it with DELETE.
|
||||||
|
"""
|
||||||
# todo check with components
|
# todo check with components
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
device = Desktop(serial_number='foo',
|
device = Desktop(serial_number='foo',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
|
|
||||||
import math
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ereuse_devicehub.client import UserClient
|
from ereuse_devicehub.client import UserClient
|
||||||
|
@ -96,8 +95,7 @@ def test_price_from_rate():
|
||||||
|
|
||||||
|
|
||||||
def test_when_rate_must_not_compute(user: UserClient):
|
def test_when_rate_must_not_compute(user: UserClient):
|
||||||
"""
|
"""Test to check if rate is computed in case of should not be calculated:
|
||||||
Test to check if rate is computed in case of should not be calculated:
|
|
||||||
1. Snapshot haven't visual test
|
1. Snapshot haven't visual test
|
||||||
2. Snapshot software aren't Workbench
|
2. Snapshot software aren't Workbench
|
||||||
3. Device type are not Computer
|
3. Device type are not Computer
|
||||||
|
@ -138,8 +136,8 @@ def test_multiple_rates(user: UserClient):
|
||||||
ensuring that the tests / benchmarks...
|
ensuring that the tests / benchmarks...
|
||||||
from the first rate do not contaminate the second rate.
|
from the first rate do not contaminate the second rate.
|
||||||
|
|
||||||
This ensures that rates only takes the last version of actions
|
This ensures that rates only takes all the correct actions
|
||||||
and components (in case device has new components, for example).
|
and components rates in case device have new tests/benchmarks.
|
||||||
"""
|
"""
|
||||||
pc = Desktop(chassis=ComputerChassis.Tower)
|
pc = Desktop(chassis=ComputerChassis.Tower)
|
||||||
hdd = HardDrive(size=476940)
|
hdd = HardDrive(size=476940)
|
||||||
|
@ -172,15 +170,13 @@ def test_multiple_rates(user: UserClient):
|
||||||
|
|
||||||
assert price1.price == Decimal('92.4001')
|
assert price1.price == Decimal('92.4001')
|
||||||
|
|
||||||
hdd = SolidStateDrive(size=476940)
|
|
||||||
hdd.actions_one.add(BenchmarkDataStorage(read_speed=222, write_speed=169))
|
|
||||||
cpu = Processor(cores=1, speed=3.0)
|
|
||||||
cpu.actions_one.add(BenchmarkProcessor(rate=16069.44))
|
cpu.actions_one.add(BenchmarkProcessor(rate=16069.44))
|
||||||
|
ssd = SolidStateDrive(size=476940)
|
||||||
|
ssd.actions_one.add(BenchmarkDataStorage(read_speed=222, write_speed=111))
|
||||||
pc.components |= {
|
pc.components |= {
|
||||||
hdd,
|
ssd,
|
||||||
RamModule(size=2048, speed=1067),
|
RamModule(size=2048, speed=1067),
|
||||||
RamModule(size=2048, speed=1067),
|
RamModule(size=2048, speed=1067),
|
||||||
cpu
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add test visual with functionality and appearance range
|
# Add test visual with functionality and appearance range
|
||||||
|
@ -190,13 +186,13 @@ def test_multiple_rates(user: UserClient):
|
||||||
|
|
||||||
rate2, price2 = RateComputer.compute(pc)
|
rate2, price2 = RateComputer.compute(pc)
|
||||||
|
|
||||||
assert rate2.data_storage == 4.27
|
assert rate2.data_storage == 4.3
|
||||||
assert rate2.processor == 3.61
|
assert rate2.processor == 3.78
|
||||||
assert rate2.ram == 4.12
|
assert rate2.ram == 3.95
|
||||||
|
|
||||||
assert rate2.appearance == 0
|
assert rate2.appearance == 0
|
||||||
assert rate2.functionality == -0.5
|
assert rate2.functionality == -0.5
|
||||||
|
|
||||||
assert rate2.rating == 3.37
|
assert rate2.rating == 3.43
|
||||||
|
|
||||||
assert rate2.price.price == Decimal('67.4001')
|
assert price2.price == Decimal('68.6001')
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
"""
|
"""This file test all corner cases when compute score v1.0.
|
||||||
This file test all corner cases when compute score v1.0.
|
|
||||||
|
First test to compute rate for every component in isolation.
|
||||||
|
|
||||||
First test to compute rate for every component in isolation
|
|
||||||
todo rewrite some corner cases using range(min,max) characteristics
|
|
||||||
in devices/schemas
|
|
||||||
Components in Score v1:
|
Components in Score v1:
|
||||||
-DataStorage
|
-DataStorage
|
||||||
-RamModule
|
-RamModule
|
||||||
-Processor
|
-Processor
|
||||||
|
|
||||||
Then some test compute rate with all components that use the a1lgorithm
|
Then some test compute rate with all components that use the algorithm
|
||||||
|
|
||||||
Excluded cases in tests
|
Excluded cases in tests
|
||||||
|
|
||||||
|
@ -31,9 +29,8 @@ from tests import conftest
|
||||||
|
|
||||||
|
|
||||||
def test_rate_data_storage_rate():
|
def test_rate_data_storage_rate():
|
||||||
"""
|
"""Test to check if compute data storage rate have same value than
|
||||||
Test to check if compute data storage rate have same value than previous score version;
|
previous score version.
|
||||||
id = pc_1193, pc_1201, pc_79, pc_798
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
hdd_1969 = HardDrive(size=476940)
|
hdd_1969 = HardDrive(size=476940)
|
||||||
|
@ -67,10 +64,8 @@ def test_rate_data_storage_rate():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_data_storage_size_is_null():
|
def test_rate_data_storage_size_is_null():
|
||||||
"""
|
"""Test where input DataStorage.size = NULL, BenchmarkDataStorage.read_speed = 0,
|
||||||
Test where input DataStorage.size = NULL, BenchmarkDataStorage.read_speed = 0,
|
|
||||||
BenchmarkDataStorage.write_speed = 0 is like no DataStorage has been detected;
|
BenchmarkDataStorage.write_speed = 0 is like no DataStorage has been detected;
|
||||||
id = pc_2992
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
hdd_null = HardDrive(size=None)
|
hdd_null = HardDrive(size=None)
|
||||||
|
@ -81,9 +76,7 @@ def test_rate_data_storage_size_is_null():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_no_data_storage():
|
def test_rate_no_data_storage():
|
||||||
"""
|
"""Test without data storage devices."""
|
||||||
Test without data storage devices
|
|
||||||
"""
|
|
||||||
|
|
||||||
hdd_null = HardDrive()
|
hdd_null = HardDrive()
|
||||||
hdd_null.actions_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0))
|
hdd_null.actions_one.add(BenchmarkDataStorage(read_speed=0, write_speed=0))
|
||||||
|
@ -92,9 +85,8 @@ def test_rate_no_data_storage():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_ram_rate():
|
def test_rate_ram_rate():
|
||||||
"""
|
"""Test to check if compute ram rate have same value than previous
|
||||||
Test to check if compute ram rate have same value than previous score version
|
score version only with 1 RamModule.
|
||||||
only with 1 RamModule; id = pc_1201
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ram1 = RamModule(size=2048, speed=1333)
|
ram1 = RamModule(size=2048, speed=1333)
|
||||||
|
@ -105,9 +97,8 @@ def test_rate_ram_rate():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_ram_rate_2modules():
|
def test_rate_ram_rate_2modules():
|
||||||
"""
|
"""Test to check if compute ram rate have same value than previous
|
||||||
Test to check if compute ram rate have same value than previous score version
|
score version with 2 RamModule.
|
||||||
with 2 RamModule; id = pc_1193
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ram1 = RamModule(size=4096, speed=1600)
|
ram1 = RamModule(size=4096, speed=1600)
|
||||||
|
@ -119,9 +110,8 @@ def test_rate_ram_rate_2modules():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_ram_rate_4modules():
|
def test_rate_ram_rate_4modules():
|
||||||
"""
|
"""Test to check if compute ram rate have same value than previous
|
||||||
Test to check if compute ram rate have same value than previous score version
|
score version with 2 RamModule.
|
||||||
with 2 RamModule; id = pc_79
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ram1 = RamModule(size=512, speed=667)
|
ram1 = RamModule(size=512, speed=667)
|
||||||
|
@ -135,8 +125,8 @@ def test_rate_ram_rate_4modules():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_ram_module_size_is_0():
|
def test_rate_ram_module_size_is_0():
|
||||||
"""
|
"""Test where input data RamModule.size = 0; is like no RamModule
|
||||||
Test where input data RamModule.size = 0; is like no RamModule has been detected; id = pc_798
|
has been detected.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ram0 = RamModule(size=0, speed=888)
|
ram0 = RamModule(size=0, speed=888)
|
||||||
|
@ -146,10 +136,7 @@ def test_rate_ram_module_size_is_0():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_ram_speed_is_null():
|
def test_rate_ram_speed_is_null():
|
||||||
"""
|
"""Test where RamModule.speed is NULL (not detected) but has size."""
|
||||||
Test where RamModule.speed is NULL (not detected) but has size.
|
|
||||||
Pc ID = 795(1542), 745(1535), 804(1549)
|
|
||||||
"""
|
|
||||||
|
|
||||||
ram0 = RamModule(size=2048, speed=None)
|
ram0 = RamModule(size=2048, speed=None)
|
||||||
|
|
||||||
|
@ -165,9 +152,7 @@ def test_rate_ram_speed_is_null():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_no_ram_module():
|
def test_rate_no_ram_module():
|
||||||
"""
|
"""Test without RamModule."""
|
||||||
Test without RamModule
|
|
||||||
"""
|
|
||||||
ram0 = RamModule()
|
ram0 = RamModule()
|
||||||
|
|
||||||
ram_rate = RamRate().compute([ram0])
|
ram_rate = RamRate().compute([ram0])
|
||||||
|
@ -175,9 +160,8 @@ def test_rate_no_ram_module():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_processor_rate():
|
def test_rate_processor_rate():
|
||||||
"""
|
"""Test to check if compute processor rate have same value than previous
|
||||||
Test to check if compute processor rate have same value than previous score version
|
score version only with 1 core.
|
||||||
only with 1 core; id = 79
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cpu = Processor(cores=1, speed=1.6)
|
cpu = Processor(cores=1, speed=1.6)
|
||||||
|
@ -190,9 +174,8 @@ def test_rate_processor_rate():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_processor_rate_2cores():
|
def test_rate_processor_rate_2cores():
|
||||||
"""
|
"""Test to check if compute processor rate have same value than previous
|
||||||
Test to check if compute processor rate have same value than previous score version
|
score version with 2 cores.
|
||||||
with 2 cores; id = pc_1193, pc_1201
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cpu = Processor(cores=2, speed=3.4)
|
cpu = Processor(cores=2, speed=3.4)
|
||||||
|
@ -211,13 +194,8 @@ def test_rate_processor_rate_2cores():
|
||||||
assert math.isclose(processor_rate, 3.93, rel_tol=0.002)
|
assert math.isclose(processor_rate, 3.93, rel_tol=0.002)
|
||||||
|
|
||||||
|
|
||||||
# TODO JN if delete processor default score for benchmark_cpu
|
|
||||||
|
|
||||||
|
|
||||||
def test_rate_processor_with_null_cores():
|
def test_rate_processor_with_null_cores():
|
||||||
"""
|
"""Test with processor device have null number of cores."""
|
||||||
Test with processor device have null number of cores
|
|
||||||
"""
|
|
||||||
cpu = Processor(cores=None, speed=3.3)
|
cpu = Processor(cores=None, speed=3.3)
|
||||||
cpu.actions_one.add(BenchmarkProcessor(rate=0))
|
cpu.actions_one.add(BenchmarkProcessor(rate=0))
|
||||||
|
|
||||||
|
@ -227,9 +205,7 @@ def test_rate_processor_with_null_cores():
|
||||||
|
|
||||||
|
|
||||||
def test_rate_processor_with_null_speed():
|
def test_rate_processor_with_null_speed():
|
||||||
"""
|
"""Test with processor device have null speed value."""
|
||||||
Test with processor device have null speed value
|
|
||||||
"""
|
|
||||||
cpu = Processor(cores=1, speed=None)
|
cpu = Processor(cores=1, speed=None)
|
||||||
cpu.actions_one.add(BenchmarkProcessor(rate=0))
|
cpu.actions_one.add(BenchmarkProcessor(rate=0))
|
||||||
|
|
||||||
|
@ -238,13 +214,9 @@ def test_rate_processor_with_null_speed():
|
||||||
assert math.isclose(processor_rate, 1.06, rel_tol=0.001)
|
assert math.isclose(processor_rate, 1.06, rel_tol=0.001)
|
||||||
|
|
||||||
|
|
||||||
# TODO JN add price asserts in rate computers??
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_rate_computer_1193():
|
def test_rate_computer_1193():
|
||||||
"""
|
"""Test rate computer characteristics:
|
||||||
Test rate computer characteristics:
|
|
||||||
- 2 module ram
|
- 2 module ram
|
||||||
- processor with 2 cores
|
- processor with 2 cores
|
||||||
|
|
||||||
|
@ -297,8 +269,7 @@ def test_rate_computer_1193():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_rate_computer_1201():
|
def test_rate_computer_1201():
|
||||||
"""
|
"""Test rate computer characteristics:
|
||||||
Test rate computer characteristics:
|
|
||||||
- only 1 module ram
|
- only 1 module ram
|
||||||
- processor 2 cores
|
- processor 2 cores
|
||||||
|
|
||||||
|
@ -349,8 +320,7 @@ def test_rate_computer_1201():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_rate_computer_multiple_ram_module():
|
def test_rate_computer_multiple_ram_module():
|
||||||
"""
|
"""Test rate computer characteristics:
|
||||||
Test rate computer characteristics:
|
|
||||||
- only 1 module ram
|
- only 1 module ram
|
||||||
- processor 2 cores
|
- processor 2 cores
|
||||||
|
|
||||||
|
@ -408,8 +378,7 @@ def test_rate_computer_multiple_ram_module():
|
||||||
|
|
||||||
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
@pytest.mark.usefixtures(conftest.auth_app_context.__name__)
|
||||||
def test_rate_computer_one_ram_module():
|
def test_rate_computer_one_ram_module():
|
||||||
"""
|
"""Test rate computer characteristics:
|
||||||
Test rate computer characteristics:
|
|
||||||
- only 1 module ram
|
- only 1 module ram
|
||||||
- processor 2 cores
|
- processor 2 cores
|
||||||
|
|
||||||
|
@ -463,4 +432,5 @@ def test_rate_computer_one_ram_module():
|
||||||
@pytest.mark.xfail(reason='Data Storage rate actually requires a DSSBenchmark')
|
@pytest.mark.xfail(reason='Data Storage rate actually requires a DSSBenchmark')
|
||||||
def test_rate_computer_with_data_storage_without_benchmark():
|
def test_rate_computer_with_data_storage_without_benchmark():
|
||||||
"""For example if the data storage was introduced manually
|
"""For example if the data storage was introduced manually
|
||||||
or comes from an old version without benchmark."""
|
or comes from an old version without benchmark.
|
||||||
|
"""
|
||||||
|
|
|
@ -3,8 +3,6 @@ from datetime import datetime
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from ereuse_devicehub.client import UserClient
|
from ereuse_devicehub.client import UserClient
|
||||||
from ereuse_devicehub.resources.action.models import Snapshot
|
from ereuse_devicehub.resources.action.models import Snapshot
|
||||||
from ereuse_devicehub.resources.documents import documents
|
from ereuse_devicehub.resources.documents import documents
|
||||||
|
@ -12,9 +10,7 @@ from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
def test_export_basic_snapshot(user: UserClient):
|
def test_export_basic_snapshot(user: UserClient):
|
||||||
"""
|
"""Test export device information in a csv file."""
|
||||||
Test export device information in a csv file
|
|
||||||
"""
|
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
|
@ -41,9 +37,7 @@ def test_export_basic_snapshot(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_export_full_snapshot(user: UserClient):
|
def test_export_full_snapshot(user: UserClient):
|
||||||
"""
|
"""Test a export device with all information and a lot of components."""
|
||||||
Test a export device with all information and a lot of components
|
|
||||||
"""
|
|
||||||
snapshot, _ = user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
snapshot, _ = user.post(file('real-eee-1001pxd.snapshot.11'), res=Snapshot)
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
|
@ -71,8 +65,8 @@ def test_export_full_snapshot(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_export_empty(user: UserClient):
|
def test_export_empty(user: UserClient):
|
||||||
"""
|
"""Test to check works correctly exporting csv without any information,
|
||||||
Test to check works correctly exporting csv without any information (no snapshot)
|
export a placeholder device.
|
||||||
"""
|
"""
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
|
@ -85,9 +79,7 @@ def test_export_empty(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_export_computer_monitor(user: UserClient):
|
def test_export_computer_monitor(user: UserClient):
|
||||||
"""
|
"""Test a export device type computer monitor."""
|
||||||
Test a export device type computer monitor
|
|
||||||
"""
|
|
||||||
snapshot, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
|
@ -111,9 +103,7 @@ def test_export_computer_monitor(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_export_keyboard(user: UserClient):
|
def test_export_keyboard(user: UserClient):
|
||||||
"""
|
"""Test a export device type keyboard."""
|
||||||
Test a export device type keyboard
|
|
||||||
"""
|
|
||||||
snapshot, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
|
@ -136,8 +126,7 @@ def test_export_keyboard(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_export_multiple_different_devices(user: UserClient):
|
def test_export_multiple_different_devices(user: UserClient):
|
||||||
"""
|
"""Test function 'Export' of multiple different device types (like
|
||||||
Test function 'Export' of multiple different device types (like
|
|
||||||
computers, keyboards, monitors, etc..)
|
computers, keyboards, monitors, etc..)
|
||||||
"""
|
"""
|
||||||
# Open fixture csv and transform to list
|
# Open fixture csv and transform to list
|
||||||
|
|
|
@ -26,8 +26,7 @@ from tests.conftest import file
|
||||||
|
|
||||||
@pytest.mark.usefixtures('auth_app_context')
|
@pytest.mark.usefixtures('auth_app_context')
|
||||||
def test_snapshot_model():
|
def test_snapshot_model():
|
||||||
"""
|
"""Tests creating a Snapshot with its relationships ensuring correct
|
||||||
Tests creating a Snapshot with its relationships ensuring correct
|
|
||||||
DB mapping.
|
DB mapping.
|
||||||
"""
|
"""
|
||||||
device = m.Desktop(serial_number='a1', chassis=ComputerChassis.Tower)
|
device = m.Desktop(serial_number='a1', chassis=ComputerChassis.Tower)
|
||||||
|
@ -62,8 +61,7 @@ def test_snapshot_schema(app: Devicehub):
|
||||||
|
|
||||||
|
|
||||||
def test_snapshot_post(user: UserClient):
|
def test_snapshot_post(user: UserClient):
|
||||||
"""
|
"""Tests the post snapshot endpoint (validation, etc), data correctness,
|
||||||
Tests the post snapshot endpoint (validation, etc), data correctness,
|
|
||||||
and relationship correctness.
|
and relationship correctness.
|
||||||
"""
|
"""
|
||||||
# TODO add all action_types to check, how to add correctly??
|
# TODO add all action_types to check, how to add correctly??
|
||||||
|
@ -98,8 +96,7 @@ def test_snapshot_post(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_snapshot_component_add_remove(user: UserClient):
|
def test_snapshot_component_add_remove(user: UserClient):
|
||||||
"""
|
"""Tests adding and removing components and some don't generate HID.
|
||||||
Tests adding and removing components and some don't generate HID.
|
|
||||||
All computers generate HID.
|
All computers generate HID.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -212,8 +209,7 @@ def test_snapshot_component_add_remove(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def _test_snapshot_computer_no_hid(user: UserClient):
|
def _test_snapshot_computer_no_hid(user: UserClient):
|
||||||
"""
|
"""Tests inserting a computer that doesn't generate a HID, neither
|
||||||
Tests inserting a computer that doesn't generate a HID, neither
|
|
||||||
some of its components.
|
some of its components.
|
||||||
"""
|
"""
|
||||||
# PC with 2 components. PC doesn't have HID and neither 1st component
|
# PC with 2 components. PC doesn't have HID and neither 1st component
|
||||||
|
@ -384,18 +380,14 @@ def test_test_data_storage(user: UserClient):
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
||||||
def test_snapshot_computer_monitor(user: UserClient):
|
def test_snapshot_computer_monitor(user: UserClient):
|
||||||
"""
|
"""Tests that a snapshot of computer monitor device create correctly."""
|
||||||
Tests that a snapshot of computer monitor device create correctly.
|
|
||||||
"""
|
|
||||||
s = file('computer-monitor.snapshot')
|
s = file('computer-monitor.snapshot')
|
||||||
snapshot_and_check(user, s, action_types=('RateMonitor',))
|
snapshot_and_check(user, s, action_types=('RateMonitor',))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
@pytest.mark.xfail(reason='Not implemented yet, new rate is need it')
|
||||||
def test_snapshot_mobile_smartphone_imei_manual_rate(user: UserClient):
|
def test_snapshot_mobile_smartphone_imei_manual_rate(user: UserClient):
|
||||||
"""
|
"""Tests that a snapshot of smartphone device is creat correctly."""
|
||||||
Tests that a snapshot of smartphone device is creat correctly.
|
|
||||||
"""
|
|
||||||
s = file('smartphone.snapshot')
|
s = file('smartphone.snapshot')
|
||||||
snapshot = snapshot_and_check(user, s, action_types=('VisualTest',))
|
snapshot = snapshot_and_check(user, s, action_types=('VisualTest',))
|
||||||
mobile, _ = user.get(res=m.Device, item=snapshot['device']['id'])
|
mobile, _ = user.get(res=m.Device, item=snapshot['device']['id'])
|
||||||
|
@ -404,24 +396,21 @@ def test_snapshot_mobile_smartphone_imei_manual_rate(user: UserClient):
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Test not developed')
|
@pytest.mark.xfail(reason='Test not developed')
|
||||||
def test_snapshot_components_none():
|
def test_snapshot_components_none():
|
||||||
"""
|
"""Tests that a snapshot without components does not remove them
|
||||||
Tests that a snapshot without components does not
|
from the computer.
|
||||||
remove them from the computer.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# TODO JN is really necessary in which cases??
|
# TODO JN is really necessary in which cases??
|
||||||
@pytest.mark.xfail(reason='Test not developed')
|
@pytest.mark.xfail(reason='Test not developed')
|
||||||
def test_snapshot_components_empty():
|
def test_snapshot_components_empty():
|
||||||
"""
|
"""Tests that a snapshot whose components are an empty list remove
|
||||||
Tests that a snapshot whose components are an empty list remove
|
|
||||||
all its components.
|
all its components.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def assert_similar_device(device1: dict, device2: dict):
|
def assert_similar_device(device1: dict, device2: dict):
|
||||||
"""
|
"""Like :class:`ereuse_devicehub.resources.device.models.Device.
|
||||||
Like :class:`ereuse_devicehub.resources.device.models.Device.
|
|
||||||
is_similar()` but adapted for testing.
|
is_similar()` but adapted for testing.
|
||||||
"""
|
"""
|
||||||
assert isinstance(device1, dict) and device1
|
assert isinstance(device1, dict) and device1
|
||||||
|
@ -431,9 +420,8 @@ def assert_similar_device(device1: dict, device2: dict):
|
||||||
|
|
||||||
|
|
||||||
def assert_similar_components(components1: List[dict], components2: List[dict]):
|
def assert_similar_components(components1: List[dict], components2: List[dict]):
|
||||||
"""
|
"""Asserts that the components in components1 are similar than
|
||||||
Asserts that the components in components1 are
|
the components in components2.
|
||||||
similar than the components in components2.
|
|
||||||
"""
|
"""
|
||||||
assert len(components1) == len(components2)
|
assert len(components1) == len(components2)
|
||||||
key = itemgetter('serialNumber')
|
key = itemgetter('serialNumber')
|
||||||
|
@ -447,8 +435,7 @@ def snapshot_and_check(user: UserClient,
|
||||||
input_snapshot: dict,
|
input_snapshot: dict,
|
||||||
action_types: Tuple[str, ...] = tuple(),
|
action_types: Tuple[str, ...] = tuple(),
|
||||||
perform_second_snapshot=True) -> dict:
|
perform_second_snapshot=True) -> dict:
|
||||||
"""
|
"""Performs a Snapshot and then checks if the result is ok:
|
||||||
Performs a Snapshot and then checks if the result is ok:
|
|
||||||
|
|
||||||
- There have been performed the types of actions and in the same
|
- There have been performed the types of actions and in the same
|
||||||
order as described in the passed-in ``action_types``.
|
order as described in the passed-in ``action_types``.
|
||||||
|
|
|
@ -80,8 +80,7 @@ def test_tag_post(app: Devicehub, user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_tag_post_etag(user: UserClient):
|
def test_tag_post_etag(user: UserClient):
|
||||||
"""
|
"""Ensures users cannot create eReuse.org tags through POST;
|
||||||
Ensures users cannot create eReuse.org tags through POST;
|
|
||||||
only terminal.
|
only terminal.
|
||||||
"""
|
"""
|
||||||
user.post({'id': 'FO-123456'}, res=Tag, status=CannotCreateETag)
|
user.post({'id': 'FO-123456'}, res=Tag, status=CannotCreateETag)
|
||||||
|
@ -121,8 +120,7 @@ def test_tag_get_device_from_tag_endpoint_no_tag(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: UserClient):
|
def test_tag_get_device_from_tag_endpoint_multiple_tags(app: Devicehub, user: UserClient):
|
||||||
"""
|
"""As above, but when there are two tags with the same ID, the
|
||||||
As above, but when there are two tags with the same ID, the
|
|
||||||
system should not return any of both (to be deterministic) so
|
system should not return any of both (to be deterministic) so
|
||||||
it should raise an exception.
|
it should raise an exception.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -18,8 +18,7 @@ from tests.conftest import app_context, create_user
|
||||||
|
|
||||||
@pytest.mark.usefixtures(app_context.__name__)
|
@pytest.mark.usefixtures(app_context.__name__)
|
||||||
def test_create_user_method_with_agent(app: Devicehub):
|
def test_create_user_method_with_agent(app: Devicehub):
|
||||||
"""
|
"""Tests creating an user through the main method.
|
||||||
Tests creating an user through the main method.
|
|
||||||
|
|
||||||
This method checks that the token is correct, too.
|
This method checks that the token is correct, too.
|
||||||
"""
|
"""
|
||||||
|
@ -63,8 +62,7 @@ def test_hash_password():
|
||||||
|
|
||||||
|
|
||||||
def test_login_success(client: Client, app: Devicehub):
|
def test_login_success(client: Client, app: Devicehub):
|
||||||
"""
|
"""Tests successfully performing login.
|
||||||
Tests successfully performing login.
|
|
||||||
This checks that:
|
This checks that:
|
||||||
|
|
||||||
- User is returned.
|
- User is returned.
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
"""
|
"""Tests that emulates the behaviour of a WorkbenchServer."""
|
||||||
Tests that emulates the behaviour of a WorkbenchServer.
|
|
||||||
"""
|
|
||||||
import json
|
import json
|
||||||
import math
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
import math
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ereuse_devicehub.client import UserClient
|
from ereuse_devicehub.client import UserClient
|
||||||
|
@ -17,8 +15,7 @@ from tests.conftest import file
|
||||||
|
|
||||||
|
|
||||||
def test_workbench_server_condensed(user: UserClient):
|
def test_workbench_server_condensed(user: UserClient):
|
||||||
"""
|
"""As :def:`.test_workbench_server_phases` but all the actions
|
||||||
As :def:`.test_workbench_server_phases` but all the actions
|
|
||||||
condensed in only one big ``Snapshot`` file, as described
|
condensed in only one big ``Snapshot`` file, as described
|
||||||
in the docs.
|
in the docs.
|
||||||
"""
|
"""
|
||||||
|
@ -72,8 +69,7 @@ def test_workbench_server_condensed(user: UserClient):
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='Functionality not yet developed.')
|
@pytest.mark.xfail(reason='Functionality not yet developed.')
|
||||||
def test_workbench_server_phases(user: UserClient):
|
def test_workbench_server_phases(user: UserClient):
|
||||||
"""
|
"""Tests the phases described in the docs section `Snapshots from
|
||||||
Tests the phases described in the docs section `Snapshots from
|
|
||||||
Workbench <http://devicehub.ereuse.org/
|
Workbench <http://devicehub.ereuse.org/
|
||||||
actions.html#snapshots-from-workbench>`_.
|
actions.html#snapshots-from-workbench>`_.
|
||||||
"""
|
"""
|
||||||
|
@ -170,8 +166,7 @@ def test_real_toshiba_11(user: UserClient):
|
||||||
|
|
||||||
|
|
||||||
def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
def test_snapshot_real_eee_1001pxd_with_rate(user: UserClient):
|
||||||
"""
|
"""Checks the values of the device, components,
|
||||||
Checks the values of the device, components,
|
|
||||||
actions and their relationships of a real pc.
|
actions and their relationships of a real pc.
|
||||||
"""
|
"""
|
||||||
s = file('real-eee-1001pxd.snapshot.11')
|
s = file('real-eee-1001pxd.snapshot.11')
|
||||||
|
|