This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
devicehub-teal/ereuse_devicehub/resources/device/views.py

352 lines
14 KiB
Python
Raw Normal View History

2018-10-18 16:16:49 +00:00
import csv
2018-10-03 12:51:22 +00:00
import datetime
2018-10-18 16:16:49 +00:00
from io import StringIO
2018-10-11 15:51:58 +00:00
from collections import OrderedDict
2018-10-03 12:51:22 +00:00
2018-09-30 17:40:28 +00:00
import marshmallow
2018-10-18 16:16:49 +00:00
from flask import current_app as app, render_template, request, make_response
2018-09-30 17:40:28 +00:00
from flask.json import jsonify
from flask_sqlalchemy import Pagination
2018-10-06 10:45:56 +00:00
from marshmallow import fields, fields as f, validate as v
from sqlalchemy.orm import aliased
from teal import query
2018-10-03 12:51:22 +00:00
from teal.cache import cache
2018-04-10 15:06:39 +00:00
from teal.resource import View
2018-10-03 12:51:22 +00:00
from ereuse_devicehub import auth
from ereuse_devicehub.db import db
from ereuse_devicehub.resources import search
2018-10-18 16:16:49 +00:00
# from ereuse_devicehub.resources.device.definitions import ComponentDef
2018-10-11 15:51:58 +00:00
from ereuse_devicehub.resources.device.models import Component, Computer, Device, Manufacturer, \
2018-10-24 19:11:32 +00:00
RamModule, Processor, DataStorage, GraphicCard, Motherboard, Display, NetworkAdapter, SoundCard
from ereuse_devicehub.resources.device.search import DeviceSearch
2018-10-11 15:51:58 +00:00
from ereuse_devicehub.resources.event.models import Rate, Event
2018-10-06 10:45:56 +00:00
from ereuse_devicehub.resources.lot.models import Lot, LotDevice
from ereuse_devicehub.resources.tag.model import Tag
class OfType(f.Str):
def __init__(self, column: db.Column, *args, **kwargs):
super().__init__(*args, **kwargs)
self.column = column
def _deserialize(self, value, attr, data):
v = super()._deserialize(value, attr, data)
return self.column.in_(app.resources[v].subresources_types)
class RateQ(query.Query):
rating = query.Between(Rate.rating, f.Float())
appearance = query.Between(Rate.appearance, f.Float())
functionality = query.Between(Rate.functionality, f.Float())
class TagQ(query.Query):
id = query.Or(query.ILike(Tag.id), required=True)
org = query.ILike(Tag.org)
2018-10-06 10:45:56 +00:00
class LotQ(query.Query):
id = query.Or(query.QueryField(Lot.descendantsq, fields.UUID()))
class Filters(query.Query):
2018-10-06 10:45:56 +00:00
_parent = aliased(Computer)
_device_inside_lot = (Device.id == LotDevice.device_id) & (Lot.id == LotDevice.lot_id)
_component_inside_lot_through_parent = (Device.id == Component.id) \
& (Component.parent_id == _parent.id) \
& (_parent.id == LotDevice.device_id) \
& (Lot.id == LotDevice.lot_id)
2018-10-06 10:45:56 +00:00
type = query.Or(OfType(Device.type))
model = query.ILike(Device.model)
manufacturer = query.ILike(Device.manufacturer)
serialNumber = query.ILike(Device.serial_number)
rating = query.Join(Device.id == Rate.device_id, RateQ)
2018-10-06 10:45:56 +00:00
tag = query.Join(Device.id == Tag.device_id, TagQ)
lot = query.Join(_device_inside_lot | _component_inside_lot_through_parent, LotQ)
class Sorting(query.Sort):
2018-10-06 10:45:56 +00:00
id = query.SortField(Device.id)
created = query.SortField(Device.created)
2018-09-07 10:38:02 +00:00
2018-04-10 15:06:39 +00:00
class DeviceView(View):
class FindArgs(marshmallow.Schema):
search = f.Str()
filter = f.Nested(Filters, missing=[])
sort = f.Nested(Sorting, missing=[])
page = f.Integer(validate=v.Range(min=1), missing=1)
def get(self, id):
"""
Devices view
---
description: Gets a device or multiple devices.
parameters:
- name: id
type: integer
in: path
description: The identifier of the device.
responses:
200:
description: The device or devices.
"""
return super().get(id)
2018-04-27 17:16:43 +00:00
def one(self, id: int):
2018-04-10 15:06:39 +00:00
"""Gets one device."""
2018-10-03 12:51:22 +00:00
if not request.authorization:
return self.one_public(id)
else:
return self.one_private(id)
def one_public(self, id: int):
device = Device.query.filter_by(id=id).one()
return render_template('devices/layout.html', device=device)
@auth.Auth.requires_auth
def one_private(self, id: int):
device = Device.query.filter_by(id=id).one()
return self.schema.jsonify(device)
2018-10-03 12:51:22 +00:00
@auth.Auth.requires_auth
def find(self, args: dict):
"""Gets many devices."""
search_p = args.get('search', None)
query = Device.query
if search_p:
properties = DeviceSearch.properties
tags = DeviceSearch.tags
query = query.join(DeviceSearch).filter(
search.Search.match(properties, search_p) | search.Search.match(tags, search_p)
).order_by(
search.Search.rank(properties, search_p) + search.Search.rank(tags, search_p)
)
query = query.filter(*args['filter']).order_by(*args['sort'])
2018-10-18 16:16:49 +00:00
if 'text/csv' in request.accept_mimetypes:
return self.generate_post_csv(query)
2018-10-11 15:51:58 +00:00
else:
devices = query.paginate(page=args['page'], per_page=30) # type: Pagination
ret = {
'items': self.schema.dump(devices.items, many=True, nested=1),
# todo pagination should be in Header like github
# https://developer.github.com/v3/guides/traversing-with-pagination/
'pagination': {
'page': devices.page,
'perPage': devices.per_page,
'total': devices.total,
'previous': devices.prev_num,
'next': devices.next_num
},
'url': request.path
}
return jsonify(ret)
2018-10-18 16:16:49 +00:00
def generate_post_csv(self, query):
"""
Get device query and put information in csv format
:param query:
:return:
"""
2018-10-23 16:59:58 +00:00
data = StringIO()
cw = csv.writer(data)
first = True
2018-10-24 19:11:32 +00:00
# todo fix if only export components
2018-10-11 15:51:58 +00:00
for device in query:
2018-10-24 19:11:32 +00:00
# if not isinstance(device, Component):
2018-10-11 15:51:58 +00:00
d = DeviceRow(device)
2018-10-23 16:59:58 +00:00
if first:
cw.writerow(name for name in d.keys())
2018-10-24 19:11:32 +00:00
cw.writerow(v for v in d.values())
2018-10-23 16:59:58 +00:00
first = False
2018-10-24 19:11:32 +00:00
elif isinstance(device, Computer):
cw.writerow(v for v in d.values())
2018-10-23 16:59:58 +00:00
output = make_response(data.getvalue())
output.headers['Content-Disposition'] = 'attachment; filename=export.csv'
output.headers['Content-type'] = 'text/csv'
return output
2018-10-11 15:51:58 +00:00
2018-10-24 19:11:32 +00:00
""" Export Erased Certificate Code
def generate_erased_certificate(self, query):
2018-10-18 16:16:49 +00:00
data = StringIO()
cw = csv.writer(data)
2018-10-23 16:59:58 +00:00
first = True
for device in query:
d = DeviceRow(device)
if first:
cw.writerow(name for name in d.keys())
first = False
cw.writerow(v for v in d.values())
# cw = csv.DictWriter(d, fieldnames=keys)
2018-10-18 16:16:49 +00:00
output = make_response(data.getvalue())
2018-10-23 16:59:58 +00:00
output.headers['Content-Disposition'] = 'attachment; filename=export.csv'
output.headers['Content-type'] = 'text/csv'
2018-10-18 16:16:49 +00:00
return output
2018-10-11 15:51:58 +00:00
2018-10-23 16:59:58 +00:00
class EraseDataStorage(OrderedDict):
def __init__(self, device: Device) -> None:
super().__init__()
self.device = device
# General Information
self['Organization'] = device.org
self['Date report'] = datetime.time()
self['Erase Information'] = device.org + 'ha borrado los siguientes discos acorde a ..' + eraseType
# Devices information for row {TABLE}
self['Computer Serial Number'] = device.serial_number
self['Computer Tag'] = device.tags
self['DataStorage Serial Number'] = device.components.data_storage.serial_number
self['Erase Date'] = device.event.erase.event_date
self['Erase Status'] = device.event.erase.privacy
self['Erase Type'] = device.event.erase.type
# For each DataStorage
self['DataStorage Serial Number'] = device.components.data_storage.serial_number
self['DataStorage Model'] = device.components.data_storage.model
self['DataStorage Manufacturer'] = device.components.data_storage.manufacturer
self['DataStorage Size (MB)'] = device.data_storage_size
self['Erase Date'] = device.event.erase.event_date
self['Erase Status'] = device.components.data_storage.privacy
# Erase information
self['Tool used to erase'] = device.erase_tool
self['Steps'] = device.events.erase.steps
self['Elapsed time'] = device.events.erase.erase_time
self['Final clean with zeros'] = 'Yes|No'
# Optional more computer info
self['Computer Serial Number'] = device.serial_number
self['Computer Model'] = device.model
self['Computer Manufacturer'] = device.manufacturer
self['Computer Tag'] = device.tags
2018-10-24 19:11:32 +00:00
"""
2018-10-23 16:59:58 +00:00
2018-10-11 15:51:58 +00:00
class DeviceRow(OrderedDict):
NUMS = {
2018-10-24 19:11:32 +00:00
Display.t: 2,
Processor.t: 2,
GraphicCard.t: 2,
Motherboard.t: 1,
NetworkAdapter.t: 2,
SoundCard.t: 2
2018-10-11 15:51:58 +00:00
}
def __init__(self, device: Device) -> None:
super().__init__()
self.device = device
2018-10-24 19:11:32 +00:00
# General information about device
2018-10-11 15:51:58 +00:00
self['Type'] = device.t
if isinstance(device, Computer):
2018-10-24 19:11:32 +00:00
self['Chassis'] = device.chassis
2018-10-11 15:51:58 +00:00
self['Tag 1'] = self['Tag 2'] = self['Tag 3'] = ''
for i, tag in zip(range(1, 3), device.tags):
self['Tag {}'.format(i)] = format(tag)
self['Serial Number'] = device.serial_number
self['Model'] = device.model
2018-10-18 16:16:49 +00:00
self['Manufacturer'] = device.manufacturer
2018-10-24 19:11:32 +00:00
# self['State'] = device.last_event_of()
self['Price'] = device.price
self['Registered in'] = format(device.created, '%c')
2018-10-11 15:51:58 +00:00
if isinstance(device, Computer):
self['Processor'] = device.processor_model
self['RAM (GB)'] = device.ram_size
2018-10-24 19:11:32 +00:00
self['Storage Size (MB)'] = device.data_storage_size
2018-10-18 16:16:49 +00:00
rate = device.rate
2018-10-11 15:51:58 +00:00
if rate:
self['Rate'] = rate.rating
self['Range'] = rate.rating_range
2018-10-18 16:16:49 +00:00
self['Processor Rate'] = rate.processor
self['Processor Range'] = rate.workbench.processor_range
self['RAM Rate'] = rate.ram
self['RAM Range'] = rate.workbench.ram_range
self['Data Storage Rate'] = rate.data_storage
self['Data Storage Range'] = rate.workbench.data_storage_range
2018-10-24 19:11:32 +00:00
# More specific information about components
2018-10-11 15:51:58 +00:00
if isinstance(device, Computer):
self.components()
2018-10-24 19:11:32 +00:00
2018-10-11 15:51:58 +00:00
def components(self):
2018-10-24 19:11:32 +00:00
"""
Function to get all components information of a device
"""
2018-10-11 15:51:58 +00:00
assert isinstance(self.device, Computer)
2018-10-24 19:11:32 +00:00
# todo put an input specific order (non alphabetic)
for type in sorted(app.resources[Component.t].subresources_types): # type: str
2018-10-11 15:51:58 +00:00
max = self.NUMS.get(type, 4)
2018-10-24 19:11:32 +00:00
if type not in ['Component', 'HardDrive', 'SolidStateDrive']:
i = 1
for component in (r for r in self.device.components if r.type == type):
self.fill_component(type, i, component)
i += 1
if i > max:
break
while i <= max:
self.fill_component(type, i)
i += 1
def fill_component(self, type, i, component=None):
"""
Function to put specific information of components in OrderedDict (csv)
:param type: type of component
:param component: device.components
"""
self['{} {}'.format(type, i)] = format(component) if component else ''
self['{} {} Manufacturer'.format(type, i)] = component.serial_number if component else ''
self['{} {} Model'.format(type, i)] = component.serial_number if component else ''
2018-10-11 15:51:58 +00:00
self['{} {} Serial Number'.format(type, i)] = component.serial_number if component else ''
2018-10-24 19:11:32 +00:00
""" Particular fields for component GraphicCard """
if isinstance(component, GraphicCard):
self['{} {} Memory (MB)'.format(type, i)] = component.memory
""" Particular fields for component DataStorage.t -> (HardDrive, SolidStateDrive) """
2018-10-11 15:51:58 +00:00
if isinstance(component, DataStorage):
2018-10-24 19:11:32 +00:00
self['{} {} Size (MB)'.format(type, i)] = component.size
self['{} {} Privacy'.format(type, i)] = component.privacy
# todo decide if is relevant more info about Motherboard
""" Particular fields for component Motherboard """
if isinstance(component, Motherboard):
self['{} {} Slots'.format(type, i)] = component.slots
""" Particular fields for component Processor """
if isinstance(component, Processor):
self['{} {} Number of cores'.format(type, i)] = component.cores
self['{} {} Speed (GHz)'.format(type, i)] = component.speed
""" Particular fields for component RamModule """
if isinstance(component, RamModule):
self['{} {} Size (MB)'.format(type, i)] = component.size
self['{} {} Speed (MHz)'.format(type, i)] = component.speed
self['{} {} Size'.format(type, i)] = component.size
# todo add Display size, ...
# todo add NetworkAdapter speedLink?
# todo add some ComputerAccessories
2018-09-30 17:40:28 +00:00
class ManufacturerView(View):
class FindArgs(marshmallow.Schema):
search = marshmallow.fields.Str(required=True,
# Disallow like operators
validate=lambda x: '%' not in x and '_' not in x)
2018-09-30 17:40:28 +00:00
2018-10-03 12:51:22 +00:00
@cache(datetime.timedelta(days=1))
2018-09-30 17:40:28 +00:00
def find(self, args: dict):
search = args['search']
2018-09-30 17:40:28 +00:00
manufacturers = Manufacturer.query \
.filter(Manufacturer.name.ilike(search + '%')) \
2018-09-30 17:40:28 +00:00
.paginate(page=1, per_page=6) # type: Pagination
return jsonify(
items=app.resources[Manufacturer.t].schema.dump(
manufacturers.items,
many=True,
nested=1
)
)