resolve conflict

This commit is contained in:
Cayo Puigdefabregas 2022-07-07 16:33:43 +02:00
commit c9cd68b817
10 changed files with 406 additions and 5 deletions

View File

@ -30,6 +30,7 @@ from wtforms.fields import FormField
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.inventory.models import DeliveryNote, ReceiverNote, Transfer from ereuse_devicehub.inventory.models import DeliveryNote, ReceiverNote, Transfer
from ereuse_devicehub.parser.models import PlaceholdersLog
from ereuse_devicehub.parser.parser import ParseSnapshotLsHw from ereuse_devicehub.parser.parser import ParseSnapshotLsHw
from ereuse_devicehub.parser.schemas import Snapshot_lite from ereuse_devicehub.parser.schemas import Snapshot_lite
from ereuse_devicehub.resources.action.models import Snapshot, Trade from ereuse_devicehub.resources.action.models import Snapshot, Trade
@ -540,6 +541,11 @@ class NewDeviceForm(FlaskForm):
device.placeholder = self.get_placeholder() device.placeholder = self.get_placeholder()
db.session.add(device) db.session.add(device)
placeholder_log = PlaceholdersLog(
type="New device", source='Web form', placeholder=device.placeholder
)
db.session.add(placeholder_log)
def reset_ids(self): def reset_ids(self):
if self.amount.data > 1: if self.amount.data > 1:
self.phid.data = None self.phid.data = None
@ -595,6 +601,10 @@ class NewDeviceForm(FlaskForm):
and self.functionality.data != self._obj.functionality().name and self.functionality.data != self._obj.functionality().name
): ):
self._obj.set_functionality(self.functionality.data) self._obj.set_functionality(self.functionality.data)
placeholder_log = PlaceholdersLog(
type="Update", source='Web form', placeholder=self._obj.placeholder
)
db.session.add(placeholder_log)
class TagDeviceForm(FlaskForm): class TagDeviceForm(FlaskForm):
@ -1445,6 +1455,7 @@ class UploadPlaceholderForm(FlaskForm):
_file = files[0] _file = files[0]
if _file.content_type == 'text/csv': if _file.content_type == 'text/csv':
self.source = "CSV File: {}".format(_file.filename)
delimiter = ';' delimiter = ';'
data = pd.read_csv(_file).to_dict() data = pd.read_csv(_file).to_dict()
head = list(data.keys())[0].split(delimiter) head = list(data.keys())[0].split(delimiter)
@ -1458,6 +1469,7 @@ class UploadPlaceholderForm(FlaskForm):
for k, v in x.items(): for k, v in x.items():
data[head[i]][k] = v[i] data[head[i]][k] = v[i]
else: else:
self.source = "Excel File: {}".format(_file.filename)
try: try:
data = pd.read_excel(_file).to_dict() data = pd.read_excel(_file).to_dict()
except ValueError: except ValueError:
@ -1514,7 +1526,10 @@ class UploadPlaceholderForm(FlaskForm):
placeholder.pallet = "{}".format(data['Pallet'][i]) placeholder.pallet = "{}".format(data['Pallet'][i])
placeholder.info = "{}".format(data['Info'][i]) placeholder.info = "{}".format(data['Info'][i])
self.placeholders.append(device) placeholder_log = PlaceholdersLog(
type="Update", source=self.source, placeholder=device.placeholder
)
self.placeholders.append((device, placeholder_log))
continue continue
# create a new one # create a new one
@ -1540,14 +1555,18 @@ class UploadPlaceholderForm(FlaskForm):
device = snapshot_json['device'] device = snapshot_json['device']
device.placeholder = Placeholder(**json_placeholder) device.placeholder = Placeholder(**json_placeholder)
self.placeholders.append(device) placeholder_log = PlaceholdersLog(
type="New device", source=self.source, placeholder=device.placeholder
)
self.placeholders.append((device, placeholder_log))
return True return True
def save(self, commit=True): def save(self, commit=True):
for device in self.placeholders: for device, placeholder_log in self.placeholders:
db.session.add(device) db.session.add(device)
db.session.add(placeholder_log)
if commit: if commit:
db.session.commit() db.session.commit()

View File

