Merge remote-tracking branch 'origin/testing' into feature/33-export-lot-description
# Conflicts: # ereuse_devicehub/resources/documents/documents.py # tests/test_documents.py
This commit is contained in:
commit
1fccea2bcc
|
@ -9,8 +9,8 @@ import ereuse_utils.cli
|
||||||
from ereuse_utils.session import DevicehubClient
|
from ereuse_utils.session import DevicehubClient
|
||||||
from flask.globals import _app_ctx_stack, g
|
from flask.globals import _app_ctx_stack, g
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from teal.teal import Teal
|
|
||||||
from teal.db import SchemaSQLAlchemy
|
from teal.db import SchemaSQLAlchemy
|
||||||
|
from teal.teal import Teal
|
||||||
|
|
||||||
from ereuse_devicehub.auth import Auth
|
from ereuse_devicehub.auth import Auth
|
||||||
from ereuse_devicehub.client import Client, UserClient
|
from ereuse_devicehub.client import Client, UserClient
|
||||||
|
@ -19,7 +19,6 @@ from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.dummy.dummy import Dummy
|
from ereuse_devicehub.dummy.dummy import Dummy
|
||||||
from ereuse_devicehub.resources.device.search import DeviceSearch
|
from ereuse_devicehub.resources.device.search import DeviceSearch
|
||||||
from ereuse_devicehub.resources.inventory import Inventory, InventoryDef
|
from ereuse_devicehub.resources.inventory import Inventory, InventoryDef
|
||||||
from ereuse_devicehub.resources.user import User
|
|
||||||
from ereuse_devicehub.templating import Environment
|
from ereuse_devicehub.templating import Environment
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,7 +116,6 @@ class Devicehub(Teal):
|
||||||
self.db.session.commit()
|
self.db.session.commit()
|
||||||
print('done.')
|
print('done.')
|
||||||
|
|
||||||
|
|
||||||
def _init_db(self, exclude_schema=None) -> bool:
|
def _init_db(self, exclude_schema=None) -> bool:
|
||||||
if exclude_schema:
|
if exclude_schema:
|
||||||
assert isinstance(self.db, SchemaSQLAlchemy)
|
assert isinstance(self.db, SchemaSQLAlchemy)
|
||||||
|
|
|
@ -101,7 +101,7 @@ class Dummy:
|
||||||
# Make one hdd ErasePhysical
|
# Make one hdd ErasePhysical
|
||||||
hdd = next(hdd for hdd in s['components'] if hdd['type'] == 'HardDrive')
|
hdd = next(hdd for hdd in s['components'] if hdd['type'] == 'HardDrive')
|
||||||
user1.post({'type': 'ErasePhysical', 'method': 'Shred', 'device': hdd['id']},
|
user1.post({'type': 'ErasePhysical', 'method': 'Shred', 'device': hdd['id']},
|
||||||
res=m.Action)
|
res=m.Action)
|
||||||
assert sample_pc
|
assert sample_pc
|
||||||
print('PC sample is', sample_pc)
|
print('PC sample is', sample_pc)
|
||||||
# Link tags and eTags
|
# Link tags and eTags
|
||||||
|
@ -132,25 +132,25 @@ class Dummy:
|
||||||
lot_user4, _ = user4.post({'name': 'LoteJordi'}, res=Lot)
|
lot_user4, _ = user4.post({'name': 'LoteJordi'}, res=Lot)
|
||||||
|
|
||||||
lot, _ = user1.post({},
|
lot, _ = user1.post({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot_user['id']),
|
item='{}/devices'.format(lot_user['id']),
|
||||||
query=[('id', pc) for pc in itertools.islice(pcs, 1, 4)])
|
query=[('id', pc) for pc in itertools.islice(pcs, 1, 4)])
|
||||||
assert len(lot['devices'])
|
assert len(lot['devices'])
|
||||||
|
|
||||||
lot2, _ = user2.post({},
|
lot2, _ = user2.post({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot_user2['id']),
|
item='{}/devices'.format(lot_user2['id']),
|
||||||
query=[('id', pc) for pc in itertools.islice(pcs, 4, 6)])
|
query=[('id', pc) for pc in itertools.islice(pcs, 4, 6)])
|
||||||
|
|
||||||
lot3, _ = user3.post({},
|
lot3, _ = user3.post({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot_user3['id']),
|
item='{}/devices'.format(lot_user3['id']),
|
||||||
query=[('id', pc) for pc in itertools.islice(pcs, 11, 14)])
|
query=[('id', pc) for pc in itertools.islice(pcs, 11, 14)])
|
||||||
|
|
||||||
lot4, _ = user4.post({},
|
lot4, _ = user4.post({},
|
||||||
res=Lot,
|
res=Lot,
|
||||||
item='{}/devices'.format(lot_user4['id']),
|
item='{}/devices'.format(lot_user4['id']),
|
||||||
query=[('id', pc) for pc in itertools.islice(pcs, 14, 16)])
|
query=[('id', pc) for pc in itertools.islice(pcs, 14, 16)])
|
||||||
|
|
||||||
# Keep this at the bottom
|
# Keep this at the bottom
|
||||||
inventory, _ = user1.get(res=Device)
|
inventory, _ = user1.get(res=Device)
|
||||||
|
@ -168,7 +168,7 @@ class Dummy:
|
||||||
user1.post({'type': m.Prepare.t, 'devices': [sample_pc]}, res=m.Action)
|
user1.post({'type': m.Prepare.t, 'devices': [sample_pc]}, res=m.Action)
|
||||||
user1.post({'type': m.Ready.t, 'devices': [sample_pc]}, res=m.Action)
|
user1.post({'type': m.Ready.t, 'devices': [sample_pc]}, res=m.Action)
|
||||||
user1.post({'type': m.Price.t, 'device': sample_pc, 'currency': 'EUR', 'price': 85},
|
user1.post({'type': m.Price.t, 'device': sample_pc, 'currency': 'EUR', 'price': 85},
|
||||||
res=m.Action)
|
res=m.Action)
|
||||||
|
|
||||||
# todo test reserve
|
# todo test reserve
|
||||||
user1.post( # Sell device
|
user1.post( # Sell device
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import os
|
|
||||||
from logging.config import fileConfig
|
from logging.config import fileConfig
|
||||||
|
|
||||||
from sqlalchemy import engine_from_config
|
|
||||||
from sqlalchemy import pool
|
|
||||||
from sqlalchemy import create_engine
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
|
||||||
from alembic import context
|
from alembic import context
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
from ereuse_devicehub.config import DevicehubConfig
|
from ereuse_devicehub.config import DevicehubConfig
|
||||||
|
|
||||||
|
@ -24,10 +20,11 @@ fileConfig(config.config_file_name)
|
||||||
# from myapp import mymodel
|
# from myapp import mymodel
|
||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
# target_metadata = None
|
# target_metadata = None
|
||||||
from ereuse_devicehub.db import db
|
|
||||||
from ereuse_devicehub.resources.models import Thing
|
from ereuse_devicehub.resources.models import Thing
|
||||||
|
|
||||||
target_metadata = Thing.metadata
|
target_metadata = Thing.metadata
|
||||||
|
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
# other values from the config, defined by the needs of env.py,
|
||||||
# can be acquired:
|
# can be acquired:
|
||||||
# my_important_option = config.get_main_option("my_important_option")
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
|
|
@ -5,14 +5,10 @@ Revises: 151253ac5c55
|
||||||
Create Date: 2020-06-30 17:41:28.611314
|
Create Date: 2020-06-30 17:41:28.611314
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from alembic import op
|
|
||||||
from alembic import context
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
import sqlalchemy_utils
|
from alembic import context
|
||||||
|
from alembic import op
|
||||||
from sqlalchemy.dialects import postgresql
|
from sqlalchemy.dialects import postgresql
|
||||||
import citext
|
|
||||||
import teal
|
|
||||||
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = 'b9b0ee7d9dca'
|
revision = 'b9b0ee7d9dca'
|
||||||
|
@ -27,6 +23,7 @@ def get_inv():
|
||||||
raise ValueError("Inventory value is not specified")
|
raise ValueError("Inventory value is not specified")
|
||||||
return INV
|
return INV
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
op.add_column('tag', sa.Column('owner_id', postgresql.UUID(), nullable=True), schema=f'{get_inv()}')
|
op.add_column('tag', sa.Column('owner_id', postgresql.UUID(), nullable=True), schema=f'{get_inv()}')
|
||||||
op.create_foreign_key("fk_tag_owner_id_user_id",
|
op.create_foreign_key("fk_tag_owner_id_user_id",
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -552,5 +552,6 @@ class MigrateTo(Migrate):
|
||||||
class MigrateFrom(Migrate):
|
class MigrateFrom(Migrate):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Transferred(ActionWithMultipleDevices):
|
class Transferred(ActionWithMultipleDevices):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from itertools import groupby
|
||||||
from typing import Dict, Iterable, Tuple
|
from typing import Dict, Iterable, Tuple
|
||||||
|
|
||||||
from ereuse_devicehub.resources.action.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
from ereuse_devicehub.resources.action.models import BenchmarkDataStorage, BenchmarkProcessor, \
|
||||||
BenchmarkProcessorSysbench, RateComputer, VisualTest
|
BenchmarkProcessorSysbench, RateComputer
|
||||||
from ereuse_devicehub.resources.action.rate.rate import BaseRate
|
from ereuse_devicehub.resources.action.rate.rate import BaseRate
|
||||||
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Processor, \
|
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Processor, \
|
||||||
RamModule
|
RamModule
|
||||||
|
|
|
@ -454,8 +454,6 @@ class MigrateTo(Migrate):
|
||||||
class MigrateFrom(Migrate):
|
class MigrateFrom(Migrate):
|
||||||
__doc__ = m.MigrateFrom.__doc__
|
__doc__ = m.MigrateFrom.__doc__
|
||||||
|
|
||||||
|
|
||||||
class Transferred(ActionWithMultipleDevices):
|
class Transferred(ActionWithMultipleDevices):
|
||||||
__doc__ = m.Transferred.__doc__
|
__doc__ = m.Transferred.__doc__
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from distutils.version import StrictVersion
|
||||||
from typing import List
|
from typing import List
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from flask import current_app as app, request
|
from flask import current_app as app, request, g
|
||||||
from sqlalchemy.util import OrderedSet
|
from sqlalchemy.util import OrderedSet
|
||||||
from teal.marshmallow import ValidationError
|
from teal.marshmallow import ValidationError
|
||||||
from teal.resource import View
|
from teal.resource import View
|
||||||
|
@ -13,6 +13,7 @@ from ereuse_devicehub.resources.action.models import Action, RateComputer, Snaps
|
||||||
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer
|
from ereuse_devicehub.resources.device.models import Component, Computer
|
||||||
from ereuse_devicehub.resources.enums import SnapshotSoftware, Severity
|
from ereuse_devicehub.resources.enums import SnapshotSoftware, Severity
|
||||||
|
from ereuse_devicehub.resources.user.exceptions import InsufficientPermission
|
||||||
|
|
||||||
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ class ActionView(View):
|
||||||
# Note that if we set the device / components into the snapshot
|
# Note that if we set the device / components into the snapshot
|
||||||
# model object, when we flush them to the db we will flush
|
# model object, when we flush them to the db we will flush
|
||||||
# snapshot, and we want to wait to flush snapshot at the end
|
# snapshot, and we want to wait to flush snapshot at the end
|
||||||
|
|
||||||
device = snapshot_json.pop('device') # type: Computer
|
device = snapshot_json.pop('device') # type: Computer
|
||||||
components = None
|
components = None
|
||||||
if snapshot_json['software'] == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid):
|
if snapshot_json['software'] == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid):
|
||||||
|
@ -73,6 +75,7 @@ class ActionView(View):
|
||||||
assert not device.actions_one
|
assert not device.actions_one
|
||||||
assert all(not c.actions_one for c in components) if components else True
|
assert all(not c.actions_one for c in components) if components else True
|
||||||
db_device, remove_actions = resource_def.sync.run(device, components)
|
db_device, remove_actions = resource_def.sync.run(device, components)
|
||||||
|
|
||||||
del device # Do not use device anymore
|
del device # Do not use device anymore
|
||||||
snapshot.device = db_device
|
snapshot.device = db_device
|
||||||
snapshot.actions |= remove_actions | actions_device # Set actions to snapshot
|
snapshot.actions |= remove_actions | actions_device # Set actions to snapshot
|
||||||
|
@ -87,8 +90,11 @@ class ActionView(View):
|
||||||
component.actions_one |= actions
|
component.actions_one |= actions
|
||||||
snapshot.actions |= actions
|
snapshot.actions |= actions
|
||||||
|
|
||||||
# Compute ratings
|
|
||||||
if snapshot.software == SnapshotSoftware.Workbench:
|
if snapshot.software == SnapshotSoftware.Workbench:
|
||||||
|
# Check ownership of (non-component) device to from current.user
|
||||||
|
if db_device.owner_id != g.user.id:
|
||||||
|
raise InsufficientPermission()
|
||||||
|
# Compute ratings
|
||||||
try:
|
try:
|
||||||
rate_computer, price = RateComputer.compute(db_device)
|
rate_computer, price = RateComputer.compute(db_device)
|
||||||
except CannotRate:
|
except CannotRate:
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import pathlib
|
|
||||||
from typing import Callable, Iterable, Tuple
|
from typing import Callable, Iterable, Tuple
|
||||||
|
|
||||||
from teal.resource import Converters, Resource
|
from teal.resource import Converters, Resource
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
|
||||||
from ereuse_devicehub.resources.deliverynote import schemas
|
from ereuse_devicehub.resources.deliverynote import schemas
|
||||||
from ereuse_devicehub.resources.deliverynote.views import DeliverynoteView
|
from ereuse_devicehub.resources.deliverynote.views import DeliverynoteView
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,38 @@
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
from citext import CIText
|
from citext import CIText
|
||||||
from flask import g
|
from flask import g
|
||||||
from typing import Iterable
|
|
||||||
from sqlalchemy.types import ARRAY
|
|
||||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||||
from teal.db import CASCADE_OWN, check_range, IntEnum
|
from teal.db import check_range, IntEnum
|
||||||
from teal.resource import url_for_resource
|
from teal.resource import url_for_resource
|
||||||
|
|
||||||
from ereuse_devicehub.db import db, f
|
from ereuse_devicehub.db import db
|
||||||
|
from ereuse_devicehub.resources.enums import TransferState
|
||||||
|
from ereuse_devicehub.resources.lot.models import Lot
|
||||||
from ereuse_devicehub.resources.models import Thing
|
from ereuse_devicehub.resources.models import Thing
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
from ereuse_devicehub.resources.lot.models import Lot
|
|
||||||
from ereuse_devicehub.resources.enums import TransferState
|
|
||||||
|
|
||||||
|
|
||||||
class Deliverynote(Thing):
|
class Deliverynote(Thing):
|
||||||
id = db.Column(UUID(as_uuid=True), primary_key=True) # uuid is generated on init by default
|
id = db.Column(UUID(as_uuid=True), primary_key=True) # uuid is generated on init by default
|
||||||
document_id = db.Column(CIText(), nullable=False)
|
document_id = db.Column(CIText(), nullable=False)
|
||||||
creator_id = db.Column(UUID(as_uuid=True),
|
creator_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.id)
|
default=lambda: g.user.id)
|
||||||
creator = db.relationship(User, primaryjoin=creator_id == User.id)
|
creator = db.relationship(User, primaryjoin=creator_id == User.id)
|
||||||
supplier_email = db.Column(CIText(),
|
supplier_email = db.Column(CIText(),
|
||||||
db.ForeignKey(User.email),
|
db.ForeignKey(User.email),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.email)
|
default=lambda: g.user.email)
|
||||||
supplier = db.relationship(User, primaryjoin=lambda: Deliverynote.supplier_email == User.email)
|
supplier = db.relationship(User, primaryjoin=lambda: Deliverynote.supplier_email == User.email)
|
||||||
receiver_address = db.Column(CIText(),
|
receiver_address = db.Column(CIText(),
|
||||||
db.ForeignKey(User.email),
|
db.ForeignKey(User.email),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.email)
|
default=lambda: g.user.email)
|
||||||
receiver = db.relationship(User, primaryjoin=lambda: Deliverynote.receiver_address == User.email)
|
receiver = db.relationship(User, primaryjoin=lambda: Deliverynote.receiver_address == User.email)
|
||||||
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
|
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
|
||||||
date.comment = 'The date the DeliveryNote initiated'
|
date.comment = 'The date the DeliveryNote initiated'
|
||||||
|
@ -49,15 +48,15 @@ class Deliverynote(Thing):
|
||||||
transfer_state.comment = TransferState.__doc__
|
transfer_state.comment = TransferState.__doc__
|
||||||
ethereum_address = db.Column(CIText(), unique=True, default=None)
|
ethereum_address = db.Column(CIText(), unique=True, default=None)
|
||||||
lot_id = db.Column(UUID(as_uuid=True),
|
lot_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(Lot.id),
|
db.ForeignKey(Lot.id),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
lot = db.relationship(Lot,
|
lot = db.relationship(Lot,
|
||||||
backref=db.backref('deliverynote', uselist=False, lazy=True),
|
backref=db.backref('deliverynote', uselist=False, lazy=True),
|
||||||
lazy=True,
|
lazy=True,
|
||||||
primaryjoin=Lot.id == lot_id)
|
primaryjoin=Lot.id == lot_id)
|
||||||
|
|
||||||
def __init__(self, document_id: str, deposit: str, date,
|
def __init__(self, document_id: str, deposit: str, date,
|
||||||
supplier_email: str,
|
supplier_email: str,
|
||||||
expected_devices: Iterable,
|
expected_devices: Iterable,
|
||||||
transfer_state: TransferState) -> None:
|
transfer_state: TransferState) -> None:
|
||||||
"""Initializes a delivery note
|
"""Initializes a delivery note
|
||||||
|
|
|
@ -24,13 +24,13 @@ class Lot(Thing):
|
||||||
description = ... # type: Column
|
description = ... # type: Column
|
||||||
all_devices = ... # type: relationship
|
all_devices = ... # type: relationship
|
||||||
parents = ... # type: relationship
|
parents = ... # type: relationship
|
||||||
deposit = ... # type: Column
|
deposit = ... # type: Column
|
||||||
owner_address = ... # type: Column
|
owner_address = ... # type: Column
|
||||||
owner = ... # type: relationship
|
owner = ... # type: relationship
|
||||||
transfer_state = ... # type: Column
|
transfer_state = ... # type: Column
|
||||||
receiver_address = ... # type: Column
|
receiver_address = ... # type: Column
|
||||||
receiver = ... # type: relationship
|
receiver = ... # type: relationship
|
||||||
deliverynote_address = ... # type: Column
|
deliverynote_address = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, name: str, closed: bool = closed.default.arg) -> None:
|
def __init__(self, name: str, closed: bool = closed.default.arg) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -43,10 +43,10 @@ class Lot(Thing):
|
||||||
self.all_devices = ... # type: Set[Device]
|
self.all_devices = ... # type: Set[Device]
|
||||||
self.parents = ... # type: Set[Lot]
|
self.parents = ... # type: Set[Lot]
|
||||||
self.children = ... # type: Set[Lot]
|
self.children = ... # type: Set[Lot]
|
||||||
self.owner_address = ... # type: UUID
|
self.owner_address = ... # type: UUID
|
||||||
self.transfer_state = ...
|
self.transfer_state = ...
|
||||||
self.receiver_address = ... # type: str
|
self.receiver_address = ... # type: str
|
||||||
self.deliverynote_address = ... # type: str
|
self.deliverynote_address = ... # type: str
|
||||||
|
|
||||||
def add_children(self, *children: Union[Lot, uuid.UUID]):
|
def add_children(self, *children: Union[Lot, uuid.UUID]):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
from marshmallow import fields as f
|
from marshmallow import fields as f
|
||||||
from teal.marshmallow import SanitizedStr, URL, EnumField
|
from teal.marshmallow import SanitizedStr, EnumField
|
||||||
|
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
from ereuse_devicehub.resources.deliverynote import models as m
|
from ereuse_devicehub.resources.deliverynote import models as m
|
||||||
from ereuse_devicehub.resources.user import schemas as s_user
|
from ereuse_devicehub.resources.enums import TransferState
|
||||||
from ereuse_devicehub.resources.device import schemas as s_device
|
|
||||||
from ereuse_devicehub.resources.models import STR_SIZE
|
from ereuse_devicehub.resources.models import STR_SIZE
|
||||||
from ereuse_devicehub.resources.schemas import Thing
|
from ereuse_devicehub.resources.schemas import Thing
|
||||||
from ereuse_devicehub.resources.enums import TransferState
|
from ereuse_devicehub.resources.user import schemas as s_user
|
||||||
|
|
||||||
|
|
||||||
class Deliverynote(Thing):
|
class Deliverynote(Thing):
|
||||||
|
@ -21,7 +20,7 @@ class Deliverynote(Thing):
|
||||||
receiver = NestedOn(s_user.User, dump_only=True)
|
receiver = NestedOn(s_user.User, dump_only=True)
|
||||||
date = f.DateTime('iso', required=True)
|
date = f.DateTime('iso', required=True)
|
||||||
deposit = f.Integer(validate=f.validate.Range(min=0, max=100),
|
deposit = f.Integer(validate=f.validate.Range(min=0, max=100),
|
||||||
description=m.Deliverynote.deposit.__doc__)
|
description=m.Deliverynote.deposit.__doc__)
|
||||||
ethereum_address = f.String(description='User identifier address inside the Blockchain')
|
ethereum_address = f.String(description='User identifier address inside the Blockchain')
|
||||||
expected_devices = f.List(f.Dict, required=True, data_key='expectedDevices')
|
expected_devices = f.List(f.Dict, required=True, data_key='expectedDevices')
|
||||||
transferred_devices = f.List(f.Integer(), required=False, data_key='transferredDevices')
|
transferred_devices = f.List(f.Integer(), required=False, data_key='transferredDevices')
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
import datetime
|
import datetime
|
||||||
import uuid
|
import uuid
|
||||||
from collections import deque
|
|
||||||
from enum import Enum
|
|
||||||
from typing import Dict, List, Set, Union
|
|
||||||
|
|
||||||
import marshmallow as ma
|
from flask import Response, request
|
||||||
import teal.cache
|
|
||||||
from flask import Response, jsonify, request
|
|
||||||
from marshmallow import Schema as MarshmallowSchema, fields as f
|
|
||||||
from teal.marshmallow import EnumField
|
|
||||||
from teal.resource import View
|
from teal.resource import View
|
||||||
from sqlalchemy.orm import joinedload
|
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.query import things_response
|
|
||||||
from ereuse_devicehub.resources.deliverynote.models import Deliverynote
|
from ereuse_devicehub.resources.deliverynote.models import Deliverynote
|
||||||
from ereuse_devicehub.resources.lot.models import Lot
|
from ereuse_devicehub.resources.lot.models import Lot
|
||||||
from ereuse_devicehub.resources.device.models import Computer
|
|
||||||
|
|
||||||
|
|
||||||
class DeliverynoteView(View):
|
class DeliverynoteView(View):
|
||||||
|
@ -53,7 +43,7 @@ class DeliverynoteView(View):
|
||||||
# if devKey in device_fields:
|
# if devKey in device_fields:
|
||||||
# for dev in computers:
|
# for dev in computers:
|
||||||
# setattr(dev, devKey, value)
|
# setattr(dev, devKey, value)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return Response(status=204)
|
return Response(status=204)
|
||||||
|
|
||||||
|
|
|
@ -7,17 +7,17 @@ from typing import Dict, List, Set
|
||||||
|
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
from citext import CIText
|
from citext import CIText
|
||||||
from flask import g
|
|
||||||
from ereuse_utils.naming import HID_CONVERSION_DOC, Naming
|
from ereuse_utils.naming import HID_CONVERSION_DOC, Naming
|
||||||
|
from flask import g
|
||||||
from more_itertools import unique_everseen
|
from more_itertools import unique_everseen
|
||||||
from sqlalchemy import BigInteger, Boolean, Column, Enum as DBEnum, Float, ForeignKey, Integer, \
|
from sqlalchemy import BigInteger, Boolean, Column, Enum as DBEnum, Float, ForeignKey, Integer, \
|
||||||
Sequence, SmallInteger, Unicode, inspect, text
|
Sequence, SmallInteger, Unicode, inspect, text
|
||||||
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy.ext.declarative import declared_attr
|
from sqlalchemy.ext.declarative import declared_attr
|
||||||
from sqlalchemy.ext.hybrid import hybrid_property
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
from sqlalchemy.orm import ColumnProperty, backref, relationship, validates
|
from sqlalchemy.orm import ColumnProperty, backref, relationship, validates
|
||||||
from sqlalchemy.util import OrderedSet
|
from sqlalchemy.util import OrderedSet
|
||||||
from sqlalchemy_utils import ColorType
|
from sqlalchemy_utils import ColorType
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
|
||||||
from stdnum import imei, meid
|
from stdnum import imei, meid
|
||||||
from teal.db import CASCADE_DEL, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, URL, \
|
from teal.db import CASCADE_DEL, POLYMORPHIC_ID, POLYMORPHIC_ON, ResourceNotFound, URL, \
|
||||||
check_lower, check_range, IntEnum
|
check_lower, check_range, IntEnum
|
||||||
|
@ -382,17 +382,17 @@ class Computer(Device):
|
||||||
It is a subset of the Linux definition of DMI / DMI decode.
|
It is a subset of the Linux definition of DMI / DMI decode.
|
||||||
"""
|
"""
|
||||||
ethereum_address = Column(CIText(), unique=True, default=None)
|
ethereum_address = Column(CIText(), unique=True, default=None)
|
||||||
deposit = Column(Integer, check_range('deposit',min=0,max=100), default=0)
|
deposit = Column(Integer, check_range('deposit', min=0, max=100), default=0)
|
||||||
owner_id = db.Column(UUID(as_uuid=True),
|
owner_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.id)
|
default=lambda: g.user.id)
|
||||||
author = db.relationship(User, primaryjoin=owner_id == User.id)
|
author = db.relationship(User, primaryjoin=owner_id == User.id)
|
||||||
transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False)
|
transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False)
|
||||||
transfer_state.comment = TransferState.__doc__
|
transfer_state.comment = TransferState.__doc__
|
||||||
receiver_id = db.Column(UUID(as_uuid=True),
|
receiver_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
nullable=True)
|
nullable=True)
|
||||||
receiver = db.relationship(User, primaryjoin=receiver_id == User.id)
|
receiver = db.relationship(User, primaryjoin=receiver_id == User.id)
|
||||||
deliverynote_address = db.Column(CIText(), nullable=True)
|
deliverynote_address = db.Column(CIText(), nullable=True)
|
||||||
|
|
||||||
|
|
|
@ -141,21 +141,21 @@ class DisplayMixin:
|
||||||
class Computer(DisplayMixin, Device):
|
class Computer(DisplayMixin, Device):
|
||||||
components = ... # type: Column
|
components = ... # type: Column
|
||||||
chassis = ... # type: Column
|
chassis = ... # type: Column
|
||||||
deposit = ... # type: Column
|
deposit = ... # type: Column
|
||||||
owner_address = ... # type: Column
|
owner_address = ... # type: Column
|
||||||
transfer_state = ... # type: Column
|
transfer_state = ... # type: Column
|
||||||
receiver_address = ... # type: Column
|
receiver_address = ... # type: Column
|
||||||
deliverynote_address = ... # type: Column
|
deliverynote_address = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.components = ... # type: Set[Component]
|
self.components = ... # type: Set[Component]
|
||||||
self.actions_parent = ... # type: Set[e.Action]
|
self.actions_parent = ... # type: Set[e.Action]
|
||||||
self.chassis = ... # type: ComputerChassis
|
self.chassis = ... # type: ComputerChassis
|
||||||
self.owner_address = ... # type: UUID
|
self.owner_address = ... # type: UUID
|
||||||
self.transfer_state = ...
|
self.transfer_state = ...
|
||||||
self.receiver_address = ... # type: str
|
self.receiver_address = ... # type: str
|
||||||
self.deliverynote_address = ... # type: str
|
self.deliverynote_address = ... # type: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def actions(self) -> List:
|
def actions(self) -> List:
|
||||||
|
@ -219,7 +219,7 @@ class Mobile(Device):
|
||||||
meid = ... # type: Column
|
meid = ... # type: Column
|
||||||
ram_size = ... # type: Column
|
ram_size = ... # type: Column
|
||||||
data_storage_size = ... # type: Column
|
data_storage_size = ... # type: Column
|
||||||
display_size = ... # type: Column
|
display_size = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, **kwargs) -> None:
|
def __init__(self, **kwargs) -> None:
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
|
@ -14,7 +14,6 @@ from ereuse_devicehub.resources import enums
|
||||||
from ereuse_devicehub.resources.device import models as m, states
|
from ereuse_devicehub.resources.device import models as m, states
|
||||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
||||||
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
||||||
from ereuse_devicehub.resources.user import schemas as s_user
|
|
||||||
|
|
||||||
|
|
||||||
class Device(Thing):
|
class Device(Thing):
|
||||||
|
@ -124,7 +123,7 @@ class Computer(Device):
|
||||||
description=m.Computer.privacy.__doc__)
|
description=m.Computer.privacy.__doc__)
|
||||||
ethereum_address = SanitizedStr(validate=f.validate.Length(max=42))
|
ethereum_address = SanitizedStr(validate=f.validate.Length(max=42))
|
||||||
deposit = Integer(validate=f.validate.Range(min=0, max=100),
|
deposit = Integer(validate=f.validate.Range(min=0, max=100),
|
||||||
description=m.Computer.deposit.__doc__)
|
description=m.Computer.deposit.__doc__)
|
||||||
# author_id = NestedOn(s_user.User,only_query='author_id')
|
# author_id = NestedOn(s_user.User,only_query='author_id')
|
||||||
owner_id = UUID(data_key='ownerID')
|
owner_id = UUID(data_key='ownerID')
|
||||||
transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment)
|
transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment)
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
<!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">
|
||||||
<script src="https://use.fontawesome.com/7553aecc27.js"></script>
|
<script src="https://use.fontawesome.com/7553aecc27.js"></script>
|
||||||
<title>Devicehub | {{ device.__format__('t') }}</title>
|
<title>Devicehub | {{ device.__format__('t') }}</title>
|
||||||
<style>
|
<style>
|
||||||
/*Sticky footer*/
|
/*Sticky footer*/
|
||||||
html {
|
html {
|
||||||
|
@ -36,174 +36,174 @@
|
||||||
<a href="https://www.usody.com/" target="_blank">
|
<a href="https://www.usody.com/" target="_blank">
|
||||||
<h1 align="center">Usody Public Link</h1>
|
<h1 align="center">Usody Public Link</h1>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="page-header col-md-6 col-md-offset-3">
|
<div class="page-header col-md-6 col-md-offset-3">
|
||||||
<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="row">
|
<div class="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<ul>
|
<ul>
|
||||||
{% for key, value in device.physical_properties.items() %}
|
{% for key, value in device.physical_properties.items() %}
|
||||||
<li>{{ key }}: {{ value }}
|
<li>{{ key }}: {{ value }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% if isinstance(device, d.Computer) %}
|
{% if isinstance(device, d.Computer) %}
|
||||||
<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>
|
||||||
Processor Rate = {% if device.rate %}
|
Processor 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>
|
||||||
RAM Rate = {% if device.rate %}
|
RAM 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>
|
||||||
Data Storage Rate = {% if device.rate %}
|
Data Storage 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>
|
||||||
|
<h4>Public traceability log of the device</h4>
|
||||||
|
<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>
|
||||||
|
{% 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>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<h4>Public traceability log of the device</h4>
|
|
||||||
<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>
|
|
||||||
{% 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>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<footer class="container-fluid footer">
|
<footer class="container-fluid footer">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -6,8 +6,7 @@ import marshmallow
|
||||||
from flask import g, current_app as app, render_template, request, Response
|
from flask import g, current_app as app, render_template, request, Response
|
||||||
from flask.json import jsonify
|
from flask.json import jsonify
|
||||||
from flask_sqlalchemy import Pagination
|
from flask_sqlalchemy import Pagination
|
||||||
from marshmallow import fields, fields as f, validate as v, ValidationError, \
|
from marshmallow import fields, fields as f, validate as v, Schema as MarshmallowSchema
|
||||||
Schema as MarshmallowSchema
|
|
||||||
from teal import query
|
from teal import query
|
||||||
from teal.cache import cache
|
from teal.cache import cache
|
||||||
from teal.resource import View
|
from teal.resource import View
|
||||||
|
@ -20,9 +19,9 @@ from ereuse_devicehub.resources.action import models as actions
|
||||||
from ereuse_devicehub.resources.device import states
|
from ereuse_devicehub.resources.device import states
|
||||||
from ereuse_devicehub.resources.device.models import Device, Manufacturer, Computer
|
from ereuse_devicehub.resources.device.models import Device, Manufacturer, Computer
|
||||||
from ereuse_devicehub.resources.device.search import DeviceSearch
|
from ereuse_devicehub.resources.device.search import DeviceSearch
|
||||||
|
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
||||||
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
from ereuse_devicehub.resources.lot.models import LotDeviceDescendants
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
|
||||||
|
|
||||||
|
|
||||||
class OfType(f.Str):
|
class OfType(f.Str):
|
||||||
|
@ -103,14 +102,15 @@ class DeviceView(View):
|
||||||
if isinstance(dev, Computer):
|
if isinstance(dev, Computer):
|
||||||
resource_def = app.resources['Computer']
|
resource_def = app.resources['Computer']
|
||||||
# TODO check how to handle the 'actions_one'
|
# TODO check how to handle the 'actions_one'
|
||||||
patch_schema = resource_def.SCHEMA(only=['ethereum_address', 'transfer_state', 'deliverynote_address', 'actions_one'], partial=True)
|
patch_schema = resource_def.SCHEMA(
|
||||||
|
only=['ethereum_address', 'transfer_state', 'deliverynote_address', 'actions_one'], partial=True)
|
||||||
json = request.get_json(schema=patch_schema)
|
json = request.get_json(schema=patch_schema)
|
||||||
# TODO check how to handle the 'actions_one'
|
# TODO check how to handle the 'actions_one'
|
||||||
json.pop('actions_one')
|
json.pop('actions_one')
|
||||||
if not dev:
|
if not dev:
|
||||||
raise ValueError('Device non existent')
|
raise ValueError('Device non existent')
|
||||||
for key, value in json.items():
|
for key, value in json.items():
|
||||||
setattr(dev,key,value)
|
setattr(dev, key, value)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return Response(status=204)
|
return Response(status=204)
|
||||||
raise ValueError('Cannot patch a non computer')
|
raise ValueError('Cannot patch a non computer')
|
||||||
|
@ -157,20 +157,20 @@ class DeviceView(View):
|
||||||
query = self.visibility_filter(query)
|
query = self.visibility_filter(query)
|
||||||
return query.filter(*args['filter']).order_by(*args['sort'])
|
return query.filter(*args['filter']).order_by(*args['sort'])
|
||||||
|
|
||||||
|
|
||||||
def visibility_filter(self, query):
|
def visibility_filter(self, query):
|
||||||
filterqs = request.args.get('filter', None)
|
filterqs = request.args.get('filter', None)
|
||||||
if (filterqs and
|
if (filterqs and
|
||||||
'lot' not in filterqs):
|
'lot' not in filterqs):
|
||||||
query = query.filter((Computer.id == Device.id), (Computer.owner_id == g.user.id))
|
query = query.filter((Computer.id == Device.id), (Computer.owner_id == g.user.id))
|
||||||
pass
|
pass
|
||||||
return query
|
return query
|
||||||
|
|
||||||
class DeviceMergeView(View):
|
|
||||||
|
|
||||||
|
class DeviceMergeView(View):
|
||||||
"""View for merging two devices
|
"""View for merging two devices
|
||||||
Ex. ``device/<id>/merge/id=X``.
|
Ex. ``device/<id>/merge/id=X``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class FindArgs(MarshmallowSchema):
|
class FindArgs(MarshmallowSchema):
|
||||||
id = fields.Integer()
|
id = fields.Integer()
|
||||||
|
|
||||||
|
@ -197,10 +197,13 @@ class DeviceMergeView(View):
|
||||||
This operation is highly costly as it forces refreshing
|
This operation is highly costly as it forces refreshing
|
||||||
many models in session.
|
many models in session.
|
||||||
"""
|
"""
|
||||||
snapshots = sorted(filterfalse(lambda x: not isinstance(x, actions.Snapshot), (base_device.actions + with_device.actions)))
|
snapshots = sorted(
|
||||||
workbench_snapshots = [ s for s in snapshots if s.software == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid)]
|
filterfalse(lambda x: not isinstance(x, actions.Snapshot), (base_device.actions + with_device.actions)))
|
||||||
latest_snapshot_device = [ d for d in (base_device, with_device) if d.id == snapshots[-1].device.id][0]
|
workbench_snapshots = [s for s in snapshots if
|
||||||
latest_snapshotworkbench_device = [ d for d in (base_device, with_device) if d.id == workbench_snapshots[-1].device.id][0]
|
s.software == (SnapshotSoftware.Workbench or SnapshotSoftware.WorkbenchAndroid)]
|
||||||
|
latest_snapshot_device = [d for d in (base_device, with_device) if d.id == snapshots[-1].device.id][0]
|
||||||
|
latest_snapshotworkbench_device = \
|
||||||
|
[d for d in (base_device, with_device) if d.id == workbench_snapshots[-1].device.id][0]
|
||||||
# Adding actions of with_device
|
# Adding actions of with_device
|
||||||
with_actions_one = [a for a in with_device.actions if isinstance(a, actions.ActionWithOneDevice)]
|
with_actions_one = [a for a in with_device.actions if isinstance(a, actions.ActionWithOneDevice)]
|
||||||
with_actions_multiple = [a for a in with_device.actions if isinstance(a, actions.ActionWithMultipleDevices)]
|
with_actions_multiple = [a for a in with_device.actions if isinstance(a, actions.ActionWithMultipleDevices)]
|
||||||
|
|
|
@ -30,31 +30,23 @@ class DeviceRow(OrderedDict):
|
||||||
self['Tag 1'] = self['Tag 2'] = self['Tag 3'] = ''
|
self['Tag 1'] = self['Tag 2'] = self['Tag 3'] = ''
|
||||||
for i, tag in zip(range(1, 3), device.tags):
|
for i, tag in zip(range(1, 3), device.tags):
|
||||||
self['Tag {}'.format(i)] = format(tag)
|
self['Tag {}'.format(i)] = format(tag)
|
||||||
self['Serial Number'] = device.serial_number
|
self['Serial Number'] = convert_none_to_empty_str(device.serial_number)
|
||||||
self['Model'] = device.model
|
self['Model'] = convert_none_to_empty_str(device.model)
|
||||||
self['Manufacturer'] = device.manufacturer
|
self['Manufacturer'] = convert_none_to_empty_str(device.manufacturer)
|
||||||
# self['State'] = device.last_action_of()
|
|
||||||
self['Registered in'] = format(device.created, '%c')
|
self['Registered in'] = format(device.created, '%c')
|
||||||
try:
|
try:
|
||||||
self['Physical state'] = device.last_action_of(*states.Physical.actions()).t
|
self['Physical state'] = device.last_action_of(*states.Physical.actions()).t
|
||||||
except:
|
except LookupError:
|
||||||
self['Physical state'] = ''
|
self['Physical state'] = ''
|
||||||
try:
|
try:
|
||||||
self['Trading state'] = device.last_action_of(*states.Trading.actions()).t
|
self['Trading state'] = device.last_action_of(*states.Trading.actions()).t
|
||||||
except:
|
except LookupError:
|
||||||
self['Trading state'] = ''
|
self['Trading state'] = ''
|
||||||
try:
|
self['Price'] = convert_none_to_empty_str(device.price)
|
||||||
self['Price'] = device.price
|
|
||||||
except:
|
|
||||||
self['Price'] = ''
|
|
||||||
if isinstance(device, d.Computer):
|
if isinstance(device, d.Computer):
|
||||||
self['Processor'] = device.processor_model
|
self['Processor'] = convert_none_to_empty_str(device.processor_model)
|
||||||
self['RAM (MB)'] = device.ram_size
|
self['RAM (MB)'] = convert_none_to_empty_str(device.ram_size)
|
||||||
self['Data Storage Size (MB)'] = device.data_storage_size
|
self['Data Storage Size (MB)'] = convert_none_to_empty_str(device.data_storage_size)
|
||||||
if isinstance(device, d.Mobile):
|
|
||||||
self['Display Size'] = device.display_size
|
|
||||||
self['RAM (MB)'] = device.ram_size
|
|
||||||
self['Data Storage Size (MB)'] = device.data_storage_size
|
|
||||||
rate = device.rate
|
rate = device.rate
|
||||||
if rate:
|
if rate:
|
||||||
self['Rate'] = rate.rating
|
self['Rate'] = rate.rating
|
||||||
|
@ -135,3 +127,47 @@ class DeviceRow(OrderedDict):
|
||||||
self['{} {} Speed (MHz)'.format(type, i)] = component.speed
|
self['{} {} Speed (MHz)'.format(type, i)] = component.speed
|
||||||
|
|
||||||
# todo add Display, NetworkAdapter, etc...
|
# todo add Display, NetworkAdapter, etc...
|
||||||
|
|
||||||
|
|
||||||
|
class StockRow(OrderedDict):
|
||||||
|
def __init__(self, device: d.Device) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.device = device
|
||||||
|
self['Type'] = convert_none_to_empty_str(device.t)
|
||||||
|
if isinstance(device, d.Computer):
|
||||||
|
self['Chassis'] = device.chassis
|
||||||
|
else:
|
||||||
|
self['Chassis'] = ''
|
||||||
|
self['Serial Number'] = convert_none_to_empty_str(device.serial_number)
|
||||||
|
self['Model'] = convert_none_to_empty_str(device.model)
|
||||||
|
self['Manufacturer'] = convert_none_to_empty_str(device.manufacturer)
|
||||||
|
self['Registered in'] = format(device.created, '%c')
|
||||||
|
try:
|
||||||
|
self['Physical state'] = device.last_action_of(*states.Physical.actions()).t
|
||||||
|
except LookupError:
|
||||||
|
self['Physical state'] = ''
|
||||||
|
try:
|
||||||
|
self['Trading state'] = device.last_action_of(*states.Trading.actions()).t
|
||||||
|
except LookupError:
|
||||||
|
self['Trading state'] = ''
|
||||||
|
self['Price'] = convert_none_to_empty_str(device.price)
|
||||||
|
self['Processor'] = convert_none_to_empty_str(device.processor_model)
|
||||||
|
self['RAM (MB)'] = convert_none_to_empty_str(device.ram_size)
|
||||||
|
self['Data Storage Size (MB)'] = convert_none_to_empty_str(device.data_storage_size)
|
||||||
|
rate = device.rate
|
||||||
|
if rate:
|
||||||
|
self['Rate'] = rate.rating
|
||||||
|
self['Range'] = rate.rating_range
|
||||||
|
assert isinstance(rate, RateComputer)
|
||||||
|
self['Processor Rate'] = rate.processor
|
||||||
|
self['Processor Range'] = rate.processor_range
|
||||||
|
self['RAM Rate'] = rate.ram
|
||||||
|
self['RAM Range'] = rate.ram_range
|
||||||
|
self['Data Storage Rate'] = rate.data_storage
|
||||||
|
self['Data Storage Range'] = rate.data_storage_range
|
||||||
|
|
||||||
|
|
||||||
|
def convert_none_to_empty_str(s):
|
||||||
|
if s is None:
|
||||||
|
return ''
|
||||||
|
return s
|
||||||
|
|
|
@ -11,7 +11,7 @@ import flask
|
||||||
import flask_weasyprint
|
import flask_weasyprint
|
||||||
import teal.marshmallow
|
import teal.marshmallow
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
from flask import make_response
|
from flask import make_response, g
|
||||||
from teal.cache import cache
|
from teal.cache import cache
|
||||||
from teal.resource import Resource
|
from teal.resource import Resource
|
||||||
|
|
||||||
|
@ -19,11 +19,12 @@ from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.action import models as evs
|
from ereuse_devicehub.resources.action import models as evs
|
||||||
from ereuse_devicehub.resources.device import models as devs
|
from ereuse_devicehub.resources.device import models as devs
|
||||||
from ereuse_devicehub.resources.device.views import DeviceView
|
from ereuse_devicehub.resources.device.views import DeviceView
|
||||||
|
from ereuse_devicehub.resources.documents.device_row import DeviceRow, StockRow
|
||||||
from ereuse_devicehub.resources.documents.device_row import DeviceRow
|
from ereuse_devicehub.resources.documents.device_row import DeviceRow
|
||||||
from ereuse_devicehub.resources.lot import LotView
|
from ereuse_devicehub.resources.lot import LotView
|
||||||
from ereuse_devicehub.resources.lot.models import Lot
|
from ereuse_devicehub.resources.lot.models import Lot
|
||||||
|
|
||||||
from flask import g, request
|
|
||||||
|
|
||||||
class Format(enum.Enum):
|
class Format(enum.Enum):
|
||||||
HTML = 'HTML'
|
HTML = 'HTML'
|
||||||
|
@ -110,7 +111,7 @@ class DocumentView(DeviceView):
|
||||||
class DevicesDocumentView(DeviceView):
|
class DevicesDocumentView(DeviceView):
|
||||||
@cache(datetime.timedelta(minutes=1))
|
@cache(datetime.timedelta(minutes=1))
|
||||||
def find(self, args: dict):
|
def find(self, args: dict):
|
||||||
query = self.query(args)
|
query = (x for x in self.query(args) if x.owner_id == g.user.id)
|
||||||
return self.generate_post_csv(query)
|
return self.generate_post_csv(query)
|
||||||
|
|
||||||
def generate_post_csv(self, query):
|
def generate_post_csv(self, query):
|
||||||
|
@ -169,7 +170,7 @@ class LotRow(OrderedDict):
|
||||||
class StockDocumentView(DeviceView):
|
class StockDocumentView(DeviceView):
|
||||||
# @cache(datetime.timedelta(minutes=1))
|
# @cache(datetime.timedelta(minutes=1))
|
||||||
def find(self, args: dict):
|
def find(self, args: dict):
|
||||||
query = self.query(args)
|
query = (x for x in self.query(args) if x.owner_id == g.user.id)
|
||||||
return self.generate_post_csv(query)
|
return self.generate_post_csv(query)
|
||||||
|
|
||||||
def generate_post_csv(self, query):
|
def generate_post_csv(self, query):
|
||||||
|
@ -178,13 +179,13 @@ class StockDocumentView(DeviceView):
|
||||||
cw = csv.writer(data)
|
cw = csv.writer(data)
|
||||||
first = True
|
first = True
|
||||||
for device in query:
|
for device in query:
|
||||||
d = DeviceRow(device)
|
d = StockRow(device)
|
||||||
if first:
|
if first:
|
||||||
cw.writerow(d.keys())
|
cw.writerow(d.keys())
|
||||||
first = False
|
first = False
|
||||||
cw.writerow(d.values())
|
cw.writerow(d.values())
|
||||||
output = make_response(data.getvalue())
|
output = make_response(data.getvalue())
|
||||||
output.headers['Content-Disposition'] = 'attachment; filename=export.csv'
|
output.headers['Content-Disposition'] = 'attachment; filename=devices-stock.csv'
|
||||||
output.headers['Content-type'] = 'text/csv'
|
output.headers['Content-type'] = 'text/csv'
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
@ -194,6 +195,7 @@ class DocumentDef(Resource):
|
||||||
SCHEMA = None
|
SCHEMA = None
|
||||||
VIEW = None # We do not want to create default / documents endpoint
|
VIEW = None # We do not want to create default / documents endpoint
|
||||||
AUTH = False
|
AUTH = False
|
||||||
|
|
||||||
def __init__(self, app,
|
def __init__(self, app,
|
||||||
import_name=__name__,
|
import_name=__name__,
|
||||||
static_folder='static',
|
static_folder='static',
|
||||||
|
@ -222,8 +224,11 @@ class DocumentDef(Resource):
|
||||||
devices_view = DevicesDocumentView.as_view('devicesDocumentView',
|
devices_view = DevicesDocumentView.as_view('devicesDocumentView',
|
||||||
definition=self,
|
definition=self,
|
||||||
auth=app.auth)
|
auth=app.auth)
|
||||||
|
|
||||||
devices_view = app.auth.requires_auth(devices_view)
|
devices_view = app.auth.requires_auth(devices_view)
|
||||||
|
|
||||||
|
stock_view = StockDocumentView.as_view('stockDocumentView', definition=self)
|
||||||
|
stock_view = app.auth.requires_auth(stock_view)
|
||||||
|
|
||||||
self.add_url_rule('/devices/', defaults=d, view_func=devices_view, methods=get)
|
self.add_url_rule('/devices/', defaults=d, view_func=devices_view, methods=get)
|
||||||
|
|
||||||
lots_view = LotsDocumentView.as_view('lotsDocumentView', definition=self)
|
lots_view = LotsDocumentView.as_view('lotsDocumentView', definition=self)
|
||||||
|
|
|
@ -370,6 +370,7 @@ class ErasureStandards(Enum):
|
||||||
standards.add(cls.HMG_IS5)
|
standards.add(cls.HMG_IS5)
|
||||||
return standards
|
return standards
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class TransferState(IntEnum):
|
class TransferState(IntEnum):
|
||||||
"""State of transfer for a given Lot of devices.
|
"""State of transfer for a given Lot of devices.
|
||||||
|
|
|
@ -5,7 +5,7 @@ from typing import Union
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
from citext import CIText
|
from citext import CIText
|
||||||
from flask import g
|
from flask import g
|
||||||
from sqlalchemy import TEXT, Enum as DBEnum
|
from sqlalchemy import TEXT
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy_utils import LtreeType
|
from sqlalchemy_utils import LtreeType
|
||||||
from sqlalchemy_utils.types.ltree import LQUERY
|
from sqlalchemy_utils.types.ltree import LQUERY
|
||||||
|
@ -14,9 +14,9 @@ from teal.resource import url_for_resource
|
||||||
|
|
||||||
from ereuse_devicehub.db import create_view, db, exp, f
|
from ereuse_devicehub.db import create_view, db, exp, f
|
||||||
from ereuse_devicehub.resources.device.models import Component, Device
|
from ereuse_devicehub.resources.device.models import Component, Device
|
||||||
|
from ereuse_devicehub.resources.enums import TransferState
|
||||||
from ereuse_devicehub.resources.models import Thing
|
from ereuse_devicehub.resources.models import Thing
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
from ereuse_devicehub.resources.enums import TransferState
|
|
||||||
|
|
||||||
|
|
||||||
class Lot(Thing):
|
class Lot(Thing):
|
||||||
|
@ -65,15 +65,15 @@ class Lot(Thing):
|
||||||
"""
|
"""
|
||||||
deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0)
|
deposit = db.Column(db.Integer, check_range('deposit', min=0, max=100), default=0)
|
||||||
owner_id = db.Column(UUID(as_uuid=True),
|
owner_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.id)
|
default=lambda: g.user.id)
|
||||||
owner = db.relationship(User, primaryjoin=owner_id == User.id)
|
owner = db.relationship(User, primaryjoin=owner_id == User.id)
|
||||||
transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False)
|
transfer_state = db.Column(IntEnum(TransferState), default=TransferState.Initial, nullable=False)
|
||||||
transfer_state.comment = TransferState.__doc__
|
transfer_state.comment = TransferState.__doc__
|
||||||
receiver_address = db.Column(CIText(),
|
receiver_address = db.Column(CIText(),
|
||||||
db.ForeignKey(User.ethereum_address),
|
db.ForeignKey(User.ethereum_address),
|
||||||
nullable=True)
|
nullable=True)
|
||||||
receiver = db.relationship(User, primaryjoin=receiver_address == User.ethereum_address)
|
receiver = db.relationship(User, primaryjoin=receiver_address == User.ethereum_address)
|
||||||
deliverynote_address = db.Column(CIText(), nullable=True)
|
deliverynote_address = db.Column(CIText(), nullable=True)
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,13 @@ class Lot(Thing):
|
||||||
description = ... # type: Column
|
description = ... # type: Column
|
||||||
all_devices = ... # type: relationship
|
all_devices = ... # type: relationship
|
||||||
parents = ... # type: relationship
|
parents = ... # type: relationship
|
||||||
deposit = ... # type: Column
|
deposit = ... # type: Column
|
||||||
owner_address = ... # type: Column
|
owner_address = ... # type: Column
|
||||||
owner = ... # type: relationship
|
owner = ... # type: relationship
|
||||||
transfer_state = ... # type: Column
|
transfer_state = ... # type: Column
|
||||||
receiver_address = ... # type: Column
|
receiver_address = ... # type: Column
|
||||||
receiver = ... # type: relationship
|
receiver = ... # type: relationship
|
||||||
deliverynote_address = ... # type: Column
|
deliverynote_address = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, name: str, closed: bool = closed.default.arg) -> None:
|
def __init__(self, name: str, closed: bool = closed.default.arg) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -43,10 +43,10 @@ class Lot(Thing):
|
||||||
self.all_devices = ... # type: Set[Device]
|
self.all_devices = ... # type: Set[Device]
|
||||||
self.parents = ... # type: Set[Lot]
|
self.parents = ... # type: Set[Lot]
|
||||||
self.children = ... # type: Set[Lot]
|
self.children = ... # type: Set[Lot]
|
||||||
self.owner_address = ... # type: UUID
|
self.owner_address = ... # type: UUID
|
||||||
self.transfer_state = ...
|
self.transfer_state = ...
|
||||||
self.receiver_address = ... # type: str
|
self.receiver_address = ... # type: str
|
||||||
self.deliverynote_address = ... # type: str
|
self.deliverynote_address = ... # type: str
|
||||||
|
|
||||||
def add_children(self, *children: Union[Lot, uuid.UUID]):
|
def add_children(self, *children: Union[Lot, uuid.UUID]):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,12 +2,12 @@ from marshmallow import fields as f
|
||||||
from teal.marshmallow import SanitizedStr, URL, EnumField
|
from teal.marshmallow import SanitizedStr, URL, EnumField
|
||||||
|
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
from ereuse_devicehub.resources.device import schemas as s_device
|
|
||||||
from ereuse_devicehub.resources.lot import models as m
|
|
||||||
from ereuse_devicehub.resources.deliverynote import schemas as s_deliverynote
|
from ereuse_devicehub.resources.deliverynote import schemas as s_deliverynote
|
||||||
|
from ereuse_devicehub.resources.device import schemas as s_device
|
||||||
|
from ereuse_devicehub.resources.enums import TransferState
|
||||||
|
from ereuse_devicehub.resources.lot import models as m
|
||||||
from ereuse_devicehub.resources.models import STR_SIZE
|
from ereuse_devicehub.resources.models import STR_SIZE
|
||||||
from ereuse_devicehub.resources.schemas import Thing
|
from ereuse_devicehub.resources.schemas import Thing
|
||||||
from ereuse_devicehub.resources.enums import TransferState
|
|
||||||
|
|
||||||
|
|
||||||
class Lot(Thing):
|
class Lot(Thing):
|
||||||
|
@ -20,7 +20,7 @@ class Lot(Thing):
|
||||||
parents = NestedOn('Lot', many=True, dump_only=True)
|
parents = NestedOn('Lot', many=True, dump_only=True)
|
||||||
url = URL(dump_only=True, description=m.Lot.url.__doc__)
|
url = URL(dump_only=True, description=m.Lot.url.__doc__)
|
||||||
deposit = f.Integer(validate=f.validate.Range(min=0, max=100),
|
deposit = f.Integer(validate=f.validate.Range(min=0, max=100),
|
||||||
description=m.Lot.deposit.__doc__)
|
description=m.Lot.deposit.__doc__)
|
||||||
# author_id = NestedOn(s_user.User,only_query='author_id')
|
# author_id = NestedOn(s_user.User,only_query='author_id')
|
||||||
owner_id = f.UUID(data_key='ownerID')
|
owner_id = f.UUID(data_key='ownerID')
|
||||||
transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment)
|
transfer_state = EnumField(TransferState, description=m.Lot.transfer_state.comment)
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
import datetime
|
|
||||||
import uuid
|
import uuid
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, List, Set, Union
|
from typing import Dict, List, Set, Union
|
||||||
|
|
||||||
import marshmallow as ma
|
import marshmallow as ma
|
||||||
import teal.cache
|
|
||||||
from flask import Response, jsonify, request, g
|
from flask import Response, jsonify, request, g
|
||||||
from marshmallow import Schema as MarshmallowSchema, fields as f
|
from marshmallow import Schema as MarshmallowSchema, fields as f
|
||||||
|
from sqlalchemy import or_
|
||||||
from teal.marshmallow import EnumField
|
from teal.marshmallow import EnumField
|
||||||
from teal.resource import View
|
from teal.resource import View
|
||||||
from sqlalchemy import or_
|
|
||||||
from sqlalchemy.orm import joinedload
|
|
||||||
|
|
||||||
from ereuse_devicehub import auth
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.query import things_response
|
from ereuse_devicehub.query import things_response
|
||||||
|
from ereuse_devicehub.resources.deliverynote.models import Deliverynote
|
||||||
from ereuse_devicehub.resources.device.models import Device, Computer
|
from ereuse_devicehub.resources.device.models import Device, Computer
|
||||||
from ereuse_devicehub.resources.lot.models import Lot, Path
|
from ereuse_devicehub.resources.lot.models import Lot, Path
|
||||||
from ereuse_devicehub.resources.deliverynote.models import Deliverynote
|
|
||||||
|
|
||||||
|
|
||||||
class LotFormat(Enum):
|
class LotFormat(Enum):
|
||||||
|
@ -44,7 +40,9 @@ class LotView(View):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def patch(self, id):
|
def patch(self, id):
|
||||||
patch_schema = self.resource_def.SCHEMA(only=('name', 'description', 'transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'devices', 'owner_address'), partial=True)
|
patch_schema = self.resource_def.SCHEMA(only=(
|
||||||
|
'name', 'description', 'transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'devices',
|
||||||
|
'owner_address'), partial=True)
|
||||||
l = request.get_json(schema=patch_schema)
|
l = request.get_json(schema=patch_schema)
|
||||||
lot = Lot.query.filter_by(id=id).one()
|
lot = Lot.query.filter_by(id=id).one()
|
||||||
device_fields = ['transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'owner_address']
|
device_fields = ['transfer_state', 'receiver_address', 'deposit', 'deliverynote_address', 'owner_address']
|
||||||
|
|
|
@ -2,30 +2,22 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from collections import Iterable
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional, Set, Union
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
from citext import CIText
|
from citext import CIText
|
||||||
from flask import current_app as app, g
|
from flask import g
|
||||||
from sortedcontainers import SortedSet
|
from sqlalchemy import BigInteger, Column, ForeignKey, Unicode
|
||||||
from sqlalchemy import BigInteger, Column, Enum as DBEnum, \
|
|
||||||
ForeignKey, Integer, Unicode
|
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy.ext.declarative import declared_attr
|
from sqlalchemy.ext.declarative import declared_attr
|
||||||
from sqlalchemy.ext.orderinglist import ordering_list
|
from sqlalchemy.orm import backref, relationship
|
||||||
from sqlalchemy.orm import backref, relationship, validates
|
|
||||||
from sqlalchemy.util import OrderedSet
|
|
||||||
from teal.db import CASCADE_OWN, INHERIT_COND, POLYMORPHIC_ID, \
|
from teal.db import CASCADE_OWN, INHERIT_COND, POLYMORPHIC_ID, \
|
||||||
POLYMORPHIC_ON, StrictVersionType, URL
|
POLYMORPHIC_ON
|
||||||
from teal.marshmallow import ValidationError
|
|
||||||
from teal.resource import url_for_resource
|
from teal.resource import url_for_resource
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.action.models import Action, DisposeProduct, \
|
from ereuse_devicehub.resources.action.models import EraseBasic, Rate
|
||||||
EraseBasic, Rate, Trade
|
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
from ereuse_devicehub.resources.models import Thing
|
from ereuse_devicehub.resources.models import Thing
|
||||||
from ereuse_devicehub.resources.user import User
|
from ereuse_devicehub.resources.user import User
|
||||||
|
@ -83,16 +75,15 @@ class Proof(Thing):
|
||||||
return '<{0.t} {0.id} >'.format(self)
|
return '<{0.t} {0.id} >'.format(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ProofTransfer(JoinedTableMixin, Proof):
|
class ProofTransfer(JoinedTableMixin, Proof):
|
||||||
supplier_id = db.Column(UUID(as_uuid=True),
|
supplier_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.id)
|
default=lambda: g.user.id)
|
||||||
supplier = db.relationship(User, primaryjoin=lambda: ProofTransfer.supplier_id == User.id)
|
supplier = db.relationship(User, primaryjoin=lambda: ProofTransfer.supplier_id == User.id)
|
||||||
receiver_id = db.Column(UUID(as_uuid=True),
|
receiver_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
receiver = db.relationship(User, primaryjoin=lambda: ProofTransfer.receiver_id == User.id)
|
receiver = db.relationship(User, primaryjoin=lambda: ProofTransfer.receiver_id == User.id)
|
||||||
deposit = Column(db.Integer, default=0)
|
deposit = Column(db.Integer, default=0)
|
||||||
|
|
||||||
|
@ -103,9 +94,9 @@ class ProofDataWipe(JoinedTableMixin, Proof):
|
||||||
result = Column(db.Boolean, default=False, nullable=False)
|
result = Column(db.Boolean, default=False, nullable=False)
|
||||||
result.comment = """Identifies proof datawipe as a result."""
|
result.comment = """Identifies proof datawipe as a result."""
|
||||||
proof_author_id = Column(UUID(as_uuid=True),
|
proof_author_id = Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.id)
|
default=lambda: g.user.id)
|
||||||
proof_author = relationship(User, primaryjoin=lambda: ProofDataWipe.proof_author_id == User.id)
|
proof_author = relationship(User, primaryjoin=lambda: ProofDataWipe.proof_author_id == User.id)
|
||||||
erasure_id = Column(UUID(as_uuid=True), ForeignKey(EraseBasic.id), nullable=False)
|
erasure_id = Column(UUID(as_uuid=True), ForeignKey(EraseBasic.id), nullable=False)
|
||||||
erasure = relationship(EraseBasic,
|
erasure = relationship(EraseBasic,
|
||||||
|
@ -119,16 +110,16 @@ class ProofDataWipe(JoinedTableMixin, Proof):
|
||||||
class ProofFunction(JoinedTableMixin, Proof):
|
class ProofFunction(JoinedTableMixin, Proof):
|
||||||
disk_usage = Column(db.Integer, default=0)
|
disk_usage = Column(db.Integer, default=0)
|
||||||
proof_author_id = Column(UUID(as_uuid=True),
|
proof_author_id = Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
default=lambda: g.user.id)
|
default=lambda: g.user.id)
|
||||||
proof_author = db.relationship(User, primaryjoin=lambda: ProofFunction.proof_author_id == User.id)
|
proof_author = db.relationship(User, primaryjoin=lambda: ProofFunction.proof_author_id == User.id)
|
||||||
rate_id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), nullable=False)
|
rate_id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), nullable=False)
|
||||||
rate = relationship(Rate,
|
rate = relationship(Rate,
|
||||||
backref=backref('proof_function',
|
backref=backref('proof_function',
|
||||||
lazy=True,
|
lazy=True,
|
||||||
uselist=False,
|
uselist=False,
|
||||||
cascade=CASCADE_OWN),
|
cascade=CASCADE_OWN),
|
||||||
primaryjoin=Rate.id == rate_id)
|
primaryjoin=Rate.id == rate_id)
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,15 +127,15 @@ class ProofReuse(JoinedTableMixin, Proof):
|
||||||
receiver_segment = Column(CIText(), default='', nullable=False)
|
receiver_segment = Column(CIText(), default='', nullable=False)
|
||||||
id_receipt = Column(CIText(), default='', nullable=False)
|
id_receipt = Column(CIText(), default='', nullable=False)
|
||||||
supplier_id = db.Column(UUID(as_uuid=True),
|
supplier_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
# nullable=False,
|
# nullable=False,
|
||||||
# default=lambda: g.user.id)
|
# default=lambda: g.user.id)
|
||||||
nullable=True)
|
nullable=True)
|
||||||
supplier = db.relationship(User, primaryjoin=lambda: ProofReuse.supplier_id == User.id)
|
supplier = db.relationship(User, primaryjoin=lambda: ProofReuse.supplier_id == User.id)
|
||||||
receiver_id = db.Column(UUID(as_uuid=True),
|
receiver_id = db.Column(UUID(as_uuid=True),
|
||||||
db.ForeignKey(User.id),
|
db.ForeignKey(User.id),
|
||||||
# nullable=False)
|
# nullable=False)
|
||||||
nullable=True)
|
nullable=True)
|
||||||
receiver = db.relationship(User, primaryjoin=lambda: ProofReuse.receiver_id == User.id)
|
receiver = db.relationship(User, primaryjoin=lambda: ProofReuse.receiver_id == User.id)
|
||||||
price = Column(db.Integer)
|
price = Column(db.Integer)
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
from flask import current_app as app
|
from marshmallow import fields as f
|
||||||
from marshmallow import Schema as MarshmallowSchema, ValidationError, fields as f, validates_schema
|
from marshmallow import fields as f
|
||||||
from marshmallow.fields import Boolean, DateTime, Integer, Nested, String, UUID
|
from marshmallow.fields import Boolean, DateTime, Integer, String, UUID
|
||||||
from marshmallow.validate import Length
|
from marshmallow.validate import Length
|
||||||
from sqlalchemy.util import OrderedSet
|
|
||||||
from teal.marshmallow import SanitizedStr, URL
|
from teal.marshmallow import SanitizedStr, URL
|
||||||
from teal.resource import Schema
|
|
||||||
|
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
from ereuse_devicehub.resources.proof import models as m
|
|
||||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
|
||||||
from ereuse_devicehub.resources.schemas import Thing
|
|
||||||
from ereuse_devicehub.resources.action import schemas as s_action
|
from ereuse_devicehub.resources.action import schemas as s_action
|
||||||
from ereuse_devicehub.resources.device import schemas as s_device
|
from ereuse_devicehub.resources.device import schemas as s_device
|
||||||
|
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
||||||
|
from ereuse_devicehub.resources.proof import models as m
|
||||||
|
from ereuse_devicehub.resources.schemas import Thing
|
||||||
from ereuse_devicehub.resources.user import schemas as s_user
|
from ereuse_devicehub.resources.user import schemas as s_user
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,7 +17,7 @@ class Proof(Thing):
|
||||||
__doc__ = m.Proof.__doc__
|
__doc__ = m.Proof.__doc__
|
||||||
id = UUID(dump_only=True)
|
id = UUID(dump_only=True)
|
||||||
ethereum_hash = SanitizedStr(default='', validate=Length(max=STR_BIG_SIZE),
|
ethereum_hash = SanitizedStr(default='', validate=Length(max=STR_BIG_SIZE),
|
||||||
data_key="ethereumHash", required=True)
|
data_key="ethereumHash", required=True)
|
||||||
url = URL(dump_only=True, description=m.Proof.url.__doc__)
|
url = URL(dump_only=True, description=m.Proof.url.__doc__)
|
||||||
device_id = Integer(load_only=True, data_key='deviceID')
|
device_id = Integer(load_only=True, data_key='deviceID')
|
||||||
device = NestedOn(s_device.Device, dump_only=True)
|
device = NestedOn(s_device.Device, dump_only=True)
|
||||||
|
|
|
@ -1,18 +1,10 @@
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from typing import List
|
|
||||||
from uuid import UUID
|
|
||||||
|
|
||||||
from flask import current_app as app, request, jsonify
|
from flask import current_app as app, request, jsonify
|
||||||
from sqlalchemy.util import OrderedSet
|
|
||||||
from teal.marshmallow import ValidationError
|
from teal.marshmallow import ValidationError
|
||||||
from teal.resource import View
|
from teal.resource import View
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.query import things_response
|
|
||||||
from ereuse_devicehub.resources.action.models import Action, RateComputer, Snapshot, VisualTest
|
|
||||||
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
|
||||||
from ereuse_devicehub.resources.device.models import Component, Computer
|
|
||||||
from ereuse_devicehub.resources.enums import SnapshotSoftware
|
|
||||||
|
|
||||||
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
SUPPORTED_WORKBENCH = StrictVersion('11.0')
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
from flask import g
|
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
|
from flask import g
|
||||||
from sqlalchemy import BigInteger, Column, ForeignKey, UniqueConstraint
|
from sqlalchemy import BigInteger, Column, ForeignKey, UniqueConstraint
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy.orm import backref, relationship, validates
|
from sqlalchemy.orm import backref, relationship, validates
|
||||||
|
@ -13,8 +13,8 @@ from teal.resource import url_for_resource
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.agent.models import Organization
|
from ereuse_devicehub.resources.agent.models import Organization
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
from ereuse_devicehub.resources.user.models import User
|
|
||||||
from ereuse_devicehub.resources.models import Thing
|
from ereuse_devicehub.resources.models import Thing
|
||||||
|
from ereuse_devicehub.resources.user.models import User
|
||||||
|
|
||||||
|
|
||||||
class Tags(Set['Tag']):
|
class Tags(Set['Tag']):
|
||||||
|
|
|
@ -3,11 +3,11 @@ from sqlalchemy.util import OrderedSet
|
||||||
from teal.marshmallow import SanitizedStr, URL
|
from teal.marshmallow import SanitizedStr, URL
|
||||||
|
|
||||||
from ereuse_devicehub.marshmallow import NestedOn
|
from ereuse_devicehub.marshmallow import NestedOn
|
||||||
from ereuse_devicehub.resources.user.schemas import User
|
|
||||||
from ereuse_devicehub.resources.agent.schemas import Organization
|
from ereuse_devicehub.resources.agent.schemas import Organization
|
||||||
from ereuse_devicehub.resources.device.schemas import Device
|
from ereuse_devicehub.resources.device.schemas import Device
|
||||||
from ereuse_devicehub.resources.schemas import Thing
|
from ereuse_devicehub.resources.schemas import Thing
|
||||||
from ereuse_devicehub.resources.tag import model as m
|
from ereuse_devicehub.resources.tag import model as m
|
||||||
|
from ereuse_devicehub.resources.user.schemas import User
|
||||||
|
|
||||||
|
|
||||||
def without_slash(x: str) -> bool:
|
def without_slash(x: str) -> bool:
|
||||||
|
|
|
@ -3,8 +3,8 @@ from flask_sqlalchemy import Pagination
|
||||||
from teal.marshmallow import ValidationError
|
from teal.marshmallow import ValidationError
|
||||||
from teal.resource import View, url_for_resource
|
from teal.resource import View, url_for_resource
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
|
||||||
from ereuse_devicehub import auth
|
from ereuse_devicehub import auth
|
||||||
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.query import things_response
|
from ereuse_devicehub.query import things_response
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
from ereuse_devicehub.resources.tag import Tag
|
from ereuse_devicehub.resources.tag import Tag
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
from werkzeug.exceptions import Unauthorized
|
from werkzeug.exceptions import Unauthorized, Forbidden
|
||||||
|
|
||||||
|
|
||||||
class WrongCredentials(Unauthorized):
|
class WrongCredentials(Unauthorized):
|
||||||
description = 'There is not an user with the matching username/password'
|
description = 'There is not an user with the matching username/password'
|
||||||
|
|
||||||
|
|
||||||
|
class InsufficientPermission(Forbidden):
|
||||||
|
description = (
|
||||||
|
"You don't have the permissions to access the requested"
|
||||||
|
"resource. It is either read-protected or not readable by the"
|
||||||
|
"server."
|
||||||
|
)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from citext import CIText
|
||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
from sqlalchemy import Column
|
from sqlalchemy import Column
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy_utils import EmailType, PasswordType
|
from sqlalchemy_utils import EmailType, PasswordType
|
||||||
from citext import CIText
|
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.inventory.model import Inventory
|
from ereuse_devicehub.resources.inventory.model import Inventory
|
||||||
|
|
|
@ -17,7 +17,7 @@ class User(Thing):
|
||||||
password = ... # type: Column
|
password = ... # type: Column
|
||||||
token = ... # type: Column
|
token = ... # type: Column
|
||||||
inventories = ... # type: relationship
|
inventories = ... # type: relationship
|
||||||
ethereum_address = ... # type: Column
|
ethereum_address = ... # type: Column
|
||||||
|
|
||||||
def __init__(self, email: str, password: str = None,
|
def __init__(self, email: str, password: str = None,
|
||||||
inventories: Set[Inventory] = None) -> None:
|
inventories: Set[Inventory] = None) -> None:
|
||||||
|
@ -28,7 +28,7 @@ class User(Thing):
|
||||||
self.individuals = ... # type: Set[Individual]
|
self.individuals = ... # type: Set[Individual]
|
||||||
self.token = ... # type: UUID
|
self.token = ... # type: UUID
|
||||||
self.inventories = ... # type: Set[Inventory]
|
self.inventories = ... # type: Set[Inventory]
|
||||||
self.ethereum_address = ... # type: str
|
self.ethereum_address = ... # type: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def individual(self) -> Union[Individual, None]:
|
def individual(self) -> Union[Individual, None]:
|
||||||
|
|
|
@ -93,6 +93,18 @@ def user(app: Devicehub) -> UserClient:
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def user2(app: Devicehub) -> UserClient:
|
||||||
|
"""Gets a client with a logged-in dummy user."""
|
||||||
|
with app.app_context():
|
||||||
|
password = 'foo'
|
||||||
|
email = 'foo2@foo.com'
|
||||||
|
user = create_user(email=email, password=password)
|
||||||
|
client = UserClient(app, user.email, password, response_wrapper=app.response_class)
|
||||||
|
client.login()
|
||||||
|
return client
|
||||||
|
|
||||||
|
|
||||||
def create_user(email='foo@foo.com', password='foo') -> User:
|
def create_user(email='foo@foo.com', password='foo') -> User:
|
||||||
user = User(email=email, password=password)
|
user = User(email=email, password=password)
|
||||||
user.individuals.add(Person(name='Timmy'))
|
user.individuals.add(Person(name='Timmy'))
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Type,Chassis,Serial Number,Model,Manufacturer,Registered in,Physical state,Trading state,Price,Processor,RAM (MB),Data Storage Size (MB),Rate,Range,Processor Rate,Processor Range,RAM Rate,RAM Range,Data Storage Rate,Data Storage Range
|
||||||
|
Desktop,Microtower,d1s,d1ml,d1mr,Tue Jul 2 10:35:10 2019,,,,p1ml,0,0,1.0,Very low,1.0,Very low,1.0,Very low,1.0,Very low
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
type: Snapshot
|
||||||
|
uuid: 123e4567-e89b-12d3-a456-426655440000
|
||||||
|
version: '11.0'
|
||||||
|
software: Workbench
|
||||||
|
elapsed: 4
|
||||||
|
device:
|
||||||
|
type: Desktop
|
||||||
|
chassis: Microtower
|
||||||
|
serialNumber: d2s
|
||||||
|
model: d1ml
|
||||||
|
manufacturer: d1mr
|
||||||
|
actions:
|
||||||
|
- type: VisualTest
|
||||||
|
appearanceRange: A
|
||||||
|
functionalityRange: B
|
||||||
|
components:
|
||||||
|
- type: GraphicCard
|
||||||
|
serialNumber: gc1s
|
||||||
|
model: gc1ml
|
||||||
|
manufacturer: gc1mr
|
||||||
|
- type: RamModule
|
||||||
|
serialNumber: rm1s
|
||||||
|
model: rm1ml
|
||||||
|
manufacturer: rm1mr
|
||||||
|
speed: 1333
|
||||||
|
- type: Processor
|
||||||
|
serialNumber: p1s
|
||||||
|
model: p1mla
|
||||||
|
manufacturer: p1mr
|
||||||
|
speed: 1.6
|
||||||
|
actions:
|
||||||
|
- type: BenchmarkProcessor
|
||||||
|
rate: 2410
|
||||||
|
elapsed: 11
|
|
@ -1,11 +1,12 @@
|
||||||
import pytest
|
|
||||||
import teal.marshmallow
|
|
||||||
from ereuse_utils.test import ANY
|
|
||||||
import csv
|
import csv
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import teal.marshmallow
|
||||||
|
from ereuse_utils.test import ANY
|
||||||
|
|
||||||
from ereuse_devicehub.client import Client, UserClient
|
from ereuse_devicehub.client import Client, 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
|
||||||
|
@ -227,6 +228,40 @@ def test_export_multiple_different_devices(user: UserClient):
|
||||||
assert fixture_csv == export_csv
|
assert fixture_csv == export_csv
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
def test_report_devices_stock_control(user: UserClient, user2: UserClient):
|
||||||
|
"""Test export device information in a csv file."""
|
||||||
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
|
snapshot2, _ = user2.post(file('basic.snapshot2'), res=Snapshot)
|
||||||
|
|
||||||
|
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
|
item='stock/',
|
||||||
|
accept='text/csv',
|
||||||
|
query=[('filter', {'type': ['Computer']})])
|
||||||
|
f = StringIO(csv_str)
|
||||||
|
obj_csv = csv.reader(f, f)
|
||||||
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
|
# Open fixture csv and transform to list
|
||||||
|
with Path(__file__).parent.joinpath('files').joinpath('basic-stock.csv').open() as csv_file:
|
||||||
|
obj_csv = csv.reader(csv_file)
|
||||||
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
assert user.user['id'] != user2.user['id']
|
||||||
|
assert len(export_csv) == 2
|
||||||
|
|
||||||
|
assert isinstance(datetime.strptime(export_csv[1][5], '%c'), datetime), \
|
||||||
|
'Register in field is not a datetime'
|
||||||
|
|
||||||
|
# Pop dates fields from csv lists to compare them
|
||||||
|
fixture_csv[1] = fixture_csv[1][:5] + fixture_csv[1][6:]
|
||||||
|
export_csv[1] = export_csv[1][:5] + export_csv[1][6:]
|
||||||
|
|
||||||
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
|
assert fixture_csv[1] == export_csv[1], 'Computer information are not equal'
|
||||||
|
assert fixture_csv == export_csv
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
def test_get_document_lots(user: UserClient):
|
def test_get_document_lots(user: UserClient):
|
||||||
"""Tests submitting and retreiving all lots."""
|
"""Tests submitting and retreiving all lots."""
|
||||||
|
|
Reference in New Issue