Merge pull request #48 from eReuse/feature/46-device-stock-control

Feature/46-device-stock-control
This commit is contained in:
cayop 2020-08-17 18:11:48 +02:00 committed by GitHub
commit 24578e7188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 28 deletions

View File

@ -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

View File

@ -10,7 +10,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
@ -18,7 +18,8 @@ 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 from ereuse_devicehub.resources.documents.device_row import DeviceRow, StockRow
class Format(enum.Enum): class Format(enum.Enum):
@ -106,7 +107,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):
@ -129,7 +130,7 @@ class DevicesDocumentView(DeviceView):
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):
@ -138,13 +139,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
@ -183,8 +184,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)
stock_view = StockDocumentView.as_view('stockDocumentView', definition=self, auth=app.auth) stock_view = StockDocumentView.as_view('stockDocumentView', definition=self, auth=app.auth)

View File

@ -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'))

View File

@ -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
1 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
2 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

View File

@ -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

View File

@ -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
@ -223,4 +224,37 @@ def test_export_multiple_different_devices(user: UserClient):
for row in export_csv: for row in export_csv:
del row[8] del row[8]
assert fixture_csv[:3] == 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'