add upload form placeholder

This commit is contained in:
Cayo Puigdefabregas 2022-07-04 11:26:24 +02:00
parent b22ea880dd
commit 71518ca2a5
10 changed files with 320 additions and 70 deletions

View file

@ -3,6 +3,7 @@ import datetime
import json import json
from json.decoder import JSONDecodeError from json.decoder import JSONDecodeError
import pandas as pd
from boltons.urlutils import URL from boltons.urlutils import URL
from flask import current_app as app from flask import current_app as app
from flask import g, request from flask import g, request
@ -53,7 +54,6 @@ from ereuse_devicehub.resources.device.models import (
Smartphone, Smartphone,
Tablet, Tablet,
) )
from ereuse_devicehub.resources.device.sync import Sync
from ereuse_devicehub.resources.documents.models import DataWipeDocument from ereuse_devicehub.resources.documents.models import DataWipeDocument
from ereuse_devicehub.resources.enums import Severity from ereuse_devicehub.resources.enums import Severity
from ereuse_devicehub.resources.hash_reports import insert_hash from ereuse_devicehub.resources.hash_reports import insert_hash
@ -404,8 +404,8 @@ class NewDeviceForm(FlaskForm):
is_valid = False is_valid = False
if self.phid.data and self.amount.data == 1: if self.phid.data and self.amount.data == 1:
dev = Device.query.filter_by( dev = Placeholder.query.filter(
hid=self.phid.data, owner=g.user, active=True Placeholder.phid == self.phid.data, Device.owner == g.user
).first() ).first()
if dev: if dev:
msg = "Sorry, exist one snapshot device with this HID" msg = "Sorry, exist one snapshot device with this HID"
@ -435,7 +435,7 @@ class NewDeviceForm(FlaskForm):
db.session.commit() db.session.commit()
def create_device(self): def create_device(self):
schema = SnapshotSchema()
json_snapshot = { json_snapshot = {
'type': 'Snapshot', 'type': 'Snapshot',
'software': 'Web', 'software': 'Web',
@ -466,45 +466,20 @@ class NewDeviceForm(FlaskForm):
'functionalityRange': self.functionality.data, 'functionalityRange': self.functionality.data,
} }
] ]
# import pdb; pdb.set_trace()
upload_form = UploadSnapshotForm()
upload_form.sync = Sync()
schema = SnapshotSchema()
self.tmp_snapshots = '/tmp/'
path_snapshot = save_json(json_snapshot, self.tmp_snapshots, g.user.email)
snapshot_json = schema.load(json_snapshot) snapshot_json = schema.load(json_snapshot)
device = snapshot_json['device']
if self.type.data == 'ComputerMonitor': if self.type.data == 'ComputerMonitor':
snapshot_json['device'].resolution_width = self.resolution.data device.resolution_width = self.resolution.data
snapshot_json['device'].size = self.screen.data device.size = self.screen.data
if self.type.data in ['Smartphone', 'Tablet', 'Cellphone']: if self.type.data in ['Smartphone', 'Tablet', 'Cellphone']:
snapshot_json['device'].imei = self.imei.data device.imei = self.imei.data
snapshot_json['device'].meid = self.meid.data device.meid = self.meid.data
snapshot_json['device'].placeholder = self.get_placeholder() device.placeholder = self.get_placeholder()
snapshot_json['device'].hid = self.phid.data db.session.add(device)
snapshot = upload_form.build(snapshot_json)
move_json(self.tmp_snapshots, path_snapshot, g.user.email)
if self.type.data == 'ComputerMonitor':
snapshot.device.resolution = self.resolution.data
snapshot.device.screen = self.screen.data
return snapshot
def get_phid(self):
_hid = self.phid.data
if not _hid:
_hid = Placeholder.query.order_by(Placeholder.id.desc()).first()
if _hid:
_hid = str(_hid.id + 1)
else:
_hid = '1'
self.phid.data = _hid.lower()
def reset_ids(self): def reset_ids(self):
if self.amount.data > 1: if self.amount.data > 1:
@ -514,11 +489,11 @@ class NewDeviceForm(FlaskForm):
self.sku.data = None self.sku.data = None
self.imei.data = None self.imei.data = None
self.meid.data = None self.meid.data = None
self.get_phid()
def get_placeholder(self): def get_placeholder(self):
self.placeholder = Placeholder( self.placeholder = Placeholder(
**{ **{
'phid': self.phid.data or None,
'id_device_supplier': self.id_device_supplier.data, 'id_device_supplier': self.id_device_supplier.data,
'info': self.info.data, 'info': self.info.data,
'pallet': self.pallet.data, 'pallet': self.pallet.data,
@ -1359,3 +1334,97 @@ class NotesForm(FlaskForm):
db.session.commit() db.session.commit()
return self._obj return self._obj
class UploadPlaceholderForm(FlaskForm):
type = StringField('Type', [validators.DataRequired()])
placeholder_file = FileField(
'Select a Placeholder File', [validators.DataRequired()]
)
def validate(self, extra_validators=None):
is_valid = super().validate(extra_validators)
if not is_valid:
return False
files = request.files.getlist(self.placeholder_file.name)
if not files:
return False
data = pd.read_excel(files[0]).to_dict()
header = [
'Phid',
'Model',
'Manufacturer',
'Serial Number',
'Id device Supplier',
'Pallet',
'Info',
]
for k in header:
if k not in data.keys():
self.placeholder_file.errors = ["Missing required fields in the file"]
return False
self.placeholders = []
schema = SnapshotSchema()
self.path_snapshots = {}
for i in data['Phid'].keys():
placeholder = None
if data['Phid'][i]:
placeholder = Placeholder.query.filter_by(phid=data['Phid'][i]).first()
# update one
if placeholder:
device = placeholder.device
device.model = "{}".format(data['Model'][i]).lower()
device.manufacturer = "{}".format(data['Manufacturer'][i]).lower()
device.serial_number = "{}".format(data['Serial Number'][i]).lower()
placeholder.id_device_supplier = "{}".format(
data['Id device Supplier'][i]
)
placeholder.pallet = "{}".format(data['Pallet'][i])
placeholder.info = "{}".format(data['Info'][i])
self.placeholders.append(device)
continue
# create a new one
json_snapshot = {
'type': 'Snapshot',
'software': 'Web',
'version': '11.0',
'device': {
'type': self.type.data,
'model': "{}".format(data['Model'][i]),
'manufacturer': "{}".format(data['Manufacturer'][i]),
'serialNumber': "{}".format(data['Serial Number'][i]),
},
}
json_placeholder = {
'phid': data['Phid'][i] or None,
'id_device_supplier': data['Id device Supplier'][i],
'pallet': data['Pallet'][i],
'info': data['Info'][i],
}
snapshot_json = schema.load(json_snapshot)
device = snapshot_json['device']
device.placeholder = Placeholder(**json_placeholder)
self.placeholders.append(device)
return True
def save(self, commit=True):
for device in self.placeholders:
db.session.add(device)
if commit:
db.session.commit()
return self.placeholders

View file

@ -30,6 +30,7 @@ from ereuse_devicehub.inventory.forms import (
TradeDocumentForm, TradeDocumentForm,
TradeForm, TradeForm,
TransferForm, TransferForm,
UploadPlaceholderForm,
UploadSnapshotForm, UploadSnapshotForm,
) )
from ereuse_devicehub.labels.forms import PrintLabelsForm from ereuse_devicehub.labels.forms import PrintLabelsForm
@ -832,6 +833,34 @@ class ReceiverNoteView(GenericMixin):
return flask.redirect(next_url) return flask.redirect(next_url)
class UploadPlaceholderView(GenericMixin):
methods = ['GET', 'POST']
decorators = [login_required]
template_name = 'inventory/upload_placeholder.html'
def dispatch_request(self, lot_id=None):
self.get_context()
form = UploadPlaceholderForm()
self.context.update(
{
'page_title': 'Upload Placeholder',
'form': form,
'lot_id': lot_id,
}
)
if form.validate_on_submit():
snapshots = form.save(commit=False)
if lot_id:
lots = self.context['lots']
lot = lots.filter(Lot.id == lot_id).one()
for snap in snapshots:
lot.devices.add(snap.device)
db.session.add(lot)
db.session.commit()
return flask.render_template(self.template_name, **self.context)
devices.add_url_rule('/action/add/', view_func=NewActionView.as_view('action_add')) 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/trade/add/', view_func=NewTradeView.as_view('trade_add'))
devices.add_url_rule( devices.add_url_rule(
@ -902,3 +931,11 @@ devices.add_url_rule(
'/lot/<string:lot_id>/receivernote/', '/lot/<string:lot_id>/receivernote/',
view_func=ReceiverNoteView.as_view('receiver_note'), view_func=ReceiverNoteView.as_view('receiver_note'),
) )
devices.add_url_rule(
'/upload-placeholder/',
view_func=UploadPlaceholderView.as_view('upload_placeholder'),
)
devices.add_url_rule(
'/lot/<string:lot_id>/upload-placeholder/',
view_func=UploadPlaceholderView.as_view('lot_upload_placeholder'),
)

View file

@ -25,9 +25,6 @@ def get_inv():
def upgrade(): def upgrade():
# creating placeholder table # creating placeholder table
# con = op.get_bind()
# sql = f"CREATE SEQUENCE {get_inv()}.placeholder_id_seq START 1;"
# con.execute(sql)
op.create_table( op.create_table(
'placeholder', 'placeholder',
@ -44,13 +41,12 @@ def upgrade():
nullable=False, nullable=False,
), ),
sa.Column('id', sa.BigInteger(), nullable=False), sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column('phid', sa.Unicode(), nullable=False),
sa.Column('id_device_supplier', sa.Unicode(), nullable=True), sa.Column('id_device_supplier', sa.Unicode(), nullable=True),
sa.Column('pallet', sa.Unicode(), nullable=True), sa.Column('pallet', sa.Unicode(), nullable=True),
sa.Column('info', citext.CIText(), nullable=True), sa.Column('info', citext.CIText(), nullable=True),
sa.Column('device_id', sa.BigInteger(), nullable=False), sa.Column('device_id', sa.BigInteger(), nullable=False),
sa.Column('binding_id', sa.BigInteger(), nullable=True),
sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id']), sa.ForeignKeyConstraint(['device_id'], [f'{get_inv()}.device.id']),
sa.ForeignKeyConstraint(['binding_id'], [f'{get_inv()}.device.id']),
sa.PrimaryKeyConstraint('id'), sa.PrimaryKeyConstraint('id'),
schema=f'{get_inv()}', schema=f'{get_inv()}',
) )

View file

@ -75,6 +75,13 @@ def create_code(context):
return hashcode.encode(_id) return hashcode.encode(_id)
def create_phid(context):
_hid = Placeholder.query.order_by(Placeholder.id.desc()).first()
if _hid:
return str(_hid.id + 1)
return '1'
class Device(Thing): class Device(Thing):
"""Base class for any type of physical object that can be identified. """Base class for any type of physical object that can be identified.
@ -794,6 +801,7 @@ class DisplayMixin:
class Placeholder(Thing): class Placeholder(Thing):
id = Column(BigInteger, Sequence('placeholder_seq'), primary_key=True) id = Column(BigInteger, Sequence('placeholder_seq'), primary_key=True)
pallet = Column(Unicode(), nullable=True) pallet = Column(Unicode(), nullable=True)
phid = Column(Unicode(), nullable=False, default=create_phid)
pallet.comment = "used for identification where from where is this placeholders" pallet.comment = "used for identification where from where is this placeholders"
info = db.Column(CIText()) info = db.Column(CIText())
info.comment = "more info of placeholders" info.comment = "more info of placeholders"
@ -814,18 +822,6 @@ class Placeholder(Thing):
) )
device_id.comment = "datas of the placeholder" device_id.comment = "datas of the placeholder"
binding_id = db.Column(
BigInteger,
db.ForeignKey(Device.id),
nullable=True,
)
binding = db.relationship(
Device,
backref=backref('binding', lazy=True, uselist=False),
primaryjoin=binding_id == Device.id,
)
binding_id.comment = "device with snapshots than is linked to the placeholder"
class Computer(Device): class Computer(Device):
"""A chassis with components inside that can be processed """A chassis with components inside that can be processed

