From 179feaba44e309a167c46f786a445d26cacbd26b Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 24 Feb 2022 14:15:58 +0100 Subject: [PATCH] add exports in front end --- ereuse_devicehub/inventory/views.py | 295 +++++++++++++----- ereuse_devicehub/static/js/main_inventory.js | 9 + .../templates/inventory/device_list.html | 27 +- .../templates/inventory/erasure.html | 92 ++++++ .../templates/inventory/trade.html | 4 + 5 files changed, 349 insertions(+), 78 deletions(-) create mode 100644 ereuse_devicehub/templates/inventory/erasure.html diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index fc0600bc..ca007b18 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -1,18 +1,24 @@ -import flask +import csv from io import StringIO -from flask import Blueprint, request, url_for, g, make_response + +import flask +import flask_weasyprint +from flask import Blueprint, g, make_response, request, url_for from flask.views import View from flask_login import current_user, login_required from werkzeug.exceptions import NotFound from ereuse_devicehub import messages -from ereuse_devicehub.inventory.forms import (AllocateForm, LotDeviceForm, - LotForm, NewActionForm, - NewDeviceForm, TagDeviceForm, - TagForm, TagUnnamedForm, - UploadSnapshotForm, DataWipeForm, - TradeForm, TradeDocumentForm) -from ereuse_devicehub.resources.device.models import Device +from ereuse_devicehub.inventory.forms import ( + AllocateForm, DataWipeForm, LotDeviceForm, LotForm, NewActionForm, + NewDeviceForm, TagDeviceForm, TagForm, TagUnnamedForm, TradeDocumentForm, + TradeForm, UploadSnapshotForm) +from ereuse_devicehub.resources.action.models import Trade +from ereuse_devicehub.resources.device.models import ( + Computer, DataStorage, Device) +from ereuse_devicehub.resources.documents.device_row import ( + ActionRow, DeviceRow) +from ereuse_devicehub.resources.hash_reports import insert_hash from ereuse_devicehub.resources.lot.models import Lot from ereuse_devicehub.resources.tag.model import Tag @@ -30,8 +36,11 @@ class DeviceListMix(View): filter_types = ['Desktop', 'Laptop', 'Server'] lots = Lot.query.filter(Lot.owner_id == current_user.id) lot = None - tags = Tag.query.filter(Tag.owner_id == current_user.id).filter( - Tag.device_id == None).order_by(Tag.created.desc()) + tags = ( + Tag.query.filter(Tag.owner_id == current_user.id) + .filter_by(device_id=None) + .order_by(Tag.created.desc()) + ) if lot_id: lot = lots.filter(Lot.id == lot_id).one() @@ -46,10 +55,12 @@ class DeviceListMix(View): user_from=g.user.email, ) else: - devices = Device.query.filter( - Device.owner_id == current_user.id).filter( - Device.type.in_(filter_types)).filter(Device.lots == None).order_by( - Device.updated.desc()) + devices = ( + Device.query.filter(Device.owner_id == current_user.id) + .filter(Device.type.in_(filter_types)) + .filter_by(lots=None) + .order_by(Device.updated.desc()) + ) form_new_action = NewActionForm() form_new_allocate = AllocateForm() form_new_datawipe = DataWipeForm() @@ -70,14 +81,13 @@ class DeviceListMix(View): 'form_new_trade': form_new_trade, 'lot': lot, 'tags': tags, - 'list_devices': list_devices + 'list_devices': list_devices, } return self.context class DeviceListView(DeviceListMix): - def dispatch_request(self, lot_id=None): self.get_context(lot_id) return flask.render_template(self.template_name, **self.context) @@ -89,8 +99,11 @@ class DeviceDetailView(View): def dispatch_request(self, id): lots = Lot.query.filter(Lot.owner_id == current_user.id) - device = Device.query.filter( - Device.owner_id == current_user.id).filter(Device.devicehub_id == id).one() + device = ( + Device.query.filter(Device.owner_id == current_user.id) + .filter(Device.devicehub_id == id) + .one() + ) context = { 'device': device, @@ -142,7 +155,9 @@ class LotCreateView(View): return flask.redirect(next_url) lots = Lot.query.filter(Lot.owner_id == current_user.id) - return flask.render_template(self.template_name, form=form, title=self.title, lots=lots) + return flask.render_template( + self.template_name, form=form, title=self.title, lots=lots + ) class LotUpdateView(View): @@ -159,7 +174,9 @@ class LotUpdateView(View): return flask.redirect(next_url) lots = Lot.query.filter(Lot.owner_id == current_user.id) - return flask.render_template(self.template_name, form=form, title=self.title, lots=lots) + return flask.render_template( + self.template_name, form=form, title=self.title, lots=lots + ) class LotDeleteView(View): @@ -186,7 +203,9 @@ class UploadSnapshotView(View): if form.validate_on_submit(): form.save() - return flask.render_template(self.template_name, form=form, lots=lots, **context) + return flask.render_template( + self.template_name, form=form, lots=lots, **context + ) class DeviceCreateView(View): @@ -203,7 +222,9 @@ class DeviceCreateView(View): next_url = url_for('inventory.devices.devicelist') return flask.redirect(next_url) - return flask.render_template(self.template_name, form=form, lots=lots, **context) + return flask.render_template( + self.template_name, form=form, lots=lots, **context + ) class TagListView(View): @@ -259,8 +280,9 @@ class TagDetailView(View): def dispatch_request(self, id): lots = Lot.query.filter(Lot.owner_id == current_user.id) - tag = Tag.query.filter( - Tag.owner_id == current_user.id).filter(Tag.id == id).one() + tag = ( + Tag.query.filter(Tag.owner_id == current_user.id).filter(Tag.id == id).one() + ) context = { 'lots': lots, @@ -296,7 +318,9 @@ class TagUnlinkDeviceView(View): next_url = url_for('inventory.devices.devicelist') return flask.redirect(next_url) - return flask.render_template(self.template_name, form=form, referrer=request.referrer) + return flask.render_template( + self.template_name, form=form, referrer=request.referrer + ) class NewActionView(View): @@ -308,8 +332,10 @@ class NewActionView(View): self.form = self.form_class() if self.form.validate_on_submit(): - instance = self.form.save() - messages.success('Action "{}" created successfully!'.format(self.form.type.data)) + self.form.save() + messages.success( + 'Action "{}" created successfully!'.format(self.form.type.data) + ) next_url = self.get_next_url() return flask.redirect(next_url) @@ -331,8 +357,10 @@ class NewAllocateView(NewActionView, DeviceListMix): self.form = self.form_class() if self.form.validate_on_submit(): - instance = self.form.save() - messages.success('Action "{}" created successfully!'.format(self.form.type.data)) + self.form.save() + messages.success( + 'Action "{}" created successfully!'.format(self.form.type.data) + ) next_url = self.get_next_url() return flask.redirect(next_url) @@ -351,8 +379,10 @@ class NewDataWipeView(NewActionView, DeviceListMix): self.form = self.form_class() if self.form.validate_on_submit(): - instance = self.form.save() - messages.success('Action "{}" created successfully!'.format(self.form.type.data)) + self.form.save() + messages.success( + 'Action "{}" created successfully!'.format(self.form.type.data) + ) next_url = self.get_next_url() return flask.redirect(next_url) @@ -371,8 +401,10 @@ class NewTradeView(NewActionView, DeviceListMix): self.form = self.form_class() if self.form.validate_on_submit(): - instance = self.form.save() - messages.success('Action "{}" created successfully!'.format(self.form.type.data)) + self.form.save() + messages.success( + 'Action "{}" created successfully!'.format(self.form.type.data) + ) next_url = self.get_next_url() return flask.redirect(next_url) @@ -399,68 +431,185 @@ class NewTradeDocumentView(View): next_url = url_for('inventory.devices.lotdevicelist', lot_id=lot_id) return flask.redirect(next_url) - return flask.render_template(self.template_name, form=self.form, title=self.title) + return flask.render_template( + self.template_name, form=self.form, title=self.title + ) class ExportsView(View): methods = ['GET'] decorators = [login_required] - # def dispatch_request(self, export_id): - def dispatch_request(self): - import pdb; pdb.trace() - export_id = 'metrics' + def dispatch_request(self, export_id): export_ids = { - 'metrics': self.metrics(), - 'devices-list': self.metrics(), - 'certificates': self.metrics() + 'metrics': self.metrics, + 'devices': self.devices_list, + 'certificates': self.erasure, + 'links': self.public_links, } if export_id not in export_ids: return NotFound() return export_ids[export_id]() + def find_devices(self): + args = request.args.get('ids') + ids = args.split(',') if args else [] + query = Device.query.filter(Device.owner == g.user) + return query.filter(Device.devicehub_id.in_(ids)) + + def response_csv(self, data, name): + bfile = data.getvalue().encode('utf-8') + # insert proof + insert_hash(bfile) + output = make_response(bfile) + output.headers['Content-Disposition'] = 'attachment; filename={}'.format(name) + output.headers['Content-type'] = 'text/csv' + return output + + def devices_list(self): + """Get device query and put information in csv format.""" + data = StringIO() + cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"') + first = True + + for device in self.find_devices(): + d = DeviceRow(device, {}) + if first: + cw.writerow(d.keys()) + first = False + cw.writerow(d.values()) + + return self.response_csv(data, "export.csv") + def metrics(self): """Get device query and put information in csv format.""" data = StringIO() - # cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"') - # first = True - # document_ids = self.get_documents_id() - # for device in query: - # d = DeviceRow(device, document_ids) - # if first: - # cw.writerow(d.keys()) - # first = False - # cw.writerow(d.values()) - bfile = data.getvalue().encode('utf-8') - output = make_response(bfile) - # insert_hash(bfile) - output.headers['Content-Disposition'] = 'attachment; filename=export.csv' - output.headers['Content-type'] = 'text/csv' - return output + cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"') + first = True + devs_id = [] + # Get the allocate info + for device in self.find_devices(): + devs_id.append(device.id) + for allocate in device.get_metrics(): + d = ActionRow(allocate) + if first: + cw.writerow(d.keys()) + first = False + cw.writerow(d.values()) + + # Get the trade info + query_trade = Trade.query.filter( + Trade.devices.any(Device.id.in_(devs_id)) + ).all() + + lot_id = request.args.get('lot') + if lot_id and not query_trade: + lot = Lot.query.filter_by(id=lot_id).one() + if hasattr(lot, "trade") and lot.trade: + if g.user in [lot.trade.user_from, lot.trade.user_to]: + query_trade = [lot.trade] + + for trade in query_trade: + data_rows = trade.get_metrics() + for row in data_rows: + d = ActionRow(row) + if first: + cw.writerow(d.keys()) + first = False + cw.writerow(d.values()) + + return self.response_csv(data, "actions_export.csv") + + def public_links(self): + # get a csv with the publink links of this devices + data = StringIO() + cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"') + cw.writerow(['links']) + host_url = request.host_url + for dev in self.find_devices(): + code = dev.devicehub_id + link = [f"{host_url}devices/{code}"] + cw.writerow(link) + + return self.response_csv(data, "links.csv") + + def erasure(self): + template = self.build_erasure_certificate() + res = flask_weasyprint.render_pdf( + flask_weasyprint.HTML(string=template), + download_filename='erasure-certificate.pdf', + ) + insert_hash(res.data) + return res + + def build_erasure_certificate(self): + erasures = [] + for device in self.find_devices(): + if isinstance(device, Computer): + for privacy in device.privacy: + erasures.append(privacy) + elif isinstance(device, DataStorage): + if device.privacy: + erasures.append(device.privacy) + + params = { + 'title': 'Erasure Certificate', + 'erasures': tuple(erasures), + 'url_pdf': '', + } + return flask.render_template('inventory/erasure.html', **params) devices.add_url_rule('/action/add/', view_func=NewActionView.as_view('action_add')) devices.add_url_rule('/action/trade/add/', view_func=NewTradeView.as_view('trade_add')) -devices.add_url_rule('/action/allocate/add/', view_func=NewAllocateView.as_view('allocate_add')) -devices.add_url_rule('/action/datawipe/add/', view_func=NewDataWipeView.as_view('datawipe_add')) -devices.add_url_rule('/lot//trade-document/add/', - view_func=NewTradeDocumentView.as_view('trade_document_add')) +devices.add_url_rule( + '/action/allocate/add/', view_func=NewAllocateView.as_view('allocate_add') +) +devices.add_url_rule( + '/action/datawipe/add/', view_func=NewDataWipeView.as_view('datawipe_add') +) +devices.add_url_rule( + '/lot//trade-document/add/', + view_func=NewTradeDocumentView.as_view('trade_document_add'), +) devices.add_url_rule('/device/', view_func=DeviceListView.as_view('devicelist')) -devices.add_url_rule('/device//', view_func=DeviceDetailView.as_view('device_details')) -devices.add_url_rule('/lot//device/', view_func=DeviceListView.as_view('lotdevicelist')) -devices.add_url_rule('/lot/devices/add/', view_func=LotDeviceAddView.as_view('lot_devices_add')) -devices.add_url_rule('/lot/devices/del/', view_func=LotDeviceDeleteView.as_view('lot_devices_del')) +devices.add_url_rule( + '/device//', view_func=DeviceDetailView.as_view('device_details') +) +devices.add_url_rule( + '/lot//device/', view_func=DeviceListView.as_view('lotdevicelist') +) +devices.add_url_rule( + '/lot/devices/add/', view_func=LotDeviceAddView.as_view('lot_devices_add') +) +devices.add_url_rule( + '/lot/devices/del/', view_func=LotDeviceDeleteView.as_view('lot_devices_del') +) devices.add_url_rule('/lot/add/', view_func=LotCreateView.as_view('lot_add')) -devices.add_url_rule('/lot//del/', view_func=LotDeleteView.as_view('lot_del')) +devices.add_url_rule( + '/lot//del/', view_func=LotDeleteView.as_view('lot_del') +) devices.add_url_rule('/lot//', view_func=LotUpdateView.as_view('lot_edit')) -devices.add_url_rule('/upload-snapshot/', view_func=UploadSnapshotView.as_view('upload_snapshot')) +devices.add_url_rule( + '/upload-snapshot/', view_func=UploadSnapshotView.as_view('upload_snapshot') +) devices.add_url_rule('/device/add/', view_func=DeviceCreateView.as_view('device_add')) devices.add_url_rule('/tag/', view_func=TagListView.as_view('taglist')) devices.add_url_rule('/tag/add/', view_func=TagAddView.as_view('tag_add')) -devices.add_url_rule('/tag/unnamed/add/', view_func=TagAddUnnamedView.as_view('tag_unnamed_add')) -devices.add_url_rule('/tag//', view_func=TagDetailView.as_view('tag_details')) -devices.add_url_rule('/tag/devices/add/', view_func=TagLinkDeviceView.as_view('tag_devices_add')) -devices.add_url_rule('/tag/devices//del/', view_func=TagUnlinkDeviceView.as_view('tag_devices_del')) -# devices.add_url_rule('/export/', view_func=ExportsView.as_view('export')) -devices.add_url_rule('/export/', view_func=ExportsView.as_view('export')) +devices.add_url_rule( + '/tag/unnamed/add/', view_func=TagAddUnnamedView.as_view('tag_unnamed_add') +) +devices.add_url_rule( + '/tag//', view_func=TagDetailView.as_view('tag_details') +) +devices.add_url_rule( + '/tag/devices/add/', view_func=TagLinkDeviceView.as_view('tag_devices_add') +) +devices.add_url_rule( + '/tag/devices//del/', + view_func=TagUnlinkDeviceView.as_view('tag_devices_del'), +) +devices.add_url_rule( + '/export//', view_func=ExportsView.as_view('export') +) diff --git a/ereuse_devicehub/static/js/main_inventory.js b/ereuse_devicehub/static/js/main_inventory.js index 1147241d..47650dcd 100644 --- a/ereuse_devicehub/static/js/main_inventory.js +++ b/ereuse_devicehub/static/js/main_inventory.js @@ -148,3 +148,12 @@ function get_device_list() { description = $.map(list_devices, function(x) { return x }).join(", "); $(".enumeration-devices").html(description); } + +function export_file(type_file) { + var devices = $(".deviceSelect").filter(':checked'); + var devices_id = $.map(devices, function(x) { return $(x).attr('data-device-dhid')}).join(","); + if (devices_id){ + var url = "/inventory/export/"+type_file+"/?ids="+devices_id; + window.location.href = url; + } +} diff --git a/ereuse_devicehub/templates/inventory/device_list.html b/ereuse_devicehub/templates/inventory/device_list.html index ce3350af..3ad4f69c 100644 --- a/ereuse_devicehub/templates/inventory/device_list.html +++ b/ereuse_devicehub/templates/inventory/device_list.html @@ -207,8 +207,27 @@ @@ -352,9 +371,7 @@ {% include "inventory/actions.html" %} {% include "inventory/allocate.html" %} {% include "inventory/data_wipe.html" %} -{% if lot and lot.is_temporary %} - {% include "inventory/trade.html" %} -{% endif %} +{% include "inventory/trade.html" %} diff --git a/ereuse_devicehub/templates/inventory/erasure.html b/ereuse_devicehub/templates/inventory/erasure.html new file mode 100644 index 00000000..5a16390c --- /dev/null +++ b/ereuse_devicehub/templates/inventory/erasure.html @@ -0,0 +1,92 @@ +{% extends "documents/layout.html" %} +{% block body %} +
+

Summary

+ + + + + + + + + + + {% for erasure in erasures %} + + + + + + + {% endfor %} + +
S/N Data StorageType of erasureResultDate
+ {{ erasure.device.serial_number.upper() }} + + {{ erasure.type }} + + {{ erasure.severity }} + + {{ erasure.date_str }} +
+
+
+

Details

+ {% for erasure in erasures %} +
+

{{ erasure.device.__format__('t') }}

+
+
Data storage:
+
{{ erasure.device.__format__('ts') }}
+ +
Computer where was erase:
+
Title: {{ erasure.parent.__format__('ts') }}
+
DevicehubID: {{ erasure.parent.devicehub_id }}
+
Hid: {{ erasure.parent.hid }}
+
Tags: {{ erasure.parent.tags }}
+ +
Computer where it resides:
+
Title: {{ erasure.device.parent.__format__('ts') }}
+
DevicehubID: {{ erasure.device.parent.devicehub_id }}
+
Hid: {{ erasure.device.parent.hid }}
+
Tags: {{ erasure.device.parent.tags }}
+ +
Erasure:
+
{{ erasure.__format__('ts') }}
+ {% if erasure.steps %} +
Erasure steps:
+
+
    + {% for step in erasure.steps %} +
  1. {{ step.__format__('') }}
  2. + {% endfor %} +
+
+ {% endif %} +
+
+ {% endfor %} +
+
+

Glossary

+
+
Erase Basic
+
+ A software-based fast non-100%-secured way of erasing data storage, + using shred. +
+
Erase Sectors
+
+ A secured-way of erasing data storages, checking sector-by-sector + the erasure, using badblocks. +
+
+
+ + +{% endblock %} diff --git a/ereuse_devicehub/templates/inventory/trade.html b/ereuse_devicehub/templates/inventory/trade.html index 42b39aa0..ae106cba 100644 --- a/ereuse_devicehub/templates/inventory/trade.html +++ b/ereuse_devicehub/templates/inventory/trade.html @@ -55,4 +55,8 @@ +{% else %} + {% endif %}