From 45ae96ce334f8bc69576c3551d00e53c182ddc1d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:16:16 +0200 Subject: [PATCH 1/8] add id_internal and update phid incremental per user --- ...626c17026ca7_id_internal_in_placeholder.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ereuse_devicehub/migrations/versions/626c17026ca7_id_internal_in_placeholder.py diff --git a/ereuse_devicehub/migrations/versions/626c17026ca7_id_internal_in_placeholder.py b/ereuse_devicehub/migrations/versions/626c17026ca7_id_internal_in_placeholder.py new file mode 100644 index 00000000..8c5f28db --- /dev/null +++ b/ereuse_devicehub/migrations/versions/626c17026ca7_id_internal_in_placeholder.py @@ -0,0 +1,59 @@ +"""id internal in placeholder + +Revision ID: 626c17026ca7 +Revises: e919fe0611ff +Create Date: 2022-10-03 19:25:00.581699 + +""" +import sqlalchemy as sa +from alembic import context, op + +# revision identifiers, used by Alembic. +revision = '626c17026ca7' +down_revision = 'e919fe0611ff' +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_datas(): + con = op.get_bind() + sql = 'select id from common.user where phantom=false and active=true' + users = con.execute(sql) + for user in users: + phid = 1 + user_id = user.id + sql = f""" + select id from {get_inv()}.placeholder where owner_id='{user_id}' + order by id + """ + placeholders = con.execute(sql) + + for p in placeholders: + p_id = p.id + sql = f""" + update {get_inv()}.placeholder set phid='{phid}' + where id='{p_id}' + """ + con.execute(sql) + phid += 1 + + +def upgrade(): + op.add_column( + 'placeholder', + sa.Column('id_device_internal', sa.Unicode(), nullable=True), + schema=f'{get_inv()}', + ) + + upgrade_datas() + + +def downgrade(): + op.drop_column('placeholder', 'id_device_internal', schema=f'{get_inv()}') From 02f394d81e59e77ce04803c1b53be9eb9323a15d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:17:27 +0200 Subject: [PATCH 2/8] add id_internal in placeholder model --- ereuse_devicehub/resources/device/models.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 5a40cfc6..f3971c15 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -75,11 +75,15 @@ def create_code(context): 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' +def create_phid(context, count=1): + phid = str(Placeholder.query.filter(Placeholder.owner == g.user).count() + count) + if ( + Placeholder.query.filter(Placeholder.owner == g.user) + .filter(Placeholder.phid == phid) + .count() + ): + return create_phid(context, count=count + 1) + return phid class Device(Thing): @@ -901,8 +905,8 @@ class DisplayMixin: class Placeholder(Thing): id = Column(BigInteger, Sequence('placeholder_seq'), primary_key=True) + phid = Column(nullable=False, default=create_phid) pallet = Column(Unicode(), nullable=True) - phid = Column(Unicode(), nullable=False, default=create_phid) pallet.comment = "used for identification where from where is this placeholders" info = db.Column(CIText()) components = Column(CIText()) @@ -912,6 +916,8 @@ class Placeholder(Thing): id_device_supplier.comment = ( "Identification used for one supplier of one placeholders" ) + id_device_internal = db.Column(CIText()) + id_device_internal.comment = "Identification used internaly for the user" device_id = db.Column( BigInteger, From 120646289cb997be62dc5a17b31c4c96ca107e36 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:18:42 +0200 Subject: [PATCH 3/8] id internal when create a new placeholder, wbform --- ereuse_devicehub/inventory/forms.py | 54 +++++-------------- ereuse_devicehub/inventory/views.py | 2 +- ereuse_devicehub/static/js/create_device.js | 4 ++ .../templates/inventory/device_create.html | 26 ++++----- 4 files changed, 32 insertions(+), 54 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 1dc9cff2..8d5694f1 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -322,7 +322,7 @@ class NewDeviceForm(FlaskForm): default=1, ) id_device_supplier = StringField('Id Supplier', [validators.Optional()]) - phid = StringField('Placeholder Hardware identity (Phid)', [validators.Optional()]) + id_device_internal = StringField('Id Internal', [validators.Optional()]) pallet = StringField('Identity of pallet', [validators.Optional()]) components = TextAreaField('Components', [validators.Optional()]) info = TextAreaField('Info', [validators.Optional()]) @@ -382,7 +382,7 @@ class NewDeviceForm(FlaskForm): self.type.data = self._obj.type self.amount.render_kw = disabled self.id_device_supplier.data = self._obj.placeholder.id_device_supplier - self.phid.data = self._obj.placeholder.phid + self.id_device_internal.data = self._obj.placeholder.id_device_internal self.pallet.data = self._obj.placeholder.pallet self.info.data = self._obj.placeholder.info self.components.data = self._obj.placeholder.components @@ -411,7 +411,7 @@ class NewDeviceForm(FlaskForm): if self._obj.placeholder.is_abstract: self.type.render_kw = disabled self.amount.render_kw = disabled - self.id_device_supplier.render_kw = disabled + # self.id_device_supplier.render_kw = disabled self.pallet.render_kw = disabled self.info.render_kw = disabled self.components.render_kw = disabled @@ -476,41 +476,11 @@ class NewDeviceForm(FlaskForm): self.meid.errors = error is_valid = False - if self.phid.data and self.amount.data == 1 and not self._obj: - dev = Placeholder.query.filter( - Placeholder.phid == self.phid.data, Placeholder.owner == g.user - ).first() - if dev: - msg = "Error, exist one Placeholder device with this PHID" - self.phid.errors = [msg] - is_valid = False - - if ( - self.phid.data - and self._obj - and self.phid.data != self._obj.placeholder.phid - ): - dev = Placeholder.query.filter( - Placeholder.phid == self.phid.data, Device.owner == g.user - ).first() - if dev: - msg = "Error, exist one Placeholder device with this PHID" - self.phid.errors = [msg] - is_valid = False - if not is_valid: return False if self.image.data == '': self.image.data = None - if self.manufacturer.data: - self.manufacturer.data = self.manufacturer.data.lower() - if self.model.data: - self.model.data = self.model.data.lower() - if self.serial_number.data: - self.serial_number.data = self.serial_number.data.lower() - if self.part_number.data: - self.part_number.data = self.part_number.data.lower() return True @@ -579,8 +549,9 @@ class NewDeviceForm(FlaskForm): def reset_ids(self): if self.amount.data > 1: - self.phid.data = None + # self.phid.data = None self.id_device_supplier.data = None + self.id_device_internal.data = None self.serial_number.data = None self.part_number.data = None self.sku.data = None @@ -590,8 +561,9 @@ class NewDeviceForm(FlaskForm): def get_placeholder(self): self.placeholder = Placeholder( **{ - 'phid': self.phid.data or None, + # 'phid': self.phid.data or None, 'id_device_supplier': self.id_device_supplier.data, + 'id_device_internal': self.id_device_internal.data, 'info': self.info.data, 'components': self.components.data, 'pallet': self.pallet.data, @@ -601,11 +573,13 @@ class NewDeviceForm(FlaskForm): return self.placeholder def edit_device(self): - self._obj.placeholder.phid = self.phid.data or self._obj.placeholder.phid if not self._obj.placeholder.is_abstract: self._obj.placeholder.id_device_supplier = ( self.id_device_supplier.data or None ) + self._obj.placeholder.id_device_internal = ( + self.id_device_internal.data or None + ) self._obj.placeholder.info = self.info.data or None self._obj.placeholder.components = self.components.data or None self._obj.placeholder.pallet = self.pallet.data or None @@ -1567,10 +1541,10 @@ class UploadPlaceholderForm(FlaskForm): if placeholder: self.dev_update += 1 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() - device.part_number = "{}".format(data['Part Number'][i]).lower() + device.model = "{}".format(data['Model'][i]) + device.manufacturer = "{}".format(data['Manufacturer'][i]) + device.serial_number = "{}".format(data['Serial Number'][i]) + device.part_number = "{}".format(data['Part Number'][i]) placeholder.id_device_supplier = "{}".format( data['Id device Supplier'][i] ) diff --git a/ereuse_devicehub/inventory/views.py b/ereuse_devicehub/inventory/views.py index 684be46f..41a3c16b 100644 --- a/ereuse_devicehub/inventory/views.py +++ b/ereuse_devicehub/inventory/views.py @@ -545,7 +545,7 @@ class DeviceCreateView(GenericMixin): tpy = form.type.data txt = f'{amount} placeholders Device "{tpy}" created successfully.' placeholder = ( - Placeholder.query.filter_by(owner=g.user) + Placeholder.query.filter(Placeholder.owner == g.user) .order_by(Placeholder.id.desc()) .first() ) diff --git a/ereuse_devicehub/static/js/create_device.js b/ereuse_devicehub/static/js/create_device.js index 04533411..4acece0f 100644 --- a/ereuse_devicehub/static/js/create_device.js +++ b/ereuse_devicehub/static/js/create_device.js @@ -29,14 +29,18 @@ function amountInputs() { if ($("#amount").val() > 1) { $("#Phid").hide(); $("#Id_device_supplier").hide(); + $("#Id_device_internal").hide(); $("#Serial_number").hide(); + $("#Part_number").hide(); $("#Sku").hide(); $("#imei").hide(); $("#meid").hide(); } else { $("#Phid").show(); $("#Id_device_supplier").show(); + $("#Id_device_internal").show(); $("#Serial_number").show(); + $("#Part_number").show(); $("#Sku").show(); deviceInputs(); }; diff --git a/ereuse_devicehub/templates/inventory/device_create.html b/ereuse_devicehub/templates/inventory/device_create.html index fa7bd591..6acf564f 100644 --- a/ereuse_devicehub/templates/inventory/device_create.html +++ b/ereuse_devicehub/templates/inventory/device_create.html @@ -90,19 +90,6 @@ {% endif %} -
- - {{ form.phid(class_="form-control") }} - Label that you want link to this device - {% if form.phid.errors %} -