View file

@ -38,7 +38,6 @@ function amountInputs() {
$("#Id_device_supplier").show(); $("#Id_device_supplier").show();
$("#Serial_number").show(); $("#Serial_number").show();
$("#Sku").show(); $("#Sku").show();
$("#imei").show(); deviceInputs();
$("#meid").show();
}; };
} }

View file

@ -330,9 +330,9 @@
</li> </li>
<li> <li>
{% if lot %} {% if lot %}
<a href="{{ url_for('inventory.lot_upload_snapshot', lot_id=lot.id) }}" class="dropdown-item"> <a href="{{ url_for('inventory.lot_upload_placeholder', lot_id=lot.id) }}" class="dropdown-item">
{% else %} {% else %}
<a href="{{ url_for('inventory.upload_snapshot') }}" class="dropdown-item"> <a href="{{ url_for('inventory.upload_placeholder') }}" class="dropdown-item">
{% endif %} {% endif %}
<i class="bi bi-upload"></i> <i class="bi bi-upload"></i>
Upload Placeholder Spreadsheet Upload Placeholder Spreadsheet

View file

@ -0,0 +1,117 @@
{% extends "ereuse_devicehub/base_site.html" %}
{% block main %}
<div class="pagetitle">
<h1>Inventory</h1>
<nav>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('inventory.devicelist')}}">Inventory</a></li>
<li class="breadcrumb-item active">{{ page_title }}</li>
</ol>
</nav>
</div><!-- End Page Title -->
<section class="section profile">
<div class="row">
<div class="col-xl-8">
<div class="card">
<div class="card-body">
<div class="pt-4 pb-2">
<h5 class="card-title text-center pb-0 fs-4">Upload Placeholder</h5>
<p class="text-center small">Please select a Placeholders CSV file.</p>
{% if form.form_errors %}
<p class="text-danger">
{% for error in form.form_errors %}
{{ error }}<br/>
{% endfor %}
</p>
{% endif %}
</div>
<div class="pt-4 pb-2 mb-3">
Use the following template to register or update placeholders.<br />
Choose the type of device.<br />
The following considerations are important:<br />
1. Do not rename columns or add new columns.<br />
2. Accepted file types are ods, xlsx and csv.<br />
3. A new Placeholder will be registered if the PHID value does not exist in the system or is empty.<br />
4. A Placeholder will be updated if the PHID value exists in the system
</div>
<form method="post" enctype="multipart/form-data" class="row g-3 needs-validation" novalidate>
{{ form.csrf_token }}
<div class="form-group has-validation mb-2">
<label for="name" class="form-label">Type *</label>
<select id="type" class="form-control" name="type" required="">
<option value="">Select one Type</option>
<optgroup label="Computer">
<option value="Laptop"
{% if form.type.data == 'Laptop' %} selected="selected"{% endif %}>Laptop</option>
<option value="Desktop"
{% if form.type.data == 'Desktop' %} selected="selected"{% endif %}>Desktop</option>
<option value="Server"
{% if form.type.data == 'Server' %} selected="selected"{% endif %}>Server</option>
</optgroup>
<optgroup label="Monitor">
<option value="ComputerMonitor"
{% if form.type.data == 'Monitor' %} selected="selected"{% endif %}>Computer Monitor</option>
</optgroup>
<optgroup label="Mobile">
<option value="Smartphone"
{% if form.type.data == 'Smartphone' %} selected="selected"{% endif %}>Smartphone</option>
<option value="Tablet"
{% if form.type.data == 'Tablet' %} selected="selected"{% endif %}>Tablet</option>
<option value="Cellphone"
{% if form.type.data == 'Cellphone' %} selected="selected"{% endif %}>Cellphone</option>
</optgroup>
<optgroup label="Computer Accessory">
<option value="Mouse"
{% if form.type.data == 'Mouse' %} selected="selected"{% endif %}>Mouse</option>
<option value="MemoryCardReader"
{% if form.type.data == 'MemoryCardReader' %} selected="selected"{% endif %}>Memory card reader</option>
<option value="SAI"
{% if form.type.data == 'SAI' %} selected="selected"{% endif %}>SAI</option>
<option value="Keyboard"
{% if form.type.data == 'Keyboard' %} selected="selected"{% endif %}>Keyboard</option>
</optgroup>
</select>
<small class="text-muted form-text">Type of devices</small>
{% if form.type.errors %}
<p class="text-danger">
{% for error in form.type.errors %}
{{ error }}<br/>
{% endfor %}
</p>
{% endif %}
</div>
<div>
<label for="name" class="form-label">Select a Placeholders CSV file *</label>
<div class="input-group has-validation">
{{ form.placeholder_file }}
</div>
</div>
<div>
{% if lot_id %}
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot_id) }}" class="btn btn-danger">Cancel</a>
{% else %}
<a href="{{ url_for('inventory.devicelist') }}" class="btn btn-danger">Cancel</a>
{% endif %}
<button class="btn btn-primary" type="submit">Send</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-xl-8">
</div>
</div>
</section>
{% endblock main %}