@ -34,7 +34,7 @@ from ereuse_devicehub.inventory.forms import (
UploadSnapshotForm, UploadSnapshotForm,
) )
from ereuse_devicehub.labels.forms import PrintLabelsForm from ereuse_devicehub.labels.forms import PrintLabelsForm
from ereuse_devicehub.parser.models import SnapshotsLog from ereuse_devicehub.parser.models import PlaceholdersLog, SnapshotsLog
from ereuse_devicehub.resources.action.models import Trade from ereuse_devicehub.resources.action.models import Trade
from ereuse_devicehub.resources.device.models import Computer, DataStorage, Device from ereuse_devicehub.resources.device.models import Computer, DataStorage, Device
from ereuse_devicehub.resources.documents.device_row import ActionRow, DeviceRow from ereuse_devicehub.resources.documents.device_row import ActionRow, DeviceRow
@ -880,7 +880,7 @@ class UploadPlaceholderView(GenericMixin):
if lot_id: if lot_id:
lots = self.context['lots'] lots = self.context['lots']
lot = lots.filter(Lot.id == lot_id).one() lot = lots.filter(Lot.id == lot_id).one()
for device in snapshots: for device, p in snapshots:
lot.devices.add(device) lot.devices.add(device)
db.session.add(lot) db.session.add(lot)
db.session.commit() db.session.commit()
@ -889,6 +889,24 @@ class UploadPlaceholderView(GenericMixin):
return flask.render_template(self.template_name, **self.context) return flask.render_template(self.template_name, **self.context)
class PlaceholderLogListView(GenericMixin):
template_name = 'inventory/placeholder_log_list.html'
def dispatch_request(self):
self.get_context()
self.context['page_title'] = "Placeholder Logs"
self.context['placeholders_log'] = self.get_placeholders_log()
return flask.render_template(self.template_name, **self.context)
def get_placeholders_log(self):
placeholder_log = PlaceholdersLog.query.filter(
PlaceholdersLog.owner == g.user
).order_by(PlaceholdersLog.created.desc())
return placeholder_log
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(
@ -970,3 +988,6 @@ devices.add_url_rule(
'/lot/<string:lot_id>/upload-placeholder/', '/lot/<string:lot_id>/upload-placeholder/',
view_func=UploadPlaceholderView.as_view('lot_upload_placeholder'), view_func=UploadPlaceholderView.as_view('lot_upload_placeholder'),
) )
devices.add_url_rule(
'/placeholder-logs/', view_func=PlaceholderLogListView.as_view('placeholder_logs')
)

View File

@ -0,0 +1,65 @@
"""placeholder log
Revision ID: 3e3a67f62972
Revises: aeca9fb50cc6
Create Date: 2022-07-06 18:23:54.267003
"""
import citext
import sqlalchemy as sa
from alembic import context, op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '3e3a67f62972'
down_revision = 'aeca9fb50cc6'
branch_labels = None
depends_on = None
def get_inv():
INV = context.get_x_argument(as_dictionary=True).get('inventory')
if not INV:
raise ValueError("Inventory value is not specified")
return INV
def upgrade():
op.create_table(
'placeholders_log',
sa.Column(
'updated',
sa.TIMESTAMP(timezone=True),
server_default=sa.text('CURRENT_TIMESTAMP'),
nullable=False,
comment='The last time Devicehub recorded a change for \n this thing.\n ',
),
sa.Column(
'created',
sa.TIMESTAMP(timezone=True),
server_default=sa.text('CURRENT_TIMESTAMP'),
nullable=False,
comment='When Devicehub created this.',
),
sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column('source', citext.CIText(), nullable=True),
sa.Column('type', citext.CIText(), nullable=True),
sa.Column('severity', sa.SmallInteger(), nullable=False),
sa.Column('placeholder_id', sa.BigInteger(), nullable=True),
sa.Column('owner_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(
['placeholder_id'],
[f'{get_inv()}.placeholder.id'],
),
sa.ForeignKeyConstraint(
['owner_id'],
['common.user.id'],
),
sa.PrimaryKeyConstraint('id'),
)
op.execute("CREATE SEQUENCE placeholders_log_seq START 1;")
def downgrade():
op.drop_table('placeholders_log')
op.execute("DROP SEQUENCE placeholders_log_seq;")

View File

@ -5,6 +5,7 @@ from sqlalchemy.dialects.postgresql import UUID
from ereuse_devicehub.db import db from ereuse_devicehub.db import db
from ereuse_devicehub.resources.action.models import Snapshot from ereuse_devicehub.resources.action.models import Snapshot
from ereuse_devicehub.resources.device.models import Placeholder
from ereuse_devicehub.resources.enums import Severity from ereuse_devicehub.resources.enums import Severity
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
@ -43,3 +44,48 @@ class SnapshotsLog(Thing):
return self.snapshot.device.devicehub_id return self.snapshot.device.devicehub_id
return '' return ''
class PlaceholdersLog(Thing):
"""A Placeholder log."""
__table_args__ = {'schema': ''}
id = Column(BigInteger, Sequence('placeholders_log_seq'), primary_key=True)
source = Column(CIText(), default='', nullable=True)
type = Column(CIText(), default='', nullable=True)
severity = Column(SmallInteger, default=Severity.Info, nullable=False)
placeholder_id = Column(BigInteger, db.ForeignKey(Placeholder.id), nullable=True)
placeholder = db.relationship(
Placeholder, primaryjoin=placeholder_id == Placeholder.id
)
owner_id = db.Column(
UUID(as_uuid=True),
db.ForeignKey(User.id),
nullable=False,
default=lambda: g.user.id,
)
owner = db.relationship(User, primaryjoin=owner_id == User.id)
def save(self, commit=False):
db.session.add(self)
if commit:
db.session.commit()
@property
def phid(self):
if self.placeholder:
return self.placeholder.phid
return ''
@property
def dhid(self):
if self.placeholder:
return self.placeholder.device.devicehub_id
return ''
def get_status(self):
return Severity(self.severity)

View File

@ -148,6 +148,15 @@
</a> </a>
</li> </li>
<li class="nav-heading">Placeholders</li>
<li class="nav-item">
<a class="nav-link collapsed" href="{{ url_for('inventory.placeholder_logs') }}">
<i class="bi-menu-button-wide"></i>
<span>Uploaded Placeholders</span>
</a>
</li>
<li class="nav-heading">Devices</li> <li class="nav-heading">Devices</li>
<li class="nav-item"> <li class="nav-item">

View File