- {% for error in form.phid.errors %} - {{ error }}
- {% endfor %} -

- {% endif %} -
-
{{ form.id_device_supplier(class_="form-control") }} @@ -116,6 +103,19 @@ {% endif %}
+
+ + {{ form.id_device_internal(class_="form-control") }} + Identity of device for the internal + {% if form.id_device_internal.errors %} +

+ {% for error in form.id_device_internal.errors %} + {{ error }}
+ {% endfor %} +

+ {% endif %} +
+
{{ form.pallet(class_="form-control") }} From 926f65e291ea49ec191ce4da2f1708cb991c07f9 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:19:16 +0200 Subject: [PATCH 4/8] id internal in device details --- ereuse_devicehub/templates/inventory/device_detail.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ereuse_devicehub/templates/inventory/device_detail.html b/ereuse_devicehub/templates/inventory/device_detail.html index 8fcd628b..762f7cb1 100644 --- a/ereuse_devicehub/templates/inventory/device_detail.html +++ b/ereuse_devicehub/templates/inventory/device_detail.html @@ -98,6 +98,11 @@
{{ placeholder.phid }}
+
+
Id device internal
+
{{ placeholder.id_device_internal or '' }}
+
+
Type
{{ placeholder.device.type }}
From 77d10661b36dec1b438df417febd0f88746a1e95 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 09:49:15 +0200 Subject: [PATCH 5/8] drop phid from UploadPlaceholderForm and drop update placeholder form this form --- ereuse_devicehub/inventory/forms.py | 38 +++-------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index 8d5694f1..eac9fcfa 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -549,7 +549,6 @@ class NewDeviceForm(FlaskForm): def reset_ids(self): if self.amount.data > 1: - # self.phid.data = None self.id_device_supplier.data = None self.id_device_internal.data = None self.serial_number.data = None @@ -561,7 +560,6 @@ class NewDeviceForm(FlaskForm): def get_placeholder(self): self.placeholder = Placeholder( **{ - # 'phid': self.phid.data or None, 'id_device_supplier': self.id_device_supplier.data, 'id_device_internal': self.id_device_internal.data, 'info': self.info.data, @@ -1488,9 +1486,7 @@ class UploadPlaceholderForm(FlaskForm): else: self.source = "Excel File: {}".format(_file.filename) try: - data = ( - pd.read_excel(_file, converters={'Phid': str}).fillna('').to_dict() - ) + data = pd.read_excel(_file).fillna('').to_dict() except ValueError: txt = ["File don't have a correct format"] self.placeholder_file.errors = txt @@ -1512,12 +1508,12 @@ class UploadPlaceholderForm(FlaskForm): return False header = [ - 'Phid', 'Model', 'Manufacturer', 'Serial Number', 'Part Number', 'Id device Supplier', + 'Id device Internal', 'Pallet', 'Info', ] @@ -1531,32 +1527,7 @@ class UploadPlaceholderForm(FlaskForm): self.placeholders = [] schema = SnapshotSchema() self.path_snapshots = {} - for i in data['Phid'].keys(): - placeholder = None - data['Phid'][i] = str(data['Phid'][i]) - if data['Phid'][i]: - placeholder = Placeholder.query.filter_by(phid=data['Phid'][i]).first() - - # update one - if placeholder: - self.dev_update += 1 - device = placeholder.device - device.model = "{}".format(data['Model'][i]) - device.manufacturer = "{}".format(data['Manufacturer'][i]) - device.serial_number = "{}".format(data['Serial Number'][i]) - device.part_number = "{}".format(data['Part Number'][i]) - placeholder.id_device_supplier = "{}".format( - data['Id device Supplier'][i] - ) - placeholder.pallet = "{}".format(data['Pallet'][i]) - placeholder.info = "{}".format(data['Info'][i]) - - placeholder_log = PlaceholdersLog( - type="Update", source=self.source, placeholder=device.placeholder - ) - self.placeholders.append((device, placeholder_log)) - continue - + for i in data['Model'].keys(): # create a new one json_snapshot = { 'type': 'Snapshot', @@ -1571,8 +1542,8 @@ class UploadPlaceholderForm(FlaskForm): }, } json_placeholder = { - 'phid': data['Phid'][i] or None, 'id_device_supplier': data['Id device Supplier'][i], + 'id_device_internal': data['Id device Internal'][i], 'pallet': data['Pallet'][i], 'info': data['Info'][i], 'is_abstract': False, @@ -1609,7 +1580,6 @@ class EditPlaceholderForm(FlaskForm): serial_number = StringField('Serial Number', [validators.Optional()]) part_number = StringField('Part Number', [validators.Optional()]) id_device_supplier = StringField('Id Supplier', [validators.Optional()]) - phid = StringField('Phid', [validators.DataRequired()]) pallet = StringField('Pallet', [validators.Optional()]) info = StringField('Info', [validators.Optional()]) From 722ee1a26134eb07ba99efd10afe1584abd92b30 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 12:22:40 +0200 Subject: [PATCH 6/8] fix phid definition --- ereuse_devicehub/resources/device/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index f3971c15..67f4bb80 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -10,7 +10,6 @@ from boltons import urlutils from citext import CIText from ereuse_utils.naming import HID_CONVERSION_DOC, Naming from flask import g, request -from flask_sqlalchemy import event from more_itertools import unique_everseen from sqlalchemy import BigInteger, Boolean, Column from sqlalchemy import Enum as DBEnum @@ -905,7 +904,7 @@ class DisplayMixin: class Placeholder(Thing): id = Column(BigInteger, Sequence('placeholder_seq'), primary_key=True) - phid = Column(nullable=False, default=create_phid) + phid = Column(Unicode(), nullable=False, default=create_phid) pallet = Column(Unicode(), nullable=True) pallet.comment = "used for identification where from where is this placeholders" info = db.Column(CIText()) @@ -1599,4 +1598,5 @@ def create_code_tag(mapper, connection, device): db.session.add(tag) +# from flask_sqlalchemy import event # event.listen(Device, 'after_insert', create_code_tag, propagate=True) From 74dde2e62693604f880d85c2435c36d01dd02634 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 13:44:29 +0200 Subject: [PATCH 7/8] fix form new device --- ereuse_devicehub/inventory/forms.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ereuse_devicehub/inventory/forms.py b/ereuse_devicehub/inventory/forms.py index eac9fcfa..b1503c6e 100644 --- a/ereuse_devicehub/inventory/forms.py +++ b/ereuse_devicehub/inventory/forms.py @@ -481,6 +481,14 @@ class NewDeviceForm(FlaskForm): if self.image.data == '': self.image.data = None + if self.manufacturer.data: + self.manufacturer.data = self.manufacturer.data.lower() + if self.model.data: + self.model.data = self.model.data.lower() + if self.serial_number.data: + self.serial_number.data = self.serial_number.data.lower() + if self.part_number.data: + self.part_number.data = self.part_number.data.lower() return True From c97ef941016352aebf4cd73c94f9565f3e2e884f Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 5 Oct 2022 13:45:06 +0200 Subject: [PATCH 8/8] fix tests --- tests/files/placeholder_test.csv | 8 +-- tests/files/placeholder_test.ods | Bin 10054 -> 10251 bytes tests/files/placeholder_test.xls | Bin 6144 -> 6144 bytes tests/files/placeholder_test.xlsx | Bin 5087 -> 5083 bytes tests/test_render_2_0.py | 90 +++++++++++------------------- 5 files changed, 36 insertions(+), 62 deletions(-) diff --git a/tests/files/placeholder_test.csv b/tests/files/placeholder_test.csv index 37579398..db9a65f7 100644 --- a/tests/files/placeholder_test.csv +++ b/tests/files/placeholder_test.csv @@ -1,4 +1,4 @@ -Phid;Model;Manufacturer;Serial Number;Part 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 +Model;Manufacturer;Serial Number;Part Number;Id device Supplier;Id device Internal;Pallet;Info +Vaio;Sony;12345678;;TTT;AA;24A;Good conditions +Vaio;Sony;12345679;;TTT;BB;24A;Good conditions +Vaio;Sony;12345680;;TTT;CC;24A;Good conditions diff --git a/tests/files/placeholder_test.ods b/tests/files/placeholder_test.ods index f41c6b35b6d2394bbbbdda338f1160cfe34fe911..b882d2bdd6c016e5725ed8f12c313cbcf4b116d6 100644 GIT binary patch delta 6154 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 delta 6013 zcmb_gXEdGd(%vN4MDK#FNAxHmh+elIy(CCP@9gM2L`@K7D`8_NMDM*z5WUwex*$5y zJDS!?E+J6xk(4NpV))@@P{2n+(@M{>pBJ-PKuJ%<4H zj#Gk!S4}1i9$%We%FMk>E1Bg2p7Tg*k*W%A8cBt-A~;xZn9YT+{S+FLG`SQUGzlj5 z+6p^YwuQwTL^Y+0<~Gf?HznEc#Uh4akEw*CgI<@tbmc!=T%d3l_HsR+sSINzmzI=! z-xh5pMEa&3psi5Tj#kn*k%cWV!=V6u*b8<=MO^m5=k%J2HdFJ;h!68z9YV31e*BX* zshDYrE2oc;k-^g*S1FP!g=M_8k59fW0Q;M3kzc!{3EwtdX*bK7AagNDga1T{ggm?itmCch? zyhBrv$i!J%HF62}o#c3EEWt9LeyE2up{^2x6!_XOGF_ zQ{9YDnB7_7z(Jq>QCv32?Fwk_4-%ZN(pTiD44vF2b7vYN@%!qJ@P6%}?W;yM;pG7iYDz6qF zKmMwjZf4v)aIfKsF+bq~ZF8g|m^(N+e`-T>+G2tYsoEfvQ5{S4IIFaL)c^f%M{A9} zjm0z`bB!da(XvV|IU`^$^0n1QVQ@TbB{j>h;{g#fv@TqD~Fi|57qc04Xw!Qf|2*G-_DI|O7q%x z<_(7DwaUD0lkS%g;qKty1)vI7T5@U^i_bktJK_e0YNEJ{To7HTKuaDtOLZ~kLzBHuIBi%9&*I~C-5X;pE# zaU_cd7LjJI-3w&EOeBU}_R~5qUwM2P6JIn(FK?w!o~4|Sya^yA?B!E;SDHu>sx)H5 zrV_Pwc>Y=`>5&&g`e7;g!LC!>LJ!+Vn>!yIiuGA(GeY;+`@%(Cl3}bGdTStB?haZP2n{h2_ zEV?j{Tl0f;+KJ#gsk;J)W`j1(L{FDM8@{Sksj4Q66ueINw$3e)7Q=%H_NMQ;x>96C zk1^8Gif1IaZNOaaP173*)GC>YpHhk})Lq7{Cl=cH#HDS#Ua*QEpr%v617F6X*cjsz z-pev&H^tBbNiqr+_Zlxt%nv5>Aw`Wv=ZUNhtz176&6%Mg*){#lo#DiDfweD0cEU+( z@9$&z#GDCv0NMD#v|diz`~|jdyXLO=XJs5}jLwh+65m!6mA5ikBZMKZz}M6;FyEmi zMf`z!OMl!!?JM@!4oedGr-WiQee(?S13$v)iW543#rvws4{UxKe1t_6P(&{-EG(q& zo$psuET8Lu*Mk^8_D~k#bf6M>(5fP8uL->zJ;@i1+#6ohvL~@iE#crcVW-M z*=y@VHr+SYG|%g z*|Ul|<_~i9`)#G=PAdwtyS46Ker`VZLiKZYx>t)7?Iw@&77RrC1gStnGS&-D{5VSy^+ty=E@@BS7)w>li#W(m z=yK%|J{IVp%0BCKnJheeS9+qyt9YKy;kdb7i`u>dP|Mdh5o3YlI#?#Du|Xgn(SQ5_ zJf4sPc*|g2WkDK?1cJWna%jxo<760?2>lqd zNoEi3Cou>!y!Zq~Q)qxnf;_c6RPRccrH%tW!gWbFJ!u6G7zgyarNc@L^4pv*?umQL zd~47}2^1zY&EWS5eA641-=RpqpNYg^At@uuEeXj>LzQ_izvSN!w!W)mYa6!YFN>n^*}H zOZkCM@cU&Sc4{q!%Ryk5sa|e=0vDk4Q%1d4wZW0T3+lHB(-^B!jZvpG@~ove$=b3D}kNimap& zx#|EDCOmHuRJX;{9uSdrw!i1g+DxbUgbel&ANj%UeimmUcVu|E{Nk1z_LO136HI^Q zz6^g#0f^*$4_8zU@pflu9DH@|M~<78P|p zGd3a^CeqU7Py>~;dUDGN%^j)};*YCe#AaK;4mXwL?$Vji?NszKt(XcP*y4H@EX0IT zQ2u&nj0gL%$*D{(ym^D;YhNX&EG5gT7Oyg}!)`e;yh1Rqu?bkg#lk27#fbx^Y@y*! z^n%_BL%w;=&$y>64@lf^QFMPL4x%i2i5)CG1Yg$1U=Setvqg@u-=F*3|?o&zavaHTPtPiGTIHOqm_Id)-x9lUA=|o ztwO%0v8QjWS2|Z69TV4-0fc!lq6=r!n(G%Zt=)c1^^64hGMcry{$MuaY})QMOGSC> zHr8-+oF2ocmgGmvoG)cOwFWDW7y@uTSrIV`kDlxFAH$Uz<|;wwBNkT@ji#CQlLL7w zJA|J7duqE~m*03MC@<@GN{S_o`*dR`EU7fzY+e2JN>Mx{!^ckDf9w3@ZU<=wp3h|Dep+H6=U zxf{jXXIWpxKdrwU16p>}1z1;|i+O8bWj9uie2E#^x!jPz*AZ&2)m^N&O`UW9{s@s# znA${pep%~w?d-FG7c%V$E8*yX&ZMP&`Qqm7wk-EDknH$10JKv9r!qKuz8YmE(O$7BX`-yr#|# zNvr&lWb*8?B~*plFG><@pvkvF+!)aW^Thu6R%|%lr;~a> zR}R^wLFOUduV$;xfSIU%GcL9n|Iuu9K!1}PrVp|=hdt-^fJ>6!26`N(9uUeMU*bsh zC4858Ye!s$4ja;WOP+HJX4$zU`&#K=Ev^zt%NWCfB1TMdbjqX{dku zMRN2bbdJ>Fs@XR6^I|J%i8v(A@kIg4&FJz*ryj(XCOg!f3Y+9GciUA4TLw6zUO^M@ z^`qVs#eTn=&#t|y3dkG_r_uJ;#=BdVXzjPb7uFhv7p*C(XL0l%))7^SNHM)84fLdf zJ{@_{(D~_UMAc`*o?SMHZlQ{I96Eeh^i8p?I(bz@bObknTza**fJy2 z>s{uIO+T4)=>Q5lC5%j&>z|5OF__7Feh!yXegk>qQMJoazTKbAr;v0iw!UR!rXML% zc<2s?T90>sJW?-XR-c&3sM=coX^WuSO_3p`6flD-cuxH96=gmL`kc&UtsSeD$5oFe98Y5yS6T@??BK;|k+98jz>Km4*fa zDc$-IVqkd~*xrEcck^Mm^I|(U!x<$A>^5oSd8j z0s^9`4+S*1&MrLMac6N3!7|h$-J1{UXJUko@ha(V(jEs!@{QUCr^7{Jv z*4EaZo}Q7Bk?HB_#l^*qjg9^N{j001U%sxd<5MXqK_JX%sH~JW;4!&1vF5P&h!`}# z{WZ>GPH5v2=^9?ezuJtOUK4;d+)8WJ>@el ze{qIM-qgyOtV1d>pbY0Jm!stYgi-t2xDGqoUeAX#tS#}S{L!l@L5#hW{=jNyxsfu3 zvnN@KwsEVPKQ}6-jvyZ1oIpAQ0 zX7t5ERS_>Lzo;r%q_NKhim_DF8^Y|~lK=3G@)8c!F0Y$<3?w^L=cZ6S&3=_#uF3F; z#aF}hjBG}6s>a&M<$_AcY(4vc8KwngYhzeqea0W*9Zs|(l+uZ9mR7x%kcD{oWS&6d ztVW@coNbizU0_O;5}37G{`j*4Wthi;bE;E)L|vEAqkQnNfwdgrmTMi^Gq&Q)2?-_| z15R>-?^5Pt1Hcy*+eaN(snGGr5yeEQ(d9ulyT&&|r1_~&i`{b^ebLeaTb~4-crmAifVDLYPto3`cyr{^HtTCg zxEYZH)MK$PuqkMH(A|}=RP(7DI_r2mcckt!ea~9)W7^o^1p!M-Tisyd(jL3zZ4rZZ>OTES5~br!Wg z-wqNuF`~^+vr8H-{f-sUl4>kxS038Cx$d(D;bf~S|F;H@9VyK=zhX(bLV#Vhng+U38-gvn6shcYiKW}+gCjUkkNIA z{3O6KqI!jrKV4=L)llgVBzun)=bpg080M3|xvK89Zx0Qz&ZgAp&@t5WH|IWoqPwqG z4@iYMe6|d8j*s&S)YDWUx4-)<5A^h{ zdEYL`RM9g&8JqW+;1{Oo&B{jpG#p()RIX|GO*a|){lzMy8fU?c4Sq!FO!Ynmb zGZ;h|+1vVjG~dbH!El=7&GsrhJEAf(w3ncN#e>bUh)J!-bBKU1YNqaq3|(Wb0#pfi z-bZiBHT9fW%_4U$GTD>tPq!>HBwHtGFDE(h%{E|EZ)piziedZs!?r6kI^&A?kmAg9 zz13jWE5woW`?g^&>TLRjs>vVCfdvYW1BbVviH4FhIs%?uZ8|NYrT6i2Bepe7Eb6;? zN#(k~9wzlFk5wc#7LvPq3JGBO0()+07O6@-rxQrChkd)ostn3jBCv@k7oZl!+9Y>Chwq2{aT!dsa z(*eDPln|C4`!KsPktZ&E=H$ECar-(tr7EnOGP)aW!2_$7oM@Ar`Z%-uT{ zYII!)T!;?xUk=_u;Ew-sP;ieLy&VEqrKkA^;@80i1Ri+r-fu2NSgFzZ=-~DAO#is| z%cUb7oPhQI@0GlPWz*d_B8Ow&Bm67c89Mk39lgT8C%EF+Y8c~&i3Q$d>E9DfL4T8_ j=ML5u=B_YqmzNg*C=I;%CK_D^_$(_cX3q^h2Z8<%`Lf)_ diff --git a/tests/files/placeholder_test.xls b/tests/files/placeholder_test.xls index e9b64b1a439a772b8c4cd63587c75a65d0694e97..50b76aa3bd43db7bbfad18623a325f4dad973be9 100644 GIT binary patch delta 404 zcmY+8&q~8U5XNUWskTX)jivD*9^z3DDN>6Hiv5Eno&<}YLlPlSvZ6)Qg9gE?m$~`? z9wk+ZZ_;P*AoS=#bW%&93$ydheDh-_sU(%xTOB{OBFp2s-ePO`t|wXwO8{U|;DK>? zUjlFgCkU@1KXlx<7hc3(cMjml>vqGQN^X26DV=yxmX2k7qX2X|9S%^hliaA*1wdQM z!0}}qM7>DHmt-~?^fjBmpwY5ZLJeB!=WPlSlV_*JO~xLuW)7H*U*_)e9oK*X2LU^e zBmP4)1C=U`2yBb$c1CAyPKJO2RT;v^$le6uQ{;a#N0TPLiPb5s%+Si1ZcJ!phE^b> z1uj}orb-&d2a(EQYOSXWTg<3YbDshV_6N2~PJV#T?#pnj37LGKM?w@LZvvrA|1)p_O|;+4!RyP&*fKec zZ#yIZWIlddnZG~@Mg}GZejv@jP{#z~F#~y4tsj{tr}LXw@&ScGikN^B{0y9M9Ge#a$pqK+l;$p>|$u2?)92dU)IyL?H4aUieEFzm5gesUAFK+%R!p;l; Dq8vhR diff --git a/tests/files/placeholder_test.xlsx b/tests/files/placeholder_test.xlsx index 28420bf21a5ea5ddad030efcdf4b5ec8cd907f6a..53b0034f77ac63a822c615dc235f406a8c2ba115 100644 GIT binary patch delta 2803 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 E4{#Oy5dZ)H diff --git a/tests/test_render_2_0.py b/tests/test_render_2_0.py index 0ffb4f91..e65140b1 100644 --- a/tests/test_render_2_0.py +++ b/tests/test_render_2_0.py @@ -562,7 +562,6 @@ def test_update_monitor(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Monitor", - 'phid': '1', 'serial_number': "AAAAB", 'model': "LCD 43 b", 'manufacturer': "Samsung", @@ -575,8 +574,9 @@ def test_update_monitor(user3: UserClientFlask): } body, status = user3.post(uri, data=data) assert status == '200 OK' - assert 'Error, exist one Placeholder device with this PHID' in body - dev = Device.query.one() + # assert 'Error, exist one Placeholder device with this PHID' in body + dev = Device.query.all()[0] + assert Device.query.count() == 2 assert dev.type == 'Monitor' assert dev.placeholder.id_device_supplier == "b2" assert dev.hid == 'monitor-samsung-lc27t55-aaaab' @@ -597,7 +597,6 @@ def test_add_2_monitor(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Monitor", - 'phid': "AAB", 'serial_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", @@ -619,7 +618,7 @@ def test_add_2_monitor(user3: UserClientFlask): assert typ == 'Monitor' assert dev.placeholder.id_device_supplier == "b1" assert dev.hid == 'monitor-samsung-lc27t55-aaaab' - assert phid == 'AAB' + assert phid == '1' assert dhid == 'O48N2' assert dev.model == 'lc27t55' assert dev.placeholder.pallet == "l34" @@ -737,35 +736,6 @@ def test_add_with_ammount_laptops(user3: UserClientFlask): assert Device.query.count() == num -@pytest.mark.mvp -@pytest.mark.usefixtures(conftest.app_context.__name__) -def test_add_laptop_duplicate(user3: UserClientFlask): - - uri = '/inventory/device/add/' - body, status = user3.get(uri) - assert status == '200 OK' - assert "New Device" in body - - data = { - 'csrf_token': generate_csrf(), - 'type': "Laptop", - 'phid': 'laptop-asustek_computer_inc-1001pxd-b8oaas048285-14:da:e9:42:f6:7b', - 'serial_number': "AAAAB", - 'model': "LC27T55", - 'manufacturer': "Samsung", - 'generation': 1, - 'weight': 0.1, - 'height': 0.1, - 'depth': 0.1, - } - body, status = user3.post(uri, data=data) - assert status == '200 OK' - assert Device.query.count() == 1 - body, status = user3.post(uri, data=data) - assert 'Error, exist one Placeholder device with this PHID' in body - assert Device.query.count() == 1 - - @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) def test_filter_monitor(user3: UserClientFlask): @@ -1729,7 +1699,7 @@ def test_add_placeholder_excel(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' @@ -1755,7 +1725,7 @@ def test_add_placeholder_csv(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' @@ -1781,7 +1751,7 @@ def test_add_placeholder_ods(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' @@ -1809,10 +1779,11 @@ def test_add_placeholder_office_open_xml(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' + assert dev.placeholder.id_device_internal == 'AA' @pytest.mark.mvp @@ -1921,8 +1892,8 @@ def test_placeholder_log_manual_edit(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Laptop", - 'phid': 'ace', 'serial_number': "AAAAB", + 'part_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", 'generation': 1, @@ -1930,9 +1901,13 @@ def test_placeholder_log_manual_edit(user3: UserClientFlask): 'height': 0.1, 'depth': 0.1, 'id_device_supplier': "b2", + 'id_device_internal': "b2i", } user3.post(uri, data=data) dev = Device.query.one() + plz = Placeholder.query.first() + assert plz.id_device_supplier == "b2" + assert plz.id_device_internal == "b2i" uri = '/inventory/device/edit/{}/'.format(dev.devicehub_id) user3.get(uri) @@ -1948,16 +1923,20 @@ def test_placeholder_log_manual_edit(user3: UserClientFlask): 'height': 0.1, 'depth': 0.1, 'id_device_supplier': "a2", + 'id_device_internal': "a2i", } user3.post(uri, data=data) + plz = Placeholder.query.first() + assert plz.id_device_supplier == "a2" + assert plz.id_device_internal == "a2i" 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 "Web form" in body + assert "1" in body assert dev.devicehub_id in body assert "✓" in body assert "CSV" not in body @@ -1980,7 +1959,7 @@ def test_placeholder_log_excel_new(user3: UserClientFlask): } user3.post(uri, data=data, content_type="multipart/form-data") dev = Device.query.first() - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' uri = '/inventory/placeholder-logs/' body, status = user3.get(uri) @@ -1989,7 +1968,6 @@ def test_placeholder_log_excel_new(user3: UserClientFlask): 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 @@ -2023,7 +2001,7 @@ def test_placeholder_log_excel_update(user3: UserClientFlask): user3.post(uri, data=data, content_type="multipart/form-data") dev = Device.query.first() - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' uri = '/inventory/placeholder-logs/' body, status = user3.get(uri) @@ -2032,7 +2010,6 @@ def test_placeholder_log_excel_update(user3: UserClientFlask): 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 @@ -2070,7 +2047,7 @@ def test_add_placeholder_excel_from_lot(user3: UserClientFlask): assert Device.query.count() == 3 dev = Device.query.first() assert dev.hid == 'laptop-sony-vaio-12345678' - assert dev.placeholder.phid == 'a123' + assert dev.placeholder.phid == '1' assert dev.placeholder.info == 'Good conditions' assert dev.placeholder.pallet == '24A' assert dev.placeholder.id_device_supplier == 'TTT' @@ -2097,7 +2074,6 @@ def test_add_new_placeholder_from_lot(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Laptop", - 'phid': 'ace', 'serial_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", @@ -2110,7 +2086,7 @@ def test_add_new_placeholder_from_lot(user3: UserClientFlask): user3.post(uri, data=data) dev = Device.query.one() assert dev.hid == 'laptop-samsung-lc27t55-aaaab' - assert dev.placeholder.phid == 'ace' + assert dev.placeholder.phid == '1' assert len(lot.devices) == 1 @@ -2124,7 +2100,6 @@ def test_manual_binding(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Laptop", - 'phid': 'sid', 'serial_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", @@ -2136,7 +2111,7 @@ def test_manual_binding(user3: UserClientFlask): user3.post(uri, data=data) dev = Device.query.one() assert dev.hid == 'laptop-samsung-lc27t55-aaaab' - assert dev.placeholder.phid == 'sid' + assert dev.placeholder.phid == '1' assert dev.placeholder.is_abstract is False # add device from wb @@ -2155,7 +2130,7 @@ def test_manual_binding(user3: UserClientFlask): # page binding dhid = dev_wb.dhid - uri = f'/inventory/binding/{dhid}/sid/' + uri = f'/inventory/binding/{dhid}/1/' body, status = user3.get(uri) assert status == '200 OK' assert 'sid' in body @@ -2174,7 +2149,7 @@ def test_manual_binding(user3: UserClientFlask): assert txt in body # check new structure - assert dev_wb.binding.phid == 'sid' + assert dev_wb.binding.phid == '1' assert dev_wb.binding.device == dev assert Placeholder.query.filter_by(id=old_placeholder.id).first() is None assert Device.query.filter_by(id=old_placeholder.device.id).first() is None @@ -2237,7 +2212,6 @@ def test_unbinding(user3: UserClientFlask): data = { 'csrf_token': generate_csrf(), 'type': "Laptop", - 'phid': 'sid', 'serial_number': "AAAAB", 'model': "LC27T55", 'manufacturer': "Samsung", @@ -2262,7 +2236,7 @@ def test_unbinding(user3: UserClientFlask): # page binding dhid = dev_wb.dhid - uri = f'/inventory/binding/{dhid}/sid/' + uri = f'/inventory/binding/{dhid}/1/' user3.get(uri) # action binding @@ -2272,10 +2246,10 @@ def test_unbinding(user3: UserClientFlask): dhid = dev.dhid # action unbinding - uri = '/inventory/unbinding/sid/' + uri = '/inventory/unbinding/1/' body, status = user3.post(uri, data={}) assert status == '200 OK' - txt = f'Device with PHID:"sid" and DHID: {dhid} unbind successfully!' + txt = f'Device with PHID:"1" and DHID: {dhid} unbind successfully!' assert txt in body # assert 'Device "sid" unbind successfully!' in body @@ -2400,8 +2374,8 @@ def test_bug_3821_binding(user3: UserClientFlask): user3.post(uri, data=data) dev = Device.query.one() dhid = dev.dhid - assert dev.phid() == 'sid' - uri = f'/inventory/binding/{dhid}/sid/' + assert dev.phid() == '1' + uri = f'/inventory/binding/{dhid}/1/' body, status = user3.get(uri) assert status == '200 OK' assert 'is not a Snapshot device!' in body