View file

@ -45,3 +45,7 @@ python-dotenv==0.14.0
pyjwt==2.4.0 pyjwt==2.4.0
pint==0.9 pint==0.9
py-dmidecode==0.1.0 py-dmidecode==0.1.0
pandas==1.3.5
numpy==1.21.6
odfpy==1.4.1
xlrd==2.0.1

Binary file not shown.

View file

@ -453,7 +453,8 @@ def test_add_monitor(user3: UserClientFlask):
dev = Device.query.one() dev = Device.query.one()
assert dev.type == 'Monitor' assert dev.type == 'Monitor'
assert dev.placeholder.id_device_supplier == "b2" assert dev.placeholder.id_device_supplier == "b2"
assert dev.hid == '1' assert dev.hid == 'monitor-samsung-lc27t55-aaaab'
assert dev.placeholder.phid == '1'
@pytest.mark.mvp @pytest.mark.mvp
@ -483,7 +484,8 @@ def test_update_monitor(user3: UserClientFlask):
dev = Device.query.one() dev = Device.query.one()
assert dev.type == 'Monitor' assert dev.type == 'Monitor'
assert dev.placeholder.id_device_supplier == "b2" assert dev.placeholder.id_device_supplier == "b2"
assert dev.hid == '1' assert dev.hid == 'monitor-samsung-lc27t55-aaaab'
assert dev.placeholder.phid == '1'
assert dev.model == 'lc27t55' assert dev.model == 'lc27t55'
assert dev.depth == 0.1 assert dev.depth == 0.1
assert dev.placeholder.pallet == "l34" assert dev.placeholder.pallet == "l34"
@ -508,7 +510,8 @@ def test_update_monitor(user3: UserClientFlask):
dev = Device.query.one() dev = Device.query.one()
assert dev.type == 'Monitor' assert dev.type == 'Monitor'
assert dev.placeholder.id_device_supplier == "b2" assert dev.placeholder.id_device_supplier == "b2"
assert dev.hid == '1' assert dev.hid == 'monitor-samsung-lc27t55-aaaab'
assert dev.placeholder.phid == '1'
assert dev.model == 'lc27t55' assert dev.model == 'lc27t55'
assert dev.depth == 0.1 assert dev.depth == 0.1
assert dev.placeholder.pallet == "l34" assert dev.placeholder.pallet == "l34"
@ -542,7 +545,8 @@ def test_add_2_monitor(user3: UserClientFlask):
dev = Device.query.one() dev = Device.query.one()
assert dev.type == 'Monitor' assert dev.type == 'Monitor'
assert dev.placeholder.id_device_supplier == "b1" assert dev.placeholder.id_device_supplier == "b1"
assert dev.hid == 'aab' assert dev.hid == 'monitor-samsung-lc27t55-aaaab'
assert dev.placeholder.phid == 'AAB'
assert dev.model == 'lc27t55' assert dev.model == 'lc27t55'
assert dev.placeholder.pallet == "l34" assert dev.placeholder.pallet == "l34"
@ -565,7 +569,8 @@ def test_add_2_monitor(user3: UserClientFlask):
dev = Device.query.all()[-1] dev = Device.query.all()[-1]
assert dev.type == 'Monitor' assert dev.type == 'Monitor'
assert dev.placeholder.id_device_supplier == "b2" assert dev.placeholder.id_device_supplier == "b2"
assert dev.hid == '2' assert dev.hid == 'monitor-samsung-lcd_43_b-aaaab'
assert dev.placeholder.phid == '2'
assert dev.model == 'lcd 43 b' assert dev.model == 'lcd 43 b'
assert dev.placeholder.pallet == "l20" assert dev.placeholder.pallet == "l20"
@ -596,7 +601,8 @@ def test_add_laptop(user3: UserClientFlask):
dev = Device.query.one() dev = Device.query.one()
assert dev.type == 'Laptop' assert dev.type == 'Laptop'
assert dev.placeholder.id_device_supplier == "b2" assert dev.placeholder.id_device_supplier == "b2"
assert dev.hid == '1' assert dev.hid == 'laptop-samsung-lc27t55-aaaab'
assert dev.placeholder.phid == '1'
@pytest.mark.mvp @pytest.mark.mvp
@ -628,21 +634,19 @@ def test_add_with_ammount_laptops(user3: UserClientFlask):
for dev in Device.query.all(): for dev in Device.query.all():
assert dev.type == 'Laptop' assert dev.type == 'Laptop'
assert dev.placeholder.id_device_supplier is None assert dev.placeholder.id_device_supplier is None
assert dev.hid in [str(x) for x in range(1, num + 1)] assert dev.hid is None
assert dev.placeholder.phid in [str(x) for x in range(1, num + 1)]
assert Device.query.count() == num assert Device.query.count() == num
@pytest.mark.mvp @pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__) @pytest.mark.usefixtures(conftest.app_context.__name__)
def test_add_laptop_duplicate(user3: UserClientFlask): def test_add_laptop_duplicate(user3: UserClientFlask):
file_name = 'real-eee-1001pxd.snapshot.12.json'
create_device(user3, file_name)
uri = '/inventory/device/add/' uri = '/inventory/device/add/'
body, status = user3.get(uri) body, status = user3.get(uri)
assert status == '200 OK' assert status == '200 OK'
assert "New Device" in body assert "New Device" in body
assert Device.query.count() == 10
data = { data = {
'csrf_token': generate_csrf(), 'csrf_token': generate_csrf(),
@ -658,8 +662,10 @@ def test_add_laptop_duplicate(user3: UserClientFlask):
} }
body, status = user3.post(uri, data=data) body, status = user3.post(uri, data=data)
assert status == '200 OK' assert status == '200 OK'
assert Device.query.count() == 1
body, status = user3.post(uri, data=data)
assert 'Sorry, exist one snapshot device with this HID' in body assert 'Sorry, exist one snapshot device with this HID' in body
assert Device.query.count() == 10 assert Device.query.count() == 1
@pytest.mark.mvp @pytest.mark.mvp
@ -1609,3 +1615,29 @@ def test_export_snapshot_json(user3: UserClientFlask):
body, status = user3.get(uri) body, status = user3.get(uri)
assert status == '200 OK' assert status == '200 OK'
assert body == snapshot assert body == snapshot
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_add_placeholder_excel(user3: UserClientFlask):
uri = '/inventory/upload-placeholder/'
body, status = user3.get(uri)
assert status == '200 OK'
assert "Upload Placeholder" in body
file_path = Path(__file__).parent.joinpath('files').joinpath('placeholder_test.xls')
with open(file_path, 'rb') as excel:
data = {
'csrf_token': generate_csrf(),
'type': "Laptop",
'placeholder_file': excel,
}
user3.post(uri, data=data, content_type="multipart/form-data")
assert Device.query.count() == 1
dev = Device.query.first()
assert dev.hid == 'laptop-sony-vaio-12345678'
assert dev.placeholder.phid == 'a123'
assert dev.placeholder.info == 'Good conditions'
assert dev.placeholder.pallet == '24A'
assert dev.placeholder.id_device_supplier == 'TTT'