@ -0,0 +1,80 @@
{% extends "ereuse_devicehub/base_site.html" %}
{% block main %}
<div class="pagetitle">
<h1>{{ page_title }}</h1>
<nav>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('inventory.devicelist')}}">Inventory</a></li>
<li class="breadcrumb-item active">Placeholders</li>
</ol>
</nav>
</div><!-- End Page Title -->
<section class="section profile">
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="card-body pt-3" style="min-height: 650px;">
<!-- Bordered Tabs -->
<div class="tab-content pt-5">
<div id="devices-list" class="tab-pane fade devices-list active show">
<div class="tab-content pt-2">
<table class="table">
<thead>
<tr>
<th scope="col">PHID</th>
<th scope="col">Placeholder source</th>
<th scope="col">Type</th>
<th scope="col">DHID</th>
<th scope="col">Status</th>
<th scope="col" data-type="date" data-format="DD-MM-YYYY">Time</th>
</tr>
</thead>
<tbody>
{% for log in placeholders_log %}
<tr>
<td>
{{ log.phid }}
</td>
<td>
{{ log.source }}
</td>
<td>
{{ log.type }}
</td>
<td>
{% if log.dhid %}
<a href="{{ url_for('inventory.device_details', id=log.dhid)}}">{{ log.dhid }}</a>
{% endif %}
</td>
<td>
{{ log.get_status() }}
</td>
<td>{{ log.created.strftime('%H:%M %d-%m-%Y') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div><!-- End Bordered Tabs -->
</div>
</div>
</div>
<div id="NotificationsContainer" style="position: absolute; bottom: 0; right: 0; margin: 10px; margin-top: 70px; width: calc(100% - 310px);"></div>
</div>
</div>
</section>
<!-- Custom Code -->
<script>
const table = new simpleDatatables.DataTable("table")
</script>
{% endblock main %}

View File

@ -0,0 +1,4 @@
Phid;Model;Manufacturer;Serial Number;Id device Supplier;Pallet;Info
a123;Vaio;Sony;12345678;TTT;24A;Good conditions
a124;Vaio;Sony;12345679;TTT;24A;Good conditions
a125;Vaio;Sony;12345680;TTT;24A;Good conditions
1 Phid Model Manufacturer Serial Number Id device Supplier Pallet Info
2 a123 Vaio Sony 12345678 TTT 24A Good conditions
3 a124 Vaio Sony 12345679 TTT 24A Good conditions
4 a125 Vaio Sony 12345680 TTT 24A Good conditions

Binary file not shown.

Binary file not shown.

View File

@ -1781,3 +1781,160 @@ def test_edit_laptop(user3: UserClientFlask):
assert dev.placeholder.id_device_supplier == 'a2' assert dev.placeholder.id_device_supplier == 'a2'
assert dev.serial_number == 'aaaac' assert dev.serial_number == 'aaaac'
assert dev.model == 'lc27t56' assert dev.model == 'lc27t56'
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_placeholder_log_manual_new(user3: UserClientFlask):
uri = '/inventory/device/add/'
user3.get(uri)
data = {
'csrf_token': generate_csrf(),
'type': "Laptop",
'phid': 'ace',
'serial_number': "AAAAB",
'model': "LC27T55",
'manufacturer': "Samsung",
'generation': 1,
'weight': 0.1,
'height': 0.1,
'depth': 0.1,
'id_device_supplier': "b2",
}
user3.post(uri, data=data)
uri = '/inventory/placeholder-logs/'
body, status = user3.get(uri)
assert status == '200 OK'
assert "Placeholder Logs" in body
assert "Web form" in body
assert "ace" in body
assert "New device" in body
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_placeholder_log_manual_edit(user3: UserClientFlask):
uri = '/inventory/device/add/'
user3.get(uri)
data = {
'csrf_token': generate_csrf(),
'type': "Laptop",
'phid': 'ace',
'serial_number': "AAAAB",
'model': "LC27T55",
'manufacturer': "Samsung",
'generation': 1,
'weight': 0.1,
'height': 0.1,
'depth': 0.1,
'id_device_supplier': "b2",
}
user3.post(uri, data=data)
dev = Device.query.one()
uri = '/inventory/device/edit/{}/'.format(dev.devicehub_id)
user3.get(uri)
data = {
'csrf_token': generate_csrf(),
'type': "Laptop",
'serial_number': "AAAAC",
'model': "LC27T56",
'manufacturer': "Samsung",
'generation': 1,
'weight': 0.1,
'height': 0.1,
'depth': 0.1,
'id_device_supplier': "a2",
}
user3.post(uri, data=data)
uri = '/inventory/placeholder-logs/'
body, status = user3.get(uri)
assert status == '200 OK'
assert "Placeholder Logs" in body
assert "Web form" in body
assert "ace" in body
assert "Update" in body
assert dev.devicehub_id in body
assert "" in body
assert "CSV" not in body
assert "Excel" not in body
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_placeholder_log_excel_new(user3: UserClientFlask):
uri = '/inventory/upload-placeholder/'
body, status = user3.get(uri)
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")
dev = Device.query.first()
assert dev.placeholder.phid == 'a123'
uri = '/inventory/placeholder-logs/'
body, status = user3.get(uri)
assert status == '200 OK'
assert "Placeholder Logs" in body
assert dev.placeholder.phid in body
assert dev.devicehub_id in body
assert "Web form" not in body
assert "Update" not in body
assert "New device" in body
assert "" in body
assert "CSV" not in body
assert "Excel" in body
assert "placeholder_test.xls" in body
@pytest.mark.mvp
@pytest.mark.usefixtures(conftest.app_context.__name__)
def test_placeholder_log_excel_update(user3: UserClientFlask):
uri = '/inventory/upload-placeholder/'
body, status = user3.get(uri)
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")
file_path = Path(__file__).parent.joinpath('files').joinpath('placeholder_test.csv')
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")
dev = Device.query.first()
assert dev.placeholder.phid == 'a123'
uri = '/inventory/placeholder-logs/'
body, status = user3.get(uri)
assert status == '200 OK'
assert "Placeholder Logs" in body
assert dev.placeholder.phid in body
assert dev.devicehub_id in body
assert "Web form" not in body
assert "Update" in body
assert "New device" in body
assert "" in body
assert "CSV" in body
assert "Excel" in body
assert "placeholder_test.xls" in body
assert "placeholder_test.csv" in body