resolv conflicts
This commit is contained in:
commit
68fc1cb2bb
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
"targets": {
|
||||||
|
"edge": "17",
|
||||||
|
"firefox": "60",
|
||||||
|
"chrome": "67",
|
||||||
|
"safari": "11.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
ereuse_devicehub/static/vendor
|
ereuse_devicehub/static/vendor
|
||||||
ereuse_devicehub/static/js/print.pdf.js
|
ereuse_devicehub/static/js/print.pdf.js
|
||||||
ereuse_devicehub/static/js/qrcode.js
|
ereuse_devicehub/static/js/qrcode.js
|
||||||
|
*.build.js
|
||||||
*.min.js
|
*.min.js
|
|
@ -8,6 +8,13 @@ ml).
|
||||||
## master
|
## master
|
||||||
|
|
||||||
## testing
|
## testing
|
||||||
|
|
||||||
|
## [2.1.1] - 2022-05-11
|
||||||
|
Hot fix release.
|
||||||
|
- [fixed] #256 JS support to old browsers using babel.
|
||||||
|
- [fixed] #266 Fix error when trade.document.url is None on device_list.html
|
||||||
|
|
||||||
|
## [2.1.0] - 2022-05-11
|
||||||
- [added] #219 Add functionality to searchbar (Lots and devices).
|
- [added] #219 Add functionality to searchbar (Lots and devices).
|
||||||
- [added] #222 Allow user to update its password.
|
- [added] #222 Allow user to update its password.
|
||||||
- [added] #233 Filter in out trades from lots selector.
|
- [added] #233 Filter in out trades from lots selector.
|
||||||
|
|
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
## Writing code
|
## Writing code
|
||||||
|
|
||||||
|
### Javascript and compatibility with "old" browsers
|
||||||
|
**Warning:** This project is using babel compiler... You need run an additional build step to make build js file
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run babel
|
||||||
|
```
|
||||||
|
NOTE: If you prefer you can use yarn instead, it's compatible
|
||||||
|
NOTE2: This only affect to file `ereuse_devicehub/static/js/main_inventory.js`.
|
||||||
|
|
||||||
### Coding style
|
### Coding style
|
||||||
|
|
||||||
#### Python style
|
#### Python style
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "2.1.0.dev"
|
__version__ = "2.1.2.alpha0"
|
||||||
|
|
|
@ -58,7 +58,7 @@ from ereuse_devicehub.resources.tradedocument.models import TradeDocument
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
|
|
||||||
DEVICES = {
|
DEVICES = {
|
||||||
"All": ["All Devices", "All Components"],
|
"All": ["All Devices"],
|
||||||
"Computer": [
|
"Computer": [
|
||||||
"All Computers",
|
"All Computers",
|
||||||
"Desktop",
|
"Desktop",
|
||||||
|
@ -79,55 +79,12 @@ DEVICES = {
|
||||||
"Smartphone",
|
"Smartphone",
|
||||||
"Cellphone",
|
"Cellphone",
|
||||||
],
|
],
|
||||||
"DataStorage": ["All DataStorage", "HardDrive", "SolidStateDrive"],
|
|
||||||
"Accessories & Peripherals": [
|
|
||||||
"All Peripherals",
|
|
||||||
"GraphicCard",
|
|
||||||
"Motherboard",
|
|
||||||
"NetworkAdapter",
|
|
||||||
"Processor",
|
|
||||||
"RamModule",
|
|
||||||
"SoundCard",
|
|
||||||
"Battery",
|
|
||||||
"Keyboard",
|
|
||||||
"Mouse",
|
|
||||||
"MemoryCardReader",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer']
|
COMPUTERS = ['Desktop', 'Laptop', 'Server', 'Computer']
|
||||||
|
|
||||||
COMPONENTS = [
|
|
||||||
'GraphicCard',
|
|
||||||
'DataStorage',
|
|
||||||
'HardDrive',
|
|
||||||
'DataStorage',
|
|
||||||
'SolidStateDrive',
|
|
||||||
'Motherboard',
|
|
||||||
'NetworkAdapter',
|
|
||||||
'Processor',
|
|
||||||
'RamModule',
|
|
||||||
'SoundCard',
|
|
||||||
'Display',
|
|
||||||
'Battery',
|
|
||||||
'Camera',
|
|
||||||
]
|
|
||||||
|
|
||||||
MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"]
|
MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"]
|
||||||
MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"]
|
MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"]
|
||||||
DATASTORAGE = ["HardDrive", "SolidStateDrive"]
|
|
||||||
PERIPHERALS = [
|
|
||||||
"GraphicCard",
|
|
||||||
"Motherboard",
|
|
||||||
"NetworkAdapter",
|
|
||||||
"Processor",
|
|
||||||
"RamModule",
|
|
||||||
"SoundCard",
|
|
||||||
"Battery",
|
|
||||||
"Keyboard",
|
|
||||||
"Mouse",
|
|
||||||
"MemoryCardReader",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class FilterForm(FlaskForm):
|
class FilterForm(FlaskForm):
|
||||||
|
@ -139,6 +96,7 @@ class FilterForm(FlaskForm):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.lots = lots
|
self.lots = lots
|
||||||
self.lot_id = lot_id
|
self.lot_id = lot_id
|
||||||
|
self.only_unassigned = kwargs.pop('only_unassigned', True)
|
||||||
self._get_types()
|
self._get_types()
|
||||||
|
|
||||||
def _get_types(self):
|
def _get_types(self):
|
||||||
|
@ -154,9 +112,9 @@ class FilterForm(FlaskForm):
|
||||||
device_ids = (d.id for d in self.lot.devices)
|
device_ids = (d.id for d in self.lot.devices)
|
||||||
self.devices = Device.query.filter(Device.id.in_(device_ids))
|
self.devices = Device.query.filter(Device.id.in_(device_ids))
|
||||||
else:
|
else:
|
||||||
self.devices = Device.query.filter(Device.owner_id == g.user.id).filter_by(
|
self.devices = Device.query.filter(Device.owner_id == g.user.id)
|
||||||
lots=None
|
if self.only_unassigned:
|
||||||
)
|
self.devices = self.devices.filter_by(lots=None)
|
||||||
|
|
||||||
def search(self):
|
def search(self):
|
||||||
self.filter_from_lots()
|
self.filter_from_lots()
|
||||||
|
@ -171,9 +129,6 @@ class FilterForm(FlaskForm):
|
||||||
if "All Devices" == self.device_type:
|
if "All Devices" == self.device_type:
|
||||||
filter_type = COMPUTERS + ["Monitor"] + MOBILE
|
filter_type = COMPUTERS + ["Monitor"] + MOBILE
|
||||||
|
|
||||||
elif "All Components" == self.device_type:
|
|
||||||
filter_type = COMPONENTS
|
|
||||||
|
|
||||||
elif "All Computers" == self.device_type:
|
elif "All Computers" == self.device_type:
|
||||||
filter_type = COMPUTERS
|
filter_type = COMPUTERS
|
||||||
|
|
||||||
|
@ -183,12 +138,6 @@ class FilterForm(FlaskForm):
|
||||||
elif "All Mobile" == self.device_type:
|
elif "All Mobile" == self.device_type:
|
||||||
filter_type = MOBILE
|
filter_type = MOBILE
|
||||||
|
|
||||||
elif "All DataStorage" == self.device_type:
|
|
||||||
filter_type = DATASTORAGE
|
|
||||||
|
|
||||||
elif "All Peripherals" == self.device_type:
|
|
||||||
filter_type = PERIPHERALS
|
|
||||||
|
|
||||||
if filter_type:
|
if filter_type:
|
||||||
self.devices = self.devices.filter(Device.type.in_(filter_type))
|
self.devices = self.devices.filter(Device.type.in_(filter_type))
|
||||||
|
|
||||||
|
@ -655,6 +604,7 @@ class NewActionForm(ActionFormMixin):
|
||||||
|
|
||||||
|
|
||||||
class AllocateForm(ActionFormMixin):
|
class AllocateForm(ActionFormMixin):
|
||||||
|
date = HiddenField('')
|
||||||
start_time = DateField('Start time')
|
start_time = DateField('Start time')
|
||||||
end_time = DateField('End time', [validators.Optional()])
|
end_time = DateField('End time', [validators.Optional()])
|
||||||
final_user_code = StringField(
|
final_user_code = StringField(
|
||||||
|
@ -663,7 +613,7 @@ class AllocateForm(ActionFormMixin):
|
||||||
transaction = StringField(
|
transaction = StringField(
|
||||||
'Transaction', [validators.Optional(), validators.length(max=50)]
|
'Transaction', [validators.Optional(), validators.length(max=50)]
|
||||||
)
|
)
|
||||||
end_users = IntegerField('End users', [validators.Optional()])
|
end_users = IntegerField('Number of end users', [validators.Optional()])
|
||||||
|
|
||||||
def validate(self, extra_validators=None):
|
def validate(self, extra_validators=None):
|
||||||
if not super().validate(extra_validators):
|
if not super().validate(extra_validators):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import csv
|
import csv
|
||||||
import logging
|
import logging
|
||||||
|
from distutils.util import strtobool
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
|
@ -40,60 +41,64 @@ logger = logging.getLogger(__name__)
|
||||||
class DeviceListMixin(GenericMixin):
|
class DeviceListMixin(GenericMixin):
|
||||||
template_name = 'inventory/device_list.html'
|
template_name = 'inventory/device_list.html'
|
||||||
|
|
||||||
def get_context(self, lot_id):
|
def get_context(self, lot_id, only_unassigned=True):
|
||||||
super().get_context()
|
super().get_context()
|
||||||
lots = self.context['lots']
|
lots = self.context['lots']
|
||||||
form_filter = FilterForm(lots, lot_id)
|
form_filter = FilterForm(lots, lot_id, only_unassigned=only_unassigned)
|
||||||
devices = form_filter.search()
|
devices = form_filter.search()
|
||||||
lot = None
|
lot = None
|
||||||
tags = (
|
|
||||||
Tag.query.filter(Tag.owner_id == current_user.id)
|
|
||||||
.filter(Tag.device_id.is_(None))
|
|
||||||
.order_by(Tag.id.asc())
|
|
||||||
)
|
|
||||||
|
|
||||||
if lot_id:
|
if lot_id:
|
||||||
lot = lots.filter(Lot.id == lot_id).one()
|
lot = lots.filter(Lot.id == lot_id).one()
|
||||||
form_new_action = NewActionForm(lot=lot.id)
|
|
||||||
form_new_allocate = AllocateForm(lot=lot.id)
|
|
||||||
form_new_datawipe = DataWipeForm(lot=lot.id)
|
|
||||||
form_new_trade = TradeForm(
|
form_new_trade = TradeForm(
|
||||||
lot=lot.id,
|
lot=lot.id,
|
||||||
user_to=g.user.email,
|
user_to=g.user.email,
|
||||||
user_from=g.user.email,
|
user_from=g.user.email,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
form_new_action = NewActionForm()
|
|
||||||
form_new_allocate = AllocateForm()
|
|
||||||
form_new_datawipe = DataWipeForm()
|
|
||||||
form_new_trade = ''
|
form_new_trade = ''
|
||||||
action_devices = form_new_action.devices.data
|
|
||||||
list_devices = []
|
|
||||||
if action_devices:
|
|
||||||
list_devices.extend([int(x) for x in action_devices.split(",")])
|
|
||||||
|
|
||||||
|
form_new_action = NewActionForm(lot=lot_id)
|
||||||
self.context.update(
|
self.context.update(
|
||||||
{
|
{
|
||||||
'devices': devices,
|
'devices': devices,
|
||||||
'form_tag_device': TagDeviceForm(),
|
'form_tag_device': TagDeviceForm(),
|
||||||
'form_new_action': form_new_action,
|
'form_new_action': form_new_action,
|
||||||
'form_new_allocate': form_new_allocate,
|
'form_new_allocate': AllocateForm(lot=lot_id),
|
||||||
'form_new_datawipe': form_new_datawipe,
|
'form_new_datawipe': DataWipeForm(lot=lot_id),
|
||||||
'form_new_trade': form_new_trade,
|
'form_new_trade': form_new_trade,
|
||||||
'form_filter': form_filter,
|
'form_filter': form_filter,
|
||||||
'form_print_labels': PrintLabelsForm(),
|
'form_print_labels': PrintLabelsForm(),
|
||||||
'lot': lot,
|
'lot': lot,
|
||||||
'tags': tags,
|
'tags': self.get_user_tags(),
|
||||||
'list_devices': list_devices,
|
'list_devices': self.get_selected_devices(form_new_action),
|
||||||
|
'unassigned_devices': only_unassigned,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.context
|
return self.context
|
||||||
|
|
||||||
|
def get_user_tags(self):
|
||||||
|
return (
|
||||||
|
Tag.query.filter(Tag.owner_id == current_user.id)
|
||||||
|
.filter(Tag.device_id.is_(None))
|
||||||
|
.order_by(Tag.id.asc())
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_selected_devices(self, action_form):
|
||||||
|
"""Retrieve selected devices (when action form is submited)"""
|
||||||
|
action_devices = action_form.devices.data
|
||||||
|
if action_devices:
|
||||||
|
return [int(x) for x in action_devices.split(",")]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class DeviceListView(DeviceListMixin):
|
class DeviceListView(DeviceListMixin):
|
||||||
def dispatch_request(self, lot_id=None):
|
def dispatch_request(self, lot_id=None):
|
||||||
self.get_context(lot_id)
|
only_unassigned = request.args.get(
|
||||||
|
'only_unassigned', default=True, type=strtobool
|
||||||
|
)
|
||||||
|
self.get_context(lot_id, only_unassigned)
|
||||||
return flask.render_template(self.template_name, **self.context)
|
return flask.render_template(self.template_name, **self.context)
|
||||||
|
|
||||||
|
|
||||||
|
@ -327,7 +332,8 @@ class NewAllocateView(DeviceListMixin, NewActionView):
|
||||||
messages.error('Action {} error!'.format(self.form.type.data))
|
messages.error('Action {} error!'.format(self.form.type.data))
|
||||||
for k, v in self.form.errors.items():
|
for k, v in self.form.errors.items():
|
||||||
value = ';'.join(v)
|
value = ';'.join(v)
|
||||||
messages.error('Action Error {key}: {value}!'.format(key=k, value=value))
|
key = self.form[k].label.text
|
||||||
|
messages.error('Action Error {key}: {value}!'.format(key=key, value=value))
|
||||||
next_url = self.get_next_url()
|
next_url = self.get_next_url()
|
||||||
return flask.redirect(next_url)
|
return flask.redirect(next_url)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,9 @@ class TagListView(View):
|
||||||
|
|
||||||
def dispatch_request(self):
|
def dispatch_request(self):
|
||||||
lots = Lot.query.filter(Lot.owner_id == current_user.id)
|
lots = Lot.query.filter(Lot.owner_id == current_user.id)
|
||||||
tags = Tag.query.filter(Tag.owner_id == current_user.id).order_by(Tag.id)
|
tags = Tag.query.filter(Tag.owner_id == current_user.id).order_by(
|
||||||
|
Tag.created.desc()
|
||||||
|
)
|
||||||
context = {
|
context = {
|
||||||
'lots': lots,
|
'lots': lots,
|
||||||
'tags': tags,
|
'tags': tags,
|
||||||
|
|
|
@ -332,6 +332,35 @@ class Device(Thing):
|
||||||
with suppress(LookupError, ValueError):
|
with suppress(LookupError, ValueError):
|
||||||
return self.last_action_of(*states.Trading.actions())
|
return self.last_action_of(*states.Trading.actions())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def allocated_status(self):
|
||||||
|
"""Show the actual status of device.
|
||||||
|
The status depend of one of this 3 actions:
|
||||||
|
- Allocate
|
||||||
|
- Deallocate
|
||||||
|
- InUse (Live register)
|
||||||
|
"""
|
||||||
|
from ereuse_devicehub.resources.device import states
|
||||||
|
|
||||||
|
with suppress(LookupError, ValueError):
|
||||||
|
return self.last_action_of(*states.Usage.actions())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def physical_status(self):
|
||||||
|
"""Show the actual status of device for this owner.
|
||||||
|
The status depend of one of this 4 actions:
|
||||||
|
- ToPrepare
|
||||||
|
- Prepare
|
||||||
|
- DataWipe
|
||||||
|
- ToRepair
|
||||||
|
- Ready
|
||||||
|
"""
|
||||||
|
from ereuse_devicehub.resources.device import states
|
||||||
|
|
||||||
|
with suppress(LookupError, ValueError):
|
||||||
|
# import pdb; pdb.set_trace()
|
||||||
|
return self.last_action_of(*states.Physical.actions())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
"""Show the actual status of device for this owner.
|
"""Show the actual status of device for this owner.
|
||||||
|
@ -560,6 +589,20 @@ class Device(Thing):
|
||||||
args[POLYMORPHIC_ON] = cls.type
|
args[POLYMORPHIC_ON] = cls.type
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
def is_status(self, action):
|
||||||
|
from ereuse_devicehub.resources.device import states
|
||||||
|
|
||||||
|
if action.type in states.Usage.__members__:
|
||||||
|
return "Allocate State: "
|
||||||
|
|
||||||
|
if action.type in states.Status.__members__:
|
||||||
|
return "Lifecycle State: "
|
||||||
|
|
||||||
|
if action.type in states.Physical.__members__:
|
||||||
|
return "Physical State: "
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
def set_hid(self):
|
def set_hid(self):
|
||||||
with suppress(TypeError):
|
with suppress(TypeError):
|
||||||
self.hid = Naming.hid(
|
self.hid = Naming.hid(
|
||||||
|
|
|
@ -33,6 +33,7 @@ class Trading(State):
|
||||||
:cvar ProductDisposed: The device has been removed
|
:cvar ProductDisposed: The device has been removed
|
||||||
from the facility. It does not mean end-of-life.
|
from the facility. It does not mean end-of-life.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Reserved = e.Reserve
|
Reserved = e.Reserve
|
||||||
Trade = e.Trade
|
Trade = e.Trade
|
||||||
Confirm = e.Confirm
|
Confirm = e.Confirm
|
||||||
|
@ -50,16 +51,17 @@ class Trading(State):
|
||||||
class Physical(State):
|
class Physical(State):
|
||||||
"""Physical states.
|
"""Physical states.
|
||||||
|
|
||||||
:cvar ToBeRepaired: The device has been selected for reparation.
|
:cvar Repair: The device has been repaired.
|
||||||
:cvar Repaired: The device has been repaired.
|
:cvar ToPrepare: The device is going to be or being prepared.
|
||||||
:cvar Preparing: The device is going to be or being prepared.
|
:cvar Prepare: The device has been prepared.
|
||||||
:cvar Prepared: The device has been prepared.
|
|
||||||
:cvar Ready: The device is in working conditions.
|
:cvar Ready: The device is in working conditions.
|
||||||
|
:cvar DataWipe: Do DataWipe over the device.
|
||||||
"""
|
"""
|
||||||
ToBeRepaired = e.ToRepair
|
|
||||||
Repaired = e.Repair
|
ToPrepare = e.ToPrepare
|
||||||
Preparing = e.ToPrepare
|
Prepare = e.Prepare
|
||||||
Prepared = e.Prepare
|
DataWipe = e.DataWipe
|
||||||
|
ToRepair = e.ToRepair
|
||||||
Ready = e.Ready
|
Ready = e.Ready
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,6 +70,7 @@ class Traking(State):
|
||||||
|
|
||||||
:cvar Receive: The device changes hands
|
:cvar Receive: The device changes hands
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Receive = e.Receive
|
# Receive = e.Receive
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -79,6 +82,7 @@ class Usage(State):
|
||||||
:cvar Deallocate: The device is deallocate and return to the owner
|
:cvar Deallocate: The device is deallocate and return to the owner
|
||||||
:cvar InUse: The device is being reported to be in active use.
|
:cvar InUse: The device is being reported to be in active use.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Allocate = e.Allocate
|
Allocate = e.Allocate
|
||||||
Deallocate = e.Deallocate
|
Deallocate = e.Deallocate
|
||||||
InUse = e.Live
|
InUse = e.Live
|
||||||
|
@ -91,6 +95,7 @@ class Status(State):
|
||||||
:cvar Recycling: The device is sended to recycling.
|
:cvar Recycling: The device is sended to recycling.
|
||||||
:cvar Management: The device is owned by one Manager.
|
:cvar Management: The device is owned by one Manager.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Use = e.Use
|
Use = e.Use
|
||||||
Refurbish = e.Refurbish
|
Refurbish = e.Refurbish
|
||||||
Recycling = e.Recycling
|
Recycling = e.Recycling
|
||||||
|
|
|
@ -154,6 +154,37 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<h4>Actual Status</h4>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<strong>
|
||||||
|
Lifecycle Status
|
||||||
|
</strong>
|
||||||
|
—
|
||||||
|
{% if device.status %}
|
||||||
|
{{ device.status.type }}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>
|
||||||
|
Allocate Status
|
||||||
|
</strong>
|
||||||
|
—
|
||||||
|
{% if device.allocated_status %}
|
||||||
|
{{ device.allocated_status.type }}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>
|
||||||
|
Physical Status
|
||||||
|
</strong>
|
||||||
|
—
|
||||||
|
{% if device.physical_status %}
|
||||||
|
{{ device.physical_status.type }}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<h4>Public traceability log of the device</h4>
|
<h4>Public traceability log of the device</h4>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<small>Latest one.</small>
|
<small>Latest one.</small>
|
||||||
|
@ -162,10 +193,17 @@
|
||||||
{% for action in device.public_actions %}
|
{% for action in device.public_actions %}
|
||||||
<li>
|
<li>
|
||||||
<strong>
|
<strong>
|
||||||
|
{{ device.is_status(action) }}
|
||||||
|
{% if not device.is_status(action) %}
|
||||||
{{ action.type }}
|
{{ action.type }}
|
||||||
|
{% endif %}
|
||||||
</strong>
|
</strong>
|
||||||
—
|
—
|
||||||
|
{% if device.is_status(action) %}
|
||||||
|
{{ action }} {{ action.type }}
|
||||||
|
{% else %}
|
||||||
{{ action }}
|
{{ action }}
|
||||||
|
{% endif %}
|
||||||
<br>
|
<br>
|
||||||
<div class="text-muted">
|
<div class="text-muted">
|
||||||
<small>
|
<small>
|
||||||
|
|
|
@ -70,16 +70,19 @@ class DeviceRow(OrderedDict):
|
||||||
self['Updated in (software)'] = device.updated
|
self['Updated in (software)'] = device.updated
|
||||||
self['Updated in (web)'] = ''
|
self['Updated in (web)'] = ''
|
||||||
|
|
||||||
try:
|
|
||||||
self['Physical state'] = device.last_action_of(
|
|
||||||
*states.Physical.actions()).t
|
|
||||||
except LookupError:
|
|
||||||
self['Physical state'] = ''
|
self['Physical state'] = ''
|
||||||
|
if device.physical_status:
|
||||||
|
self['Physical state'] = device.physical_status.type
|
||||||
|
|
||||||
|
self['Allocate state'] = ''
|
||||||
|
if device.allocated_status:
|
||||||
|
self['Allocate state'] = device.allocated_status.type
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self['Trading state'] = device.last_action_of(
|
self['Lifecycle state'] = device.last_action_of(
|
||||||
*states.Trading.actions()).t
|
*states.Trading.actions()).t
|
||||||
except LookupError:
|
except LookupError:
|
||||||
self['Trading state'] = ''
|
self['Lifecycle state'] = ''
|
||||||
if isinstance(device, d.Computer):
|
if isinstance(device, d.Computer):
|
||||||
self['Processor'] = none2str(device.processor_model)
|
self['Processor'] = none2str(device.processor_model)
|
||||||
self['RAM (MB)'] = none2str(device.ram_size)
|
self['RAM (MB)'] = none2str(device.ram_size)
|
||||||
|
@ -367,15 +370,18 @@ class StockRow(OrderedDict):
|
||||||
self['Model'] = none2str(device.model)
|
self['Model'] = none2str(device.model)
|
||||||
self['Manufacturer'] = none2str(device.manufacturer)
|
self['Manufacturer'] = none2str(device.manufacturer)
|
||||||
self['Registered in'] = format(device.created, '%c')
|
self['Registered in'] = format(device.created, '%c')
|
||||||
try:
|
|
||||||
self['Physical state'] = device.last_action_of(
|
|
||||||
*states.Physical.actions()).t
|
|
||||||
except LookupError:
|
|
||||||
self['Physical state'] = ''
|
self['Physical state'] = ''
|
||||||
|
if device.physical_status:
|
||||||
|
self['Physical state'] = device.physical_status.type
|
||||||
|
|
||||||
|
self['Allocate state'] = ''
|
||||||
|
if device.allocated_status:
|
||||||
|
self['Allocate state'] = device.allocated_status.type
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self['Trading state'] = device.last_action_of(*states.Trading.actions()).t
|
self['Lifecycle state'] = device.last_action_of(*states.Trading.actions()).t
|
||||||
except LookupError:
|
except LookupError:
|
||||||
self['Trading state'] = ''
|
self['Lifecycle state'] = ''
|
||||||
self['Price'] = none2str(device.price)
|
self['Price'] = none2str(device.price)
|
||||||
self['Processor'] = none2str(device.processor_model)
|
self['Processor'] = none2str(device.processor_model)
|
||||||
self['RAM (MB)'] = none2str(device.ram_size)
|
self['RAM (MB)'] = none2str(device.ram_size)
|
||||||
|
|
|
@ -0,0 +1,621 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) { _classCheckPrivateStaticAccess(receiver, classConstructor); _classCheckPrivateStaticFieldDescriptor(descriptor, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
|
||||||
|
|
||||||
|
function _classCheckPrivateStaticFieldDescriptor(descriptor, action) { if (descriptor === undefined) { throw new TypeError("attempted to " + action + " private static field before its declaration"); } }
|
||||||
|
|
||||||
|
function _classCheckPrivateStaticAccess(receiver, classConstructor) { if (receiver !== classConstructor) { throw new TypeError("Private static access of wrong provenance"); } }
|
||||||
|
|
||||||
|
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
|
||||||
|
|
||||||
|
$(document).ready(() => {
|
||||||
|
const show_allocate_form = $("#allocateModal").data("show-action-form");
|
||||||
|
const show_datawipe_form = $("#datawipeModal").data("show-action-form");
|
||||||
|
const show_trade_form = $("#tradeLotModal").data("show-action-form");
|
||||||
|
|
||||||
|
if (show_allocate_form != "None") {
|
||||||
|
$("#allocateModal .btn-primary").show();
|
||||||
|
newAllocate(show_allocate_form);
|
||||||
|
} else if (show_datawipe_form != "None") {
|
||||||
|
$("#datawipeModal .btn-primary").show();
|
||||||
|
newDataWipe(show_datawipe_form);
|
||||||
|
} else if (show_trade_form != "None") {
|
||||||
|
$("#tradeLotModal .btn-primary").show();
|
||||||
|
newTrade(show_trade_form);
|
||||||
|
} else {
|
||||||
|
$(".deviceSelect").on("change", deviceSelect);
|
||||||
|
} // $('#selectLot').selectpicker();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
class TableController {
|
||||||
|
/**
|
||||||
|
* @returns Selected inputs from device list
|
||||||
|
*/
|
||||||
|
static getSelectedDevices() {
|
||||||
|
if (_classStaticPrivateFieldSpecGet(this, TableController, _tableRows).call(this) == undefined) return [];
|
||||||
|
return _classStaticPrivateFieldSpecGet(this, TableController, _tableRows).call(this).filter(element => element.querySelector("input").checked).map(element => element.querySelector("input"));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @returns Selected inputs in current page from device list
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static getAllSelectedDevicesInCurrentPage() {
|
||||||
|
if (_classStaticPrivateFieldSpecGet(this, TableController, _tableRowsPage).call(this) == undefined) return [];
|
||||||
|
return _classStaticPrivateFieldSpecGet(this, TableController, _tableRowsPage).call(this).filter(element => element.querySelector("input").checked).map(element => element.querySelector("input"));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @returns All inputs from device list
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static getAllDevices() {
|
||||||
|
if (_classStaticPrivateFieldSpecGet(this, TableController, _tableRows).call(this) == undefined) return [];
|
||||||
|
return _classStaticPrivateFieldSpecGet(this, TableController, _tableRows).call(this).map(element => element.querySelector("input"));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @returns All inputs from current page in device list
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static getAllDevicesInCurrentPage() {
|
||||||
|
if (_classStaticPrivateFieldSpecGet(this, TableController, _tableRowsPage).call(this) == undefined) return [];
|
||||||
|
return _classStaticPrivateFieldSpecGet(this, TableController, _tableRowsPage).call(this).map(element => element.querySelector("input"));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} DOMElements
|
||||||
|
* @returns Procesed input atributes to an Object class
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static ProcessTR(DOMElements) {
|
||||||
|
return DOMElements.map(element => {
|
||||||
|
const info = {};
|
||||||
|
info.checked = element.checked;
|
||||||
|
Object.values(element.attributes).forEach(attrib => {
|
||||||
|
info[attrib.nodeName.replace(/-/g, "_")] = attrib.nodeValue;
|
||||||
|
});
|
||||||
|
return info;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Select all functionality
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
var _tableRows = {
|
||||||
|
writable: true,
|
||||||
|
value: () => table.activeRows.length > 0 ? table.activeRows : []
|
||||||
|
};
|
||||||
|
var _tableRowsPage = {
|
||||||
|
writable: true,
|
||||||
|
value: () => table.pages[table.rows().dt.currentPage - 1]
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectorController = action => {
|
||||||
|
const btnSelectAll = document.getElementById("SelectAllBTN");
|
||||||
|
const alertInfoDevices = document.getElementById("select-devices-info");
|
||||||
|
|
||||||
|
function softInit() {
|
||||||
|
TableController.getAllDevices().forEach(item => {
|
||||||
|
item.addEventListener("click", itemListCheckChanged);
|
||||||
|
}); // https://github.com/fiduswriter/Simple-DataTables/wiki/Events
|
||||||
|
|
||||||
|
table.on("datatable.page", () => itemListCheckChanged());
|
||||||
|
table.on("datatable.perpage", () => itemListCheckChanged());
|
||||||
|
table.on("datatable.update", () => itemListCheckChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == "softInit") {
|
||||||
|
softInit();
|
||||||
|
itemListCheckChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemListCheckChanged() {
|
||||||
|
alertInfoDevices.innerHTML = "Selected devices: ".concat(TableController.getSelectedDevices().length, "\n ").concat(TableController.getAllDevices().length != TableController.getSelectedDevices().length ? "<a href=\"#\" class=\"ml-3\">Select all devices (".concat(TableController.getAllDevices().length, ")</a>") : "<a href=\"#\" class=\"ml-3\">Cancel selection</a>");
|
||||||
|
|
||||||
|
if (TableController.getSelectedDevices().length <= 0) {
|
||||||
|
alertInfoDevices.classList.add("d-none");
|
||||||
|
} else {
|
||||||
|
alertInfoDevices.classList.remove("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TableController.getAllDevices().length == TableController.getSelectedDevices().length) {
|
||||||
|
btnSelectAll.checked = true;
|
||||||
|
btnSelectAll.indeterminate = false;
|
||||||
|
} else if (TableController.getAllSelectedDevicesInCurrentPage().length > 0) {
|
||||||
|
btnSelectAll.indeterminate = true;
|
||||||
|
} else {
|
||||||
|
btnSelectAll.checked = false;
|
||||||
|
btnSelectAll.indeterminate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TableController.getAllDevices().length == 0) {
|
||||||
|
btnSelectAll.checked = false;
|
||||||
|
btnSelectAll.disabled = true;
|
||||||
|
} else {
|
||||||
|
btnSelectAll.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_device_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
btnSelectAll.addEventListener("click", event => {
|
||||||
|
const checkedState = event.target.checked;
|
||||||
|
TableController.getAllDevicesInCurrentPage().forEach(ckeckbox => {
|
||||||
|
ckeckbox.checked = checkedState;
|
||||||
|
});
|
||||||
|
itemListCheckChanged();
|
||||||
|
});
|
||||||
|
alertInfoDevices.addEventListener("click", () => {
|
||||||
|
const checkState = TableController.getAllDevices().length == TableController.getSelectedDevices().length;
|
||||||
|
TableController.getAllDevices().forEach(ckeckbox => {
|
||||||
|
ckeckbox.checked = !checkState;
|
||||||
|
});
|
||||||
|
itemListCheckChanged();
|
||||||
|
});
|
||||||
|
softInit();
|
||||||
|
itemListCheckChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("DOMContentLoaded", () => selectorController());
|
||||||
|
|
||||||
|
function deviceSelect() {
|
||||||
|
const devices_count = TableController.getSelectedDevices().length;
|
||||||
|
get_device_list();
|
||||||
|
|
||||||
|
if (devices_count == 0) {
|
||||||
|
$("#addingLotModal .pol").show();
|
||||||
|
$("#addingLotModal .btn-primary").hide();
|
||||||
|
$("#removeLotModal .pol").show();
|
||||||
|
$("#removeLotModal .btn-primary").hide();
|
||||||
|
$("#addingTagModal .pol").show();
|
||||||
|
$("#addingTagModal .btn-primary").hide();
|
||||||
|
$("#actionModal .pol").show();
|
||||||
|
$("#actionModal .btn-primary").hide();
|
||||||
|
$("#allocateModal .pol").show();
|
||||||
|
$("#allocateModal .btn-primary").hide();
|
||||||
|
$("#datawipeModal .pol").show();
|
||||||
|
$("#datawipeModal .btn-primary").hide();
|
||||||
|
} else {
|
||||||
|
$("#addingLotModal .pol").hide();
|
||||||
|
$("#addingLotModal .btn-primary").show();
|
||||||
|
$("#removeLotModal .pol").hide();
|
||||||
|
$("#removeLotModal .btn-primary").show();
|
||||||
|
$("#actionModal .pol").hide();
|
||||||
|
$("#actionModal .btn-primary").show();
|
||||||
|
$("#allocateModal .pol").hide();
|
||||||
|
$("#allocateModal .btn-primary").show();
|
||||||
|
$("#datawipeModal .pol").hide();
|
||||||
|
$("#datawipeModal .btn-primary").show();
|
||||||
|
$("#addingTagModal .pol").hide();
|
||||||
|
$("#addingTagModal .btn-primary").show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeLot() {
|
||||||
|
const devices = TableController.getAllDevices();
|
||||||
|
|
||||||
|
if (devices.length > 0) {
|
||||||
|
$("#btnRemoveLots .text-danger").show();
|
||||||
|
} else {
|
||||||
|
$("#btnRemoveLots .text-danger").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#activeRemoveLotModal").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTag() {
|
||||||
|
const devices = TableController.getSelectedDevices();
|
||||||
|
const devices_id = devices.map(dev => dev.data);
|
||||||
|
|
||||||
|
if (devices_id.length == 1) {
|
||||||
|
const url = "/inventory/tag/devices/".concat(devices_id[0], "/del/");
|
||||||
|
window.location.href = url;
|
||||||
|
} else {
|
||||||
|
$("#unlinkTagAlertModal").click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTag() {
|
||||||
|
const devices = TableController.getSelectedDevices();
|
||||||
|
const devices_id = devices.map(dev => dev.data);
|
||||||
|
|
||||||
|
if (devices_id.length == 1) {
|
||||||
|
$("#addingTagModal .pol").hide();
|
||||||
|
$("#addingTagModal .btn-primary").show();
|
||||||
|
} else {
|
||||||
|
$("#addingTagModal .pol").show();
|
||||||
|
$("#addingTagModal .btn-primary").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#addTagAlertModal").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function newTrade(action) {
|
||||||
|
let title = "Trade ";
|
||||||
|
const user_to = $("#user_to").data("email");
|
||||||
|
const user_from = $("#user_from").data("email");
|
||||||
|
|
||||||
|
if (action == "user_from") {
|
||||||
|
title = "Trade Incoming";
|
||||||
|
$("#user_to").attr("readonly", "readonly");
|
||||||
|
$("#user_from").prop("readonly", false);
|
||||||
|
$("#user_from").val("");
|
||||||
|
$("#user_to").val(user_to);
|
||||||
|
} else if (action == "user_to") {
|
||||||
|
title = "Trade Outgoing";
|
||||||
|
$("#user_from").attr("readonly", "readonly");
|
||||||
|
$("#user_to").prop("readonly", false);
|
||||||
|
$("#user_to").val("");
|
||||||
|
$("#user_from").val(user_from);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#tradeLotModal #title-action").html(title);
|
||||||
|
$("#activeTradeModal").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function newAction(action) {
|
||||||
|
$("#actionModal #type").val(action);
|
||||||
|
$("#actionModal #title-action").html(action);
|
||||||
|
deviceSelect();
|
||||||
|
$("#activeActionModal").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function newAllocate(action) {
|
||||||
|
$("#allocateModal #type").val(action);
|
||||||
|
$("#allocateModal #title-action").html(action);
|
||||||
|
deviceSelect();
|
||||||
|
$("#activeAllocateModal").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function newDataWipe(action) {
|
||||||
|
$("#datawipeModal #type").val(action);
|
||||||
|
$("#datawipeModal #title-action").html(action);
|
||||||
|
deviceSelect();
|
||||||
|
$("#activeDatawipeModal").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_device_list() {
|
||||||
|
const devices = TableController.getSelectedDevices();
|
||||||
|
/* Insert the correct count of devices in actions form */
|
||||||
|
|
||||||
|
const devices_count = devices.length;
|
||||||
|
$("#datawipeModal .devices-count").html(devices_count);
|
||||||
|
$("#allocateModal .devices-count").html(devices_count);
|
||||||
|
$("#actionModal .devices-count").html(devices_count);
|
||||||
|
/* Insert the correct value in the input devicesList */
|
||||||
|
|
||||||
|
const devices_id = $.map(devices, x => $(x).attr("data")).join(",");
|
||||||
|
$.map($(".devicesList"), x => {
|
||||||
|
$(x).val(devices_id);
|
||||||
|
});
|
||||||
|
/* Create a list of devices for human representation */
|
||||||
|
|
||||||
|
const computer = {
|
||||||
|
"Desktop": "<i class='bi bi-building'></i>",
|
||||||
|
"Laptop": "<i class='bi bi-laptop'></i>"
|
||||||
|
};
|
||||||
|
const list_devices = devices.map(x => {
|
||||||
|
let typ = $(x).data("device-type");
|
||||||
|
const manuf = $(x).data("device-manufacturer");
|
||||||
|
const dhid = $(x).data("device-dhid");
|
||||||
|
|
||||||
|
if (computer[typ]) {
|
||||||
|
typ = computer[typ];
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
return "".concat(typ, " ").concat(manuf, " ").concat(dhid);
|
||||||
|
});
|
||||||
|
const description = $.map(list_devices, x => x).join(", ");
|
||||||
|
$(".enumeration-devices").html(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
function export_file(type_file) {
|
||||||
|
const devices = TableController.getSelectedDevices();
|
||||||
|
const devices_id = $.map(devices, x => $(x).attr("data-device-dhid")).join(",");
|
||||||
|
|
||||||
|
if (devices_id) {
|
||||||
|
const url = "/inventory/export/".concat(type_file, "/?ids=").concat(devices_id);
|
||||||
|
window.location.href = url;
|
||||||
|
} else {
|
||||||
|
$("#exportAlertModal").click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Reactive lots button
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
async function processSelectedDevices() {
|
||||||
|
class Actions {
|
||||||
|
constructor() {
|
||||||
|
this.list = []; // list of petitions of requests @item --> {type: ["Remove" | "Add"], "LotID": string, "devices": number[]}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Manage the actions that will be performed when applying the changes
|
||||||
|
* @param {EventSource} ev event (Should be a checkbox type)
|
||||||
|
* @param {Lot} lot lot id
|
||||||
|
* @param {Device[]} selectedDevices device id
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
manage(event, lot, selectedDevices) {
|
||||||
|
event.preventDefault();
|
||||||
|
const lotID = lot.id;
|
||||||
|
const srcElement = event.srcElement.parentElement.children[0];
|
||||||
|
const checked = !srcElement.checked;
|
||||||
|
const {
|
||||||
|
indeterminate
|
||||||
|
} = srcElement;
|
||||||
|
const found = this.list.filter(list => list.lot.id == lotID)[0];
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
if (found && found.type == "Remove") {
|
||||||
|
const affectedDevices = found.devices.filter(dev => found.lot.devices.includes(dev.id));
|
||||||
|
|
||||||
|
if (affectedDevices.length > 0 && found.indeterminate == false) {
|
||||||
|
// Remove action from list
|
||||||
|
actions.list = actions.list.filter(x => x.lot.id != found.lot.id);
|
||||||
|
} else {
|
||||||
|
found.type = "Add";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.list.push({
|
||||||
|
type: "Add",
|
||||||
|
lot,
|
||||||
|
devices: selectedDevices,
|
||||||
|
indeterminate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (found && found.type == "Add") {
|
||||||
|
const affectedDevices = found.devices.filter(dev => !found.lot.devices.includes(dev.id));
|
||||||
|
|
||||||
|
if (affectedDevices.length > 0 && found.indeterminate == false) {
|
||||||
|
// Remove action from list
|
||||||
|
actions.list = actions.list.filter(x => x.lot.id != found.lot.id);
|
||||||
|
} else {
|
||||||
|
found.type = "Remove";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.list.push({
|
||||||
|
type: "Remove",
|
||||||
|
lot,
|
||||||
|
devices: selectedDevices,
|
||||||
|
indeterminate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.list.length > 0) {
|
||||||
|
document.getElementById("ApplyDeviceLots").classList.remove("disabled");
|
||||||
|
} else {
|
||||||
|
document.getElementById("ApplyDeviceLots").classList.add("disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates notification to give feedback to user
|
||||||
|
* @param {string} title notification title
|
||||||
|
* @param {string | null} toastText notification text
|
||||||
|
* @param {boolean} isError defines if a toast is a error
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
notifyUser(title, toastText, isError) {
|
||||||
|
const toast = document.createElement("div");
|
||||||
|
toast.classList = "alert alert-dismissible fade show ".concat(isError ? "alert-danger" : "alert-success");
|
||||||
|
toast.attributes["data-autohide"] = !isError;
|
||||||
|
toast.attributes.role = "alert";
|
||||||
|
toast.style = "margin-left: auto; width: fit-content;";
|
||||||
|
toast.innerHTML = "<strong>".concat(title, "</strong><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" aria-label=\"Close\"></button>");
|
||||||
|
|
||||||
|
if (toastText && toastText.length > 0) {
|
||||||
|
toast.innerHTML += "<br>".concat(toastText);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("NotificationsContainer").appendChild(toast);
|
||||||
|
|
||||||
|
if (!isError) {
|
||||||
|
setTimeout(() => toast.classList.remove("show"), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => document.getElementById("NotificationsContainer").innerHTML == "", 3500);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get actions and execute call request to add or remove devices from lots
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
doActions() {
|
||||||
|
let requestCount = 0; // This is for count all requested api count, to perform reRender of table device list
|
||||||
|
|
||||||
|
this.list.forEach(async action => {
|
||||||
|
if (action.type == "Add") {
|
||||||
|
try {
|
||||||
|
const devicesIDs = action.devices.filter(dev => !action.lot.devices.includes(dev.id)).map(dev => dev.id);
|
||||||
|
await Api.devices_add(action.lot.id, devicesIDs);
|
||||||
|
this.notifyUser("Devices sucefully added to selected lot/s", "", false);
|
||||||
|
} catch (error) {
|
||||||
|
this.notifyUser("Failed to add devices to selected lot/s", error.responseJSON.message, true);
|
||||||
|
}
|
||||||
|
} else if (action.type == "Remove") {
|
||||||
|
try {
|
||||||
|
const devicesIDs = action.devices.filter(dev => action.lot.devices.includes(dev.id)).map(dev => dev.id);
|
||||||
|
await Api.devices_remove(action.lot.id, devicesIDs);
|
||||||
|
this.notifyUser("Devices sucefully removed from selected lot/s", "", false);
|
||||||
|
} catch (error) {
|
||||||
|
this.notifyUser("Failed to remove devices from selected lot/s", error.responseJSON.message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestCount += 1;
|
||||||
|
|
||||||
|
if (requestCount == this.list.length) {
|
||||||
|
this.reRenderTable();
|
||||||
|
this.list = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#confirmLotsModal").modal("hide"); // Hide dialog when click "Save changes"
|
||||||
|
|
||||||
|
document.getElementById("dropDownLotsSelector").classList.remove("show");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Re-render list in table
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
async reRenderTable() {
|
||||||
|
const newRequest = await Api.doRequest(window.location);
|
||||||
|
const tmpDiv = document.createElement("div");
|
||||||
|
tmpDiv.innerHTML = newRequest;
|
||||||
|
const newTable = document.createElement("table");
|
||||||
|
newTable.innerHTML = tmpDiv.querySelector("table").innerHTML;
|
||||||
|
newTable.classList = "table";
|
||||||
|
const oldTable = document.querySelector(".dataTable-wrapper");
|
||||||
|
oldTable.parentElement.replaceChild(newTable, oldTable);
|
||||||
|
table = new simpleDatatables.DataTable(newTable, {
|
||||||
|
perPage: 20
|
||||||
|
}); // // Restore state of checkbox
|
||||||
|
|
||||||
|
const selectAllBTN = document.getElementById("SelectAllBTN");
|
||||||
|
selectAllBTN.checked = false;
|
||||||
|
selectAllBTN.indeterminate = false; // Re-init SelectorController
|
||||||
|
|
||||||
|
selectorController("softInit");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let eventClickActions;
|
||||||
|
/**
|
||||||
|
* Generates a list item with a correspondient checkbox state
|
||||||
|
* @param {Object} lot Lot model server
|
||||||
|
* @param {Device[]} selectedDevices list selected devices
|
||||||
|
* @param {HTMLElement} elementTarget
|
||||||
|
* @param {Action[]} actions
|
||||||
|
*/
|
||||||
|
|
||||||
|
function templateLot(lot, selectedDevices, elementTarget, actions) {
|
||||||
|
elementTarget.innerHTML = "";
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
state
|
||||||
|
} = lot;
|
||||||
|
const htmlTemplate = "<input class=\"form-check-input\" type=\"checkbox\" id=\"".concat(id, "\" style=\"width: 20px; height: 20px; margin-right: 7px;\">\n <label class=\"form-check-label\" for=\"").concat(id, "\">").concat(name, "</label>");
|
||||||
|
const doc = document.createElement("li");
|
||||||
|
doc.innerHTML = htmlTemplate;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case "true":
|
||||||
|
doc.children[0].checked = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "false":
|
||||||
|
doc.children[0].checked = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "indetermined":
|
||||||
|
doc.children[0].indeterminate = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn("This shouldn't be happend: Lot without state: ", lot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.children[0].addEventListener("mouseup", ev => actions.manage(ev, lot, selectedDevices));
|
||||||
|
doc.children[1].addEventListener("mouseup", ev => actions.manage(ev, lot, selectedDevices));
|
||||||
|
elementTarget.append(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
const listHTML = $("#LotsSelector"); // Get selected devices
|
||||||
|
|
||||||
|
const selectedDevicesID = TableController.ProcessTR(TableController.getSelectedDevices()).map(item => item.data);
|
||||||
|
|
||||||
|
if (selectedDevicesID.length <= 0) {
|
||||||
|
listHTML.html("<li style=\"color: red; text-align: center\">No devices selected</li>");
|
||||||
|
return;
|
||||||
|
} // Initialize Actions list, and set checkbox triggers
|
||||||
|
|
||||||
|
|
||||||
|
const actions = new Actions();
|
||||||
|
|
||||||
|
if (eventClickActions) {
|
||||||
|
document.getElementById("ApplyDeviceLots").removeEventListener(eventClickActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
eventClickActions = document.getElementById("ApplyDeviceLots").addEventListener("click", () => {
|
||||||
|
const modal = $("#confirmLotsModal");
|
||||||
|
modal.modal({
|
||||||
|
keyboard: false
|
||||||
|
});
|
||||||
|
let list_changes_html = ""; // {type: ["Remove" | "Add"], "LotID": string, "devices": number[]}
|
||||||
|
|
||||||
|
actions.list.forEach(action => {
|
||||||
|
let type;
|
||||||
|
let devices;
|
||||||
|
|
||||||
|
if (action.type == "Add") {
|
||||||
|
type = "success";
|
||||||
|
devices = action.devices.filter(dev => !action.lot.devices.includes(dev.id)); // Only show affected devices
|
||||||
|
} else {
|
||||||
|
type = "danger";
|
||||||
|
devices = action.devices.filter(dev => action.lot.devices.includes(dev.id)); // Only show affected devices
|
||||||
|
}
|
||||||
|
|
||||||
|
list_changes_html += "\n <div class=\"card border-primary mb-3 w-100\">\n <div class=\"card-header\" title=\"".concat(action.lotID, "\">").concat(action.lot.name, "</div>\n <div class=\"card-body pt-3\">\n <p class=\"card-text\">\n ").concat(devices.map(item => {
|
||||||
|
const name = "".concat(item.type, " ").concat(item.manufacturer, " ").concat(item.model);
|
||||||
|
return "<span class=\"badge bg-".concat(type, "\" title=\"").concat(name, "\">").concat(item.devicehubID, "</span>");
|
||||||
|
}).join(" "), "\n </p>\n </div>\n </div>");
|
||||||
|
});
|
||||||
|
modal.find(".modal-body").html(list_changes_html);
|
||||||
|
const el = document.getElementById("SaveAllActions");
|
||||||
|
const elClone = el.cloneNode(true);
|
||||||
|
el.parentNode.replaceChild(elClone, el);
|
||||||
|
elClone.addEventListener("click", () => actions.doActions());
|
||||||
|
modal.modal("show"); // actions.doActions();
|
||||||
|
});
|
||||||
|
document.getElementById("ApplyDeviceLots").classList.add("disabled");
|
||||||
|
|
||||||
|
try {
|
||||||
|
listHTML.html("<li style=\"text-align: center\"><div class=\"spinner-border text-info\" style=\"margin: auto\" role=\"status\"></div></li>");
|
||||||
|
const selectedDevices = await Api.get_devices(selectedDevicesID);
|
||||||
|
let lots = await Api.get_lots();
|
||||||
|
lots = lots.map(lot => {
|
||||||
|
lot.devices = selectedDevices.filter(device => device.lots.filter(devicelot => devicelot.id == lot.id).length > 0).map(device => parseInt(device.id));
|
||||||
|
|
||||||
|
switch (lot.devices.length) {
|
||||||
|
case 0:
|
||||||
|
lot.state = "false";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case selectedDevicesID.length:
|
||||||
|
lot.state = "true";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
lot.state = "indetermined";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lot;
|
||||||
|
});
|
||||||
|
let lotsList = [];
|
||||||
|
lotsList.push(lots.filter(lot => lot.state == "true").sort((a, b) => a.name.localeCompare(b.name)));
|
||||||
|
lotsList.push(lots.filter(lot => lot.state == "indetermined").sort((a, b) => a.name.localeCompare(b.name)));
|
||||||
|
lotsList.push(lots.filter(lot => lot.state == "false").sort((a, b) => a.name.localeCompare(b.name)));
|
||||||
|
lotsList = lotsList.flat(); // flat array
|
||||||
|
|
||||||
|
listHTML.html("");
|
||||||
|
lotsList.forEach(lot => templateLot(lot, selectedDevices, listHTML, actions));
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
listHTML.html("<li style=\"color: red; text-align: center\">Error feching devices and lots<br>(see console for more details)</li>");
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,38 +78,61 @@ class TableController {
|
||||||
/**
|
/**
|
||||||
* Select all functionality
|
* Select all functionality
|
||||||
*/
|
*/
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
|
const selectorController = (action) => {
|
||||||
const btnSelectAll = document.getElementById("SelectAllBTN");
|
const btnSelectAll = document.getElementById("SelectAllBTN");
|
||||||
const alertInfoDevices = document.getElementById("select-devices-info");
|
const alertInfoDevices = document.getElementById("select-devices-info");
|
||||||
|
|
||||||
function itemListCheckChanged() {
|
function softInit() {
|
||||||
const listDevices = TableController.getAllDevicesInCurrentPage()
|
|
||||||
const isAllChecked = listDevices.map(itm => itm.checked);
|
|
||||||
|
|
||||||
if (isAllChecked.every(bool => bool == true)) {
|
|
||||||
btnSelectAll.checked = true;
|
|
||||||
btnSelectAll.indeterminate = false;
|
|
||||||
alertInfoDevices.innerHTML = `Selected devices: ${TableController.getSelectedDevices().length}
|
|
||||||
${
|
|
||||||
TableController.getAllDevices().length != TableController.getSelectedDevices().length
|
|
||||||
? `<a href="#" class="ml-3">Select all devices (${TableController.getAllDevices().length})</a>`
|
|
||||||
: "<a href=\"#\" class=\"ml-3\">Cancel selection</a>"
|
|
||||||
}`;
|
|
||||||
alertInfoDevices.classList.remove("d-none");
|
|
||||||
} else if (isAllChecked.every(bool => bool == false)) {
|
|
||||||
btnSelectAll.checked = false;
|
|
||||||
btnSelectAll.indeterminate = false;
|
|
||||||
alertInfoDevices.classList.add("d-none")
|
|
||||||
} else {
|
|
||||||
btnSelectAll.indeterminate = true;
|
|
||||||
alertInfoDevices.classList.add("d-none")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TableController.getAllDevices().forEach(item => {
|
TableController.getAllDevices().forEach(item => {
|
||||||
item.addEventListener("click", itemListCheckChanged);
|
item.addEventListener("click", itemListCheckChanged);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// https://github.com/fiduswriter/Simple-DataTables/wiki/Events
|
||||||
|
table.on("datatable.page", () => itemListCheckChanged());
|
||||||
|
table.on("datatable.perpage", () => itemListCheckChanged());
|
||||||
|
table.on("datatable.update", () => itemListCheckChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == "softInit") {
|
||||||
|
softInit();
|
||||||
|
itemListCheckChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemListCheckChanged() {
|
||||||
|
alertInfoDevices.innerHTML = `Selected devices: ${TableController.getSelectedDevices().length}
|
||||||
|
${TableController.getAllDevices().length != TableController.getSelectedDevices().length
|
||||||
|
? `<a href="#" class="ml-3">Select all devices (${TableController.getAllDevices().length})</a>`
|
||||||
|
: "<a href=\"#\" class=\"ml-3\">Cancel selection</a>"
|
||||||
|
}`;
|
||||||
|
|
||||||
|
if (TableController.getSelectedDevices().length <= 0) {
|
||||||
|
alertInfoDevices.classList.add("d-none")
|
||||||
|
} else {
|
||||||
|
alertInfoDevices.classList.remove("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TableController.getAllDevices().length == TableController.getSelectedDevices().length) {
|
||||||
|
btnSelectAll.checked = true;
|
||||||
|
btnSelectAll.indeterminate = false;
|
||||||
|
} else if(TableController.getAllSelectedDevicesInCurrentPage().length > 0) {
|
||||||
|
btnSelectAll.indeterminate = true;
|
||||||
|
} else {
|
||||||
|
btnSelectAll.checked = false;
|
||||||
|
btnSelectAll.indeterminate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TableController.getAllDevices().length == 0) {
|
||||||
|
btnSelectAll.checked = false;
|
||||||
|
btnSelectAll.disabled = true;
|
||||||
|
} else {
|
||||||
|
btnSelectAll.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_device_list();
|
||||||
|
}
|
||||||
|
|
||||||
btnSelectAll.addEventListener("click", event => {
|
btnSelectAll.addEventListener("click", event => {
|
||||||
const checkedState = event.target.checked;
|
const checkedState = event.target.checked;
|
||||||
TableController.getAllDevicesInCurrentPage().forEach(ckeckbox => { ckeckbox.checked = checkedState });
|
TableController.getAllDevicesInCurrentPage().forEach(ckeckbox => { ckeckbox.checked = checkedState });
|
||||||
|
@ -122,11 +145,12 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
itemListCheckChanged()
|
itemListCheckChanged()
|
||||||
})
|
})
|
||||||
|
|
||||||
// https://github.com/fiduswriter/Simple-DataTables/wiki/Events
|
softInit();
|
||||||
table.on("datatable.page", () => itemListCheckChanged());
|
|
||||||
table.on("datatable.perpage", () => itemListCheckChanged());
|
itemListCheckChanged();
|
||||||
table.on("datatable.update", () => itemListCheckChanged());
|
}
|
||||||
})
|
|
||||||
|
window.addEventListener("DOMContentLoaded", () => selectorController());
|
||||||
|
|
||||||
function deviceSelect() {
|
function deviceSelect() {
|
||||||
const devices_count = TableController.getSelectedDevices().length;
|
const devices_count = TableController.getSelectedDevices().length;
|
||||||
|
@ -268,7 +292,7 @@ function get_device_list() {
|
||||||
"Laptop": "<i class='bi bi-laptop'></i>",
|
"Laptop": "<i class='bi bi-laptop'></i>",
|
||||||
};
|
};
|
||||||
|
|
||||||
list_devices = devices.map((x) => {
|
const list_devices = devices.map((x) => {
|
||||||
let typ = $(x).data("device-type");
|
let typ = $(x).data("device-type");
|
||||||
const manuf = $(x).data("device-manufacturer");
|
const manuf = $(x).data("device-manufacturer");
|
||||||
const dhid = $(x).data("device-dhid");
|
const dhid = $(x).data("device-dhid");
|
||||||
|
@ -278,7 +302,7 @@ function get_device_list() {
|
||||||
return `${typ} ${manuf} ${dhid}`;
|
return `${typ} ${manuf} ${dhid}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
description = $.map(list_devices, (x) => x).join(", ");
|
const description = $.map(list_devices, (x) => x).join(", ");
|
||||||
$(".enumeration-devices").html(description);
|
$(".enumeration-devices").html(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,19 +339,30 @@ async function processSelectedDevices() {
|
||||||
const lotID = lot.id;
|
const lotID = lot.id;
|
||||||
const srcElement = event.srcElement.parentElement.children[0]
|
const srcElement = event.srcElement.parentElement.children[0]
|
||||||
const checked = !srcElement.checked;
|
const checked = !srcElement.checked;
|
||||||
|
const { indeterminate } = srcElement
|
||||||
|
|
||||||
const found = this.list.filter(list => list.lot.id == lotID)[0];
|
const found = this.list.filter(list => list.lot.id == lotID)[0];
|
||||||
|
|
||||||
if (checked) {
|
if (checked) {
|
||||||
if (found && found.type == "Remove") {
|
if (found && found.type == "Remove") {
|
||||||
found.type = "Add";
|
const affectedDevices = found.devices.filter(dev => found.lot.devices.includes(dev.id))
|
||||||
|
if (affectedDevices.length > 0 && found.indeterminate == false) { // Remove action from list
|
||||||
|
actions.list = actions.list.filter(x => x.lot.id != found.lot.id)
|
||||||
} else {
|
} else {
|
||||||
this.list.push({ type: "Add", lot, devices: selectedDevices });
|
found.type = "Add";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.list.push({ type: "Add", lot, devices: selectedDevices, indeterminate });
|
||||||
}
|
}
|
||||||
} else if (found && found.type == "Add") {
|
} else if (found && found.type == "Add") {
|
||||||
found.type = "Remove";
|
const affectedDevices = found.devices.filter(dev => !found.lot.devices.includes(dev.id))
|
||||||
|
if (affectedDevices.length > 0 && found.indeterminate == false) { // Remove action from list
|
||||||
|
actions.list = actions.list.filter(x => x.lot.id != found.lot.id)
|
||||||
} else {
|
} else {
|
||||||
this.list.push({ type: "Remove", lot, devices: selectedDevices });
|
found.type = "Remove";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.list.push({ type: "Remove", lot, devices: selectedDevices, indeterminate });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.list.length > 0) {
|
if (this.list.length > 0) {
|
||||||
|
@ -403,22 +438,23 @@ async function processSelectedDevices() {
|
||||||
const tmpDiv = document.createElement("div")
|
const tmpDiv = document.createElement("div")
|
||||||
tmpDiv.innerHTML = newRequest
|
tmpDiv.innerHTML = newRequest
|
||||||
|
|
||||||
const newTable = Array.from(tmpDiv.querySelectorAll("table.table > tbody > tr .deviceSelect")).map(x => x.attributes["data-device-dhid"].value)
|
|
||||||
|
|
||||||
// https://github.com/fiduswriter/Simple-DataTables/wiki/rows()#removeselect-arraynumber
|
const newTable = document.createElement("table")
|
||||||
const rowsToRemove = []
|
newTable.innerHTML = tmpDiv.querySelector("table").innerHTML
|
||||||
for (let i = 0; i < table.activeRows.length; i++) {
|
newTable.classList = "table"
|
||||||
const row = table.activeRows[i];
|
|
||||||
if (!newTable.includes(row.querySelector("input").attributes["data-device-dhid"].value)) {
|
|
||||||
rowsToRemove.push(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
table.rows().remove(rowsToRemove);
|
|
||||||
|
|
||||||
// Restore state of checkbox
|
const oldTable = document.querySelector(".dataTable-wrapper")
|
||||||
|
oldTable.parentElement.replaceChild(newTable, oldTable)
|
||||||
|
|
||||||
|
table = new simpleDatatables.DataTable(newTable, {perPage: 20})
|
||||||
|
|
||||||
|
// // Restore state of checkbox
|
||||||
const selectAllBTN = document.getElementById("SelectAllBTN");
|
const selectAllBTN = document.getElementById("SelectAllBTN");
|
||||||
selectAllBTN.checked = false;
|
selectAllBTN.checked = false;
|
||||||
selectAllBTN.indeterminate = false;
|
selectAllBTN.indeterminate = false;
|
||||||
|
|
||||||
|
// Re-init SelectorController
|
||||||
|
selectorController("softInit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,16 @@
|
||||||
<hr class="dropdown-divider">
|
<hr class="dropdown-divider">
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item d-flex align-items-center" href="{{ url_for('workbench.settings') }}">
|
||||||
|
<i class="bi bi-tools"></i>
|
||||||
|
<span>Workbench Settings</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<hr class="dropdown-divider">
|
||||||
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item d-flex align-items-center" href="https://help.usody.com/" target="_blank">
|
<a class="dropdown-item d-flex align-items-center" href="https://help.usody.com/" target="_blank">
|
||||||
<i class="bi bi-question-circle"></i>
|
<i class="bi bi-question-circle"></i>
|
||||||
|
@ -101,6 +111,15 @@
|
||||||
</a>
|
</a>
|
||||||
</li><!-- End Dashboard Nav -->
|
</li><!-- End Dashboard Nav -->
|
||||||
|
|
||||||
|
<li class="nav-heading">Devices</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link collapsed" href="{{ url_for('inventory.devicelist') }}?only_unassigned=false">
|
||||||
|
<i class="bi bi-laptop"></i>
|
||||||
|
<span>All devices</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="{{ url_for('inventory.devicelist') }}">
|
<a class="nav-link collapsed" href="{{ url_for('inventory.devicelist') }}">
|
||||||
<i class="bi-menu-button-wide"></i>
|
<i class="bi-menu-button-wide"></i>
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<button class="btn btn-primary w-100" type="submit">Login</button>
|
<button class="btn btn-primary w-100" type="submit">Login</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<p class="small mb-0">Don't have account? <a href="#TODO">Create an account</a></p>
|
<p class="small mb-0">Don't have account? <a href="#TODO" onclick="$('#exampleModal').modal('show')" data-toggle="modal" data-target="#exampleModal">Create an account</a></p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -83,4 +83,18 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main><!-- End #main -->
|
</main><!-- End #main -->
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Do you want to try USOdy tools?</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Just write an email to <a href="mali:hello@usody.com">hello@usody.com</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- End register modal -->
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
{{ field.label(class_="form-label") }}
|
{{ field.label(class_="form-label") }}
|
||||||
|
{% if field == form_new_allocate.start_time %}
|
||||||
|
<span class="text-danger">*</span>
|
||||||
|
{% endif %}
|
||||||
{{ field(class_="form-control") }}
|
{{ field(class_="form-control") }}
|
||||||
{% if field.errors %}
|
{% if field.errors %}
|
||||||
<p class="text-danger">
|
<p class="text-danger">
|
||||||
|
|
|
@ -124,16 +124,28 @@
|
||||||
<div class="tab-pane fade profile-overview" id="status">
|
<div class="tab-pane fade profile-overview" id="status">
|
||||||
<h5 class="card-title">Status Details</h5>
|
<h5 class="card-title">Status Details</h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-3 col-md-4 label">Physical States</div>
|
<div class="col-lg-3 col-md-4 label">Physical State</div>
|
||||||
<div class="col-lg-9 col-md-8">{{ device.physical or '' }}</div>
|
<div class="col-lg-9 col-md-8">
|
||||||
|
{% if device.physical_status %}
|
||||||
|
{{ device.physical_status.type }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-3 col-md-4 label">Trading States</div>
|
<div class="col-lg-3 col-md-4 label">Lifecycle State</div>
|
||||||
<div class="col-lg-9 col-md-8">{{ device.last_action_trading or ''}}</div>
|
<div class="col-lg-9 col-md-8">
|
||||||
|
{% if device.status %}
|
||||||
|
{{ device.status.type }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-3 col-md-4 label">Usage States</div>
|
<div class="col-lg-3 col-md-4 label">Allocated State</div>
|
||||||
<div class="col-lg-9 col-md-8">{{ device.usage or '' }}</div>
|
<div class="col-lg-9 col-md-8">
|
||||||
|
{% if device.allocated_status %}
|
||||||
|
{{ device.allocated_status.type }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,11 @@
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li class="breadcrumb-item"><a href="{{ url_for('inventory.devicelist')}}">Inventory</a></li>
|
<li class="breadcrumb-item"><a href="{{ url_for('inventory.devicelist')}}">Inventory</a></li>
|
||||||
{% if not lot %}
|
{% if not lot %}
|
||||||
|
{% if unassigned_devices %}
|
||||||
<li class="breadcrumb-item active">Unassigned</li>
|
<li class="breadcrumb-item active">Unassigned</li>
|
||||||
|
{% else %}
|
||||||
|
<li class="breadcrumb-item active">All devices</li>
|
||||||
|
{% endif %}
|
||||||
{% elif lot.is_temporary %}
|
{% elif lot.is_temporary %}
|
||||||
<li class="breadcrumb-item active">Temporary Lot</li>
|
<li class="breadcrumb-item active">Temporary Lot</li>
|
||||||
<li class="breadcrumb-item active">{{ lot.name }}</li>
|
<li class="breadcrumb-item active">{{ lot.name }}</li>
|
||||||
|
@ -84,7 +88,7 @@
|
||||||
</button>
|
</button>
|
||||||
<span class="d-none" id="activeTradeModal" data-bs-toggle="modal" data-bs-target="#tradeLotModal"></span>
|
<span class="d-none" id="activeTradeModal" data-bs-toggle="modal" data-bs-target="#tradeLotModal"></span>
|
||||||
<ul class="dropdown-menu" aria-labelledby="btnLots" id="dropDownLotsSelector">
|
<ul class="dropdown-menu" aria-labelledby="btnLots" id="dropDownLotsSelector">
|
||||||
<h6 class="dropdown-header">Select some devices to manage lots</h6>
|
<h6 class="dropdown-header">Select lots where to store the selected devices</h6>
|
||||||
<ul class="mx-3" id="LotsSelector"></ul>
|
<ul class="mx-3" id="LotsSelector"></ul>
|
||||||
<li><hr /></li>
|
<li><hr /></li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -213,7 +217,7 @@
|
||||||
<div class="btn-group dropdown m-1" uib-dropdown="">
|
<div class="btn-group dropdown m-1" uib-dropdown="">
|
||||||
<button id="btnUniqueID" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
<button id="btnUniqueID" type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="bi bi-tag"></i>
|
<i class="bi bi-tag"></i>
|
||||||
Unique Identifiers
|
Unique Identifiers (Tags)
|
||||||
</button>
|
</button>
|
||||||
<span class="d-none" id="unlinkTagAlertModal" data-bs-toggle="modal" data-bs-target="#unlinkTagErrorModal"></span>
|
<span class="d-none" id="unlinkTagAlertModal" data-bs-toggle="modal" data-bs-target="#unlinkTagErrorModal"></span>
|
||||||
<span class="d-none" id="addTagAlertModal" data-bs-toggle="modal" data-bs-target="#addingTagModal"></span>
|
<span class="d-none" id="addTagAlertModal" data-bs-toggle="modal" data-bs-target="#addingTagModal"></span>
|
||||||
|
@ -334,8 +338,11 @@
|
||||||
<th scope="col">Title</th>
|
<th scope="col">Title</th>
|
||||||
<th scope="col">DHID</th>
|
<th scope="col">DHID</th>
|
||||||
<th scope="col">Unique Identifiers</th>
|
<th scope="col">Unique Identifiers</th>
|
||||||
<th scope="col">Status</th>
|
<th scope="col">Lifecycle Status</th>
|
||||||
|
<th scope="col">Allocated Status</th>
|
||||||
|
<th scope="col">Physical Status</th>
|
||||||
<th scope="col" data-type="date" data-format="DD-MM-YYYY">Update</th>
|
<th scope="col" data-type="date" data-format="DD-MM-YYYY">Update</th>
|
||||||
|
<th scope="col"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -351,12 +358,19 @@
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
<a href="{{ url_for('inventory.device_details', id=dev.devicehub_id)}}">
|
||||||
{% if dev.get_type_logo() %}
|
{% if dev.get_type_logo() %}
|
||||||
<i class="{{ dev.get_type_logo() }}" title="{{ dev.type }}"></i>
|
<i class="{{ dev.get_type_logo() }}" title="{{ dev.type }}"></i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ url_for('inventory.device_details', id=dev.devicehub_id)}}">
|
|
||||||
{{ dev.verbose_name }}
|
{{ dev.verbose_name }}
|
||||||
</a>
|
</a>
|
||||||
|
{% if dev.lots | length > 0 %}
|
||||||
|
<h6 class="d-inline">
|
||||||
|
{% for lot in dev.lots %}
|
||||||
|
<span class="badge rounded-pill bg-light text-dark">{{ lot.name }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</h6>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ url_for('inventory.device_details', id=dev.devicehub_id)}}">
|
<a href="{{ url_for('inventory.device_details', id=dev.devicehub_id)}}">
|
||||||
|
@ -370,7 +384,14 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
<td>{% if dev.status %}{{ dev.status.type }}{% endif %}</td>
|
<td>{% if dev.status %}{{ dev.status.type }}{% endif %}</td>
|
||||||
|
<td>{% if dev.allocated_status %}{{ dev.allocated_status.type }}{% endif %}</td>
|
||||||
|
<td>{% if dev.physical_status %}{{ dev.physical_status.type }}{% endif %}</td>
|
||||||
<td>{{ dev.updated.strftime('%H:%M %d-%m-%Y') }}</td>
|
<td>{{ dev.updated.strftime('%H:%M %d-%m-%Y') }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ dev.public_link }}" target="_blank">
|
||||||
|
<i class="bi bi-box-arrow-up-right"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -392,7 +413,7 @@
|
||||||
{% for doc in lot.trade.documents %}
|
{% for doc in lot.trade.documents %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{% if doc.url.to_text() %}
|
{% if doc.url %}
|
||||||
<a href="{{ doc.url.to_text() }}" target="_blank">{{ doc.file_name}}</a>
|
<a href="{{ doc.url.to_text() }}" target="_blank">{{ doc.file_name}}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ doc.file_name}}
|
{{ doc.file_name}}
|
||||||
|
@ -430,9 +451,9 @@
|
||||||
|
|
||||||
<!-- Custom Code -->
|
<!-- Custom Code -->
|
||||||
<script>
|
<script>
|
||||||
const table = new simpleDatatables.DataTable("table", {
|
let table = new simpleDatatables.DataTable("table", {
|
||||||
perPage: 20
|
perPage: 20
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ url_for('static', filename='js/main_inventory.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/main_inventory.build.js') }}"></script>
|
||||||
{% endblock main %}
|
{% endblock main %}
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
<th scope="col">Type</th>
|
<th scope="col">Type</th>
|
||||||
<th scope="col">Provider</th>
|
<th scope="col">Provider</th>
|
||||||
<th scope="col">Device</th>
|
<th scope="col">Device</th>
|
||||||
|
<th scope="col">Created</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -62,6 +63,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>{{ tag.created.strftime('%H:%M %d-%m-%Y') }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
{% extends "ereuse_devicehub/base_site.html" %}
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<div class="pagetitle">
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
<nav>
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item">{{ page_title }}</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div><!-- End Page Title -->
|
||||||
|
|
||||||
|
<section class="section profile">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6">
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="pt-6 pb-2">
|
||||||
|
<h5 class="card-title text-center pb-0 fs-4">Download your settings for Workbench</h5>
|
||||||
|
<p class="text-center small">Please select one of this options</p>
|
||||||
|
<div class="row pt-3">
|
||||||
|
<div class="col-5">
|
||||||
|
<a href="{{ url_for('workbench.settings') }}?opt=register" class="btn btn-primary">Register devices</a>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<p class="small">Download the settings only for register devices.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-8">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock main %}
|
|
@ -0,0 +1,4 @@
|
||||||
|
[settings]
|
||||||
|
|
||||||
|
TOKEN = {{ token }}
|
||||||
|
URL = {{ url }}
|
|
@ -0,0 +1,76 @@
|
||||||
|
import time
|
||||||
|
|
||||||
|
import flask
|
||||||
|
from flask import Blueprint
|
||||||
|
from flask import current_app as app
|
||||||
|
from flask import g, make_response, request
|
||||||
|
from flask_login import login_required
|
||||||
|
|
||||||
|
from ereuse_devicehub import auth
|
||||||
|
from ereuse_devicehub.db import db
|
||||||
|
from ereuse_devicehub.resources.enums import SessionType
|
||||||
|
from ereuse_devicehub.resources.user.models import Session
|
||||||
|
from ereuse_devicehub.views import GenericMixView
|
||||||
|
|
||||||
|
workbench = Blueprint('workbench', __name__, url_prefix='/workbench')
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsView(GenericMixView):
|
||||||
|
decorators = [login_required]
|
||||||
|
template_name = 'workbench/settings.html'
|
||||||
|
page_title = "Workbench Settings"
|
||||||
|
|
||||||
|
def dispatch_request(self):
|
||||||
|
self.get_context()
|
||||||
|
self.context.update(
|
||||||
|
{
|
||||||
|
'page_title': self.page_title,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.opt = request.values.get('opt')
|
||||||
|
if self.opt in ['register']:
|
||||||
|
return self.download()
|
||||||
|
|
||||||
|
return flask.render_template(self.template_name, **self.context)
|
||||||
|
|
||||||
|
def download(self):
|
||||||
|
url = "https://{}/api/inventory/".format(app.config['HOST'])
|
||||||
|
self.wbContext = {
|
||||||
|
'token': self.get_token(),
|
||||||
|
'url': url,
|
||||||
|
}
|
||||||
|
options = {"register": self.register}
|
||||||
|
return options[self.opt]()
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
data = flask.render_template('workbench/wbSettings.ini', **self.wbContext)
|
||||||
|
return self.response_download(data)
|
||||||
|
|
||||||
|
def response_download(self, data):
|
||||||
|
bfile = str.encode(data)
|
||||||
|
output = make_response(bfile)
|
||||||
|
output.headers['Content-Disposition'] = 'attachment; filename=settings.ini'
|
||||||
|
output.headers['Content-type'] = 'text/plain'
|
||||||
|
return output
|
||||||
|
|
||||||
|
def get_token(self):
|
||||||
|
if not g.user.sessions:
|
||||||
|
ses = Session(user=g.user)
|
||||||
|
db.session.add(ses)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
tk = ''
|
||||||
|
now = time.time()
|
||||||
|
for s in g.user.sessions:
|
||||||
|
if s.type == SessionType.Internal and (s.expired == 0 or s.expired > now):
|
||||||
|
tk = s.token
|
||||||
|
break
|
||||||
|
|
||||||
|
assert tk != ''
|
||||||
|
|
||||||
|
token = auth.Auth.encode(tk)
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
workbench.add_url_rule('/settings/', view_func=SettingsView.as_view('settings'))
|
|
@ -11,12 +11,14 @@ from ereuse_devicehub.devicehub import Devicehub
|
||||||
from ereuse_devicehub.inventory.views import devices
|
from ereuse_devicehub.inventory.views import devices
|
||||||
from ereuse_devicehub.labels.views import labels
|
from ereuse_devicehub.labels.views import labels
|
||||||
from ereuse_devicehub.views import core
|
from ereuse_devicehub.views import core
|
||||||
|
from ereuse_devicehub.workbench.views import workbench
|
||||||
|
|
||||||
app = Devicehub(inventory=DevicehubConfig.DB_SCHEMA)
|
app = Devicehub(inventory=DevicehubConfig.DB_SCHEMA)
|
||||||
app.register_blueprint(core)
|
app.register_blueprint(core)
|
||||||
app.register_blueprint(devices)
|
app.register_blueprint(devices)
|
||||||
app.register_blueprint(labels)
|
app.register_blueprint(labels)
|
||||||
app.register_blueprint(api)
|
app.register_blueprint(api)
|
||||||
|
app.register_blueprint(workbench)
|
||||||
|
|
||||||
# configure & enable CSRF of Flask-WTF
|
# configure & enable CSRF of Flask-WTF
|
||||||
# NOTE: enable by blueprint to exclude API views
|
# NOTE: enable by blueprint to exclude API views
|
||||||
|
|
|
@ -3,12 +3,15 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Devicehub is a distributed IT Asset Management System focused in reusing devices, created under the project [eReuse.org](https://www.ereuse.org)",
|
"description": "Devicehub is a distributed IT Asset Management System focused in reusing devices, created under the project [eReuse.org](https://www.ereuse.org)",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
"browserslist": "> 0.25%, not dead",
|
||||||
"directories": {
|
"directories": {
|
||||||
"doc": "docs",
|
"doc": "docs",
|
||||||
"example": "examples",
|
"example": "examples",
|
||||||
"test": "tests"
|
"test": "tests"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.17.10",
|
||||||
|
"@babel/preset-env": "^7.17.10",
|
||||||
"eslint": "^8.13.0",
|
"eslint": "^8.13.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
@ -17,12 +20,14 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint:report": "eslint ereuse_devicehub --ext .js --output-file eslint_report.json --format json",
|
"lint:report": "eslint ereuse_devicehub --ext .js --output-file eslint_report.json --format json",
|
||||||
"lint:fix": "eslint ereuse_devicehub --ext .js --fix"
|
"lint:fix": "eslint ereuse_devicehub --ext .js --fix",
|
||||||
|
"babel": "babel ereuse_devicehub/static/js/main_inventory.js --out-file ereuse_devicehub/static/js/main_inventory.build.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.17.10",
|
||||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-react": "^7.29.4",
|
"eslint-plugin-react": "^7.29.4",
|
||||||
"eslint-plugin-react-hooks": "^4.4.0"
|
"eslint-plugin-react-hooks": "^4.4.0"
|
||||||
|
|
|
@ -26,6 +26,7 @@ from ereuse_devicehub.resources.enums import SessionType
|
||||||
from ereuse_devicehub.resources.tag import Tag
|
from ereuse_devicehub.resources.tag import Tag
|
||||||
from ereuse_devicehub.resources.user.models import Session, User
|
from ereuse_devicehub.resources.user.models import Session, User
|
||||||
from ereuse_devicehub.views import core
|
from ereuse_devicehub.views import core
|
||||||
|
from ereuse_devicehub.workbench.views import workbench
|
||||||
|
|
||||||
STARTT = datetime(year=2000, month=1, day=1, hour=1)
|
STARTT = datetime(year=2000, month=1, day=1, hour=1)
|
||||||
"""A dummy starting time to use in tests."""
|
"""A dummy starting time to use in tests."""
|
||||||
|
@ -61,6 +62,7 @@ def _app(config: TestConfig) -> Devicehub:
|
||||||
app.register_blueprint(devices)
|
app.register_blueprint(devices)
|
||||||
app.register_blueprint(labels)
|
app.register_blueprint(labels)
|
||||||
app.register_blueprint(api)
|
app.register_blueprint(api)
|
||||||
|
app.register_blueprint(workbench)
|
||||||
app.config["SQLALCHEMY_RECORD_QUERIES"] = True
|
app.config["SQLALCHEMY_RECORD_QUERIES"] = True
|
||||||
app.config['PROFILE'] = True
|
app.config['PROFILE'] = True
|
||||||
# app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30])
|
# app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30])
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,2 +1,7 @@
|
||||||
|
<<<<<<< HEAD
|
||||||
Type;Chassis;Serial Number;Model;Manufacturer;Registered in;Physical state;Trading state;Price;Processor;RAM (MB);Data Storage Size (MB)
|
Type;Chassis;Serial Number;Model;Manufacturer;Registered in;Physical state;Trading state;Price;Processor;RAM (MB);Data Storage Size (MB)
|
||||||
Desktop;Microtower;d1s;d1ml;d1mr;Tue Mar 29 18:13:05 2022;;;;p1ml;0;0
|
Desktop;Microtower;d1s;d1ml;d1mr;Tue Mar 29 18:13:05 2022;;;;p1ml;0;0
|
||||||
|
=======
|
||||||
|
Type;Chassis;Serial Number;Model;Manufacturer;Registered in;Physical state;Allocate state;Lifecycle state;Price;Processor;RAM (MB);Data Storage Size (MB);Rate;Range;Processor Rate;Processor Range;RAM Rate;RAM Range;Data Storage Rate;Data Storage Range
|
||||||
|
Desktop;Microtower;d1s;d1ml;d1mr;Mon May 16 09:34:22 2022;;;;;p1ml;0;0;1.0;Very low;1.0;Very low;1.0;Very low;1.0;Very low
|
||||||
|
>>>>>>> testing
|
||||||
|
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,273 @@
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"resolutionHeight": 600,
|
||||||
|
"model": "AUO LCD Monitor",
|
||||||
|
"manufacturer": "AUO \"AUO\"",
|
||||||
|
"size": 10.0,
|
||||||
|
"resolutionWidth": 1024,
|
||||||
|
"productionDate": "2009-01-04T00:00:00",
|
||||||
|
"refreshRate": 60,
|
||||||
|
"technology": "LCD",
|
||||||
|
"type": "Display",
|
||||||
|
"serialNumber": null,
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"model": "AR9285 Wireless Network Adapter",
|
||||||
|
"serialNumber": "74:2f:68:8b:fd:c8",
|
||||||
|
"manufacturer": "Qualcomm Atheros",
|
||||||
|
"wireless": true,
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"model": "AR8152 v2.0 Fast Ethernet",
|
||||||
|
"serialNumber": "14:da:e9:42:f6:7c",
|
||||||
|
"manufacturer": "Qualcomm Atheros",
|
||||||
|
"speed": 100,
|
||||||
|
"wireless": false,
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Processor",
|
||||||
|
"cores": 1,
|
||||||
|
"threads": 1,
|
||||||
|
"address": 64,
|
||||||
|
"model": "Intel Atom CPU N455 @ 1.66GHz",
|
||||||
|
"serialNumber": null,
|
||||||
|
"manufacturer": "Intel Corp.",
|
||||||
|
"speed": 1.667,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "BenchmarkProcessorSysbench",
|
||||||
|
"rate": 164.0803,
|
||||||
|
"elapsed": 164
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "BenchmarkProcessor",
|
||||||
|
"rate": 6666.24,
|
||||||
|
"elapsed": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "GraphicCard",
|
||||||
|
"model": "Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller",
|
||||||
|
"serialNumber": null,
|
||||||
|
"memory": 256.0,
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SoundCard",
|
||||||
|
"model": "NM10/ICH7 Family High Definition Audio Controller",
|
||||||
|
"serialNumber": null,
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SoundCard",
|
||||||
|
"model": "USB 2.0 UVC VGA WebCam",
|
||||||
|
"serialNumber": "0x0001",
|
||||||
|
"manufacturer": "Azurewave",
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "RamModule",
|
||||||
|
"format": "DIMM",
|
||||||
|
"model": null,
|
||||||
|
"size": 1024,
|
||||||
|
"interface": "DDR3",
|
||||||
|
"serialNumber": null,
|
||||||
|
"manufacturer": null,
|
||||||
|
"speed": 667.0,
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 1024.0,
|
||||||
|
"actions": [],
|
||||||
|
"format": "SODIMM",
|
||||||
|
"model": "48594D503131325336344350362D53362020",
|
||||||
|
"interface": "DDR3",
|
||||||
|
"type": "RamModule",
|
||||||
|
"manufacturer": "Hynix Semiconductor",
|
||||||
|
"serialNumber": "4F43487B",
|
||||||
|
"speed": 667.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "HardDrive",
|
||||||
|
"model": "HTS54322",
|
||||||
|
"size": 238475,
|
||||||
|
"interface": "ATA",
|
||||||
|
"serialNumber": "E2024242CV86HJ",
|
||||||
|
"manufacturer": "Hitachi",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "BenchmarkDataStorage",
|
||||||
|
"elapsed": 16,
|
||||||
|
"writeSpeed": 21.8,
|
||||||
|
"readSpeed": 66.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "TestDataStorage",
|
||||||
|
"length": "Extended",
|
||||||
|
"elapsed": 2,
|
||||||
|
"severity": "Error",
|
||||||
|
"status": "Unspecified Error. Self-test not started."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "EraseBasic",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "StepRandom",
|
||||||
|
"startTime": "2018-07-03T09:15:22.257059+00:00",
|
||||||
|
"severity": "Info",
|
||||||
|
"endTime": "2018-07-03T10:32:11.843190+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTime": "2018-07-03T09:15:22.256074+00:00",
|
||||||
|
"severity": "Info",
|
||||||
|
"endTime": "2018-07-03T10:32:11.848455+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": 160041.88569599998,
|
||||||
|
"variant": "1A01",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "EraseBasic",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "StepRandom",
|
||||||
|
"endTime": "2019-10-23T08:35:31.400587+00:00",
|
||||||
|
"severity": "Info",
|
||||||
|
"startTime": "2019-10-23T07:49:54.410830+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"endTime": "2019-10-23T08:35:31.400988+00:00",
|
||||||
|
"severity": "Error",
|
||||||
|
"startTime": "2019-10-23T07:49:54.410193+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elapsed": 22,
|
||||||
|
"writeSpeed": 17.3,
|
||||||
|
"readSpeed": 41.6,
|
||||||
|
"type": "BenchmarkDataStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Completed without error",
|
||||||
|
"reallocatedSectorCount": 0,
|
||||||
|
"currentPendingSectorCount": 0,
|
||||||
|
"assessment": true,
|
||||||
|
"severity": "Info",
|
||||||
|
"offlineUncorrectable": 0,
|
||||||
|
"lifetime": 4692,
|
||||||
|
"type": "TestDataStorage",
|
||||||
|
"length": "Short",
|
||||||
|
"elapsed": 118,
|
||||||
|
"reportedUncorrectableErrors": 1513,
|
||||||
|
"powerCycleCount": 5293
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"model": "WDC WD1600BEVT-2",
|
||||||
|
"interface": "ATA",
|
||||||
|
"type": "DataStorage",
|
||||||
|
"manufacturer": "Western Digital",
|
||||||
|
"serialNumber": "WD-WX11A80W7430"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"writeSpeed": 17.1,
|
||||||
|
"type": "BenchmarkDataStorage",
|
||||||
|
"elapsed": 22,
|
||||||
|
"readSpeed": 41.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "EraseSectors",
|
||||||
|
"startTime": "2019-08-19T16:48:19.689794+00:00",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"startTime": "2019-08-19T16:48:19.690458+00:00",
|
||||||
|
"type": "StepRandom",
|
||||||
|
"severity": "Info",
|
||||||
|
"endTime": "2019-08-19T17:34:22.930562+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startTime": "2019-08-19T17:34:22.690458+00:00",
|
||||||
|
"type": "StepZero",
|
||||||
|
"severity": "Info",
|
||||||
|
"endTime": "2019-08-19T18:34:22.930562+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"severity": "Info",
|
||||||
|
"endTime": "2019-08-19T18:34:22.930959+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"currentPendingSectorCount": 0,
|
||||||
|
"lifetime": 4673,
|
||||||
|
"elapsed": 115,
|
||||||
|
"reallocatedSectorCount": 0,
|
||||||
|
"powerCycleCount": 5231,
|
||||||
|
"status": "Completed without error",
|
||||||
|
"assessment": true,
|
||||||
|
"type": "TestDataStorage",
|
||||||
|
"severity": "Info",
|
||||||
|
"length": "Short",
|
||||||
|
"offlineUncorrectable": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"model": "WDC WD1600BEVT-2",
|
||||||
|
"manufacturer": "Western Digital",
|
||||||
|
"size": 160042.0,
|
||||||
|
"interface": "ATA",
|
||||||
|
"serialNumber": "WD-WX11A80W7430",
|
||||||
|
"type": "SolidStateDrive",
|
||||||
|
"variant": "1A01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Motherboard",
|
||||||
|
"serial": 1,
|
||||||
|
"firewire": 0,
|
||||||
|
"model": "1001PXD",
|
||||||
|
"slots": 2,
|
||||||
|
"pcmcia": 0,
|
||||||
|
"serialNumber": "Eee0123456789",
|
||||||
|
"usb": 5,
|
||||||
|
"manufacturer": "ASUSTeK Computer INC.",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "TestBios",
|
||||||
|
"accessRange": "C"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"elapsed": 4875,
|
||||||
|
"uuid": "3fd12a01-c04e-4fd8-9e64-2660c459e725",
|
||||||
|
"version": "11.0b11",
|
||||||
|
"type": "Snapshot",
|
||||||
|
"software": "Workbench",
|
||||||
|
"device": {
|
||||||
|
"type": "Laptop",
|
||||||
|
"model": "1001PXD",
|
||||||
|
"serialNumber": "B8OAAS048287",
|
||||||
|
"manufacturer": "ASUSTeK Computer INC.",
|
||||||
|
"chassis": "Netbook",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "BenchmarkRamSysbench",
|
||||||
|
"rate": 15.7188,
|
||||||
|
"elapsed": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "StressTest",
|
||||||
|
"severity": "Info",
|
||||||
|
"elapsed": 60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
{
|
||||||
|
"closed": true,
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"interface": "ATA",
|
||||||
|
"size": "160042.0 MB",
|
||||||
|
"serialNumber": "WD-WX11A80W7430",
|
||||||
|
"type": "HardDrive",
|
||||||
|
"variant": "1A01",
|
||||||
|
"model": "WDC WD1600BEVT-2",
|
||||||
|
"manufacturer": "Western Digital",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"severity": "Info",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"severity": "Info",
|
||||||
|
"endTime": "2019-09-10T11:37:07.459534+00:00",
|
||||||
|
"startTime": "2019-09-10T10:51:20.208391+00:00",
|
||||||
|
"type": "StepRandom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTime": "2019-09-10T10:51:20.207733+00:00",
|
||||||
|
"endTime": "2019-09-10T11:37:07.459940+00:00",
|
||||||
|
"type": "EraseBasic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "BenchmarkDataStorage",
|
||||||
|
"writeSpeed": "17.1 MB / second",
|
||||||
|
"readSpeed": "67.8 MB / second",
|
||||||
|
"elapsed": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"offlineUncorrectable": 0,
|
||||||
|
"lifetime": 4675,
|
||||||
|
"assessment": true,
|
||||||
|
"reallocatedSectorCount": 0,
|
||||||
|
"elapsed": 118,
|
||||||
|
"currentPendingSectorCount": 0,
|
||||||
|
"type": "TestDataStorage",
|
||||||
|
"status": "Completed without error",
|
||||||
|
"severity": "Info",
|
||||||
|
"powerCycleCount": 5238,
|
||||||
|
"length": "Short"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interface": "DDR2",
|
||||||
|
"serialNumber": "4F43487B",
|
||||||
|
"speed": "667.0 megahertz",
|
||||||
|
"type": "RamModule",
|
||||||
|
"size": "1024.0 mebibyte",
|
||||||
|
"model": "48594D503131325336344350362D53362020",
|
||||||
|
"format": "SODIMM",
|
||||||
|
"manufacturer": "Hynix Semiconductor",
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": 64,
|
||||||
|
"serialNumber": null,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "BenchmarkProcessor",
|
||||||
|
"rate": 6650.38,
|
||||||
|
"elapsed": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "BenchmarkProcessorSysbench",
|
||||||
|
"rate": 164.4318,
|
||||||
|
"elapsed": 164
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"speed": "1.0 gigahertz",
|
||||||
|
"type": "Processor",
|
||||||
|
"brand": "Atom",
|
||||||
|
"generation": null,
|
||||||
|
"cores": 1,
|
||||||
|
"manufacturer": "Intel Corp.",
|
||||||
|
"model": "Intel Atom CPU N450 @ 1.66GHz",
|
||||||
|
"threads": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"technology": "LCD",
|
||||||
|
"refreshRate": "60 hertz",
|
||||||
|
"serialNumber": null,
|
||||||
|
"size": "10.0 inch",
|
||||||
|
"resolutionWidth": 1024,
|
||||||
|
"type": "Display",
|
||||||
|
"productionDate": "2009-01-04T00:00:00",
|
||||||
|
"model": "AUO LCD Monitor",
|
||||||
|
"manufacturer": "AUO \"AUO\"",
|
||||||
|
"actions": [],
|
||||||
|
"resolutionHeight": 600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wireless": false,
|
||||||
|
"serialNumber": "88:ae:1d:a6:f3:d0",
|
||||||
|
"speed": "100.0 megabit / second",
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"variant": "c1",
|
||||||
|
"model": "AR8152 v1.1 Fast Ethernet",
|
||||||
|
"manufacturer": "Qualcomm Atheros",
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"wireless": true,
|
||||||
|
"serialNumber": "00:26:c7:8e:cb:8c",
|
||||||
|
"speed": null,
|
||||||
|
"type": "NetworkAdapter",
|
||||||
|
"variant": "00",
|
||||||
|
"model": "Centrino Wireless-N 1000 Condor Peak",
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"technology": "LiIon",
|
||||||
|
"serialNumber": null,
|
||||||
|
"type": "Battery",
|
||||||
|
"size": "2200 hour * milliampere",
|
||||||
|
"model": "AL10A31",
|
||||||
|
"manufacturer": "SANYO",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"severity": "Info",
|
||||||
|
"voltage": "12613.0 millivolt",
|
||||||
|
"size": "662.0 hour * milliampere",
|
||||||
|
"cycleCount": null,
|
||||||
|
"type": "MeasureBattery"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"memory": null,
|
||||||
|
"serialNumber": null,
|
||||||
|
"type": "GraphicCard",
|
||||||
|
"model": "Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller",
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SoundCard",
|
||||||
|
"model": "NM10/ICH7 Family High Definition Audio Controller",
|
||||||
|
"manufacturer": "Intel Corporation",
|
||||||
|
"serialNumber": null,
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "SoundCard",
|
||||||
|
"model": "1.3M WebCam",
|
||||||
|
"manufacturer": "XPA970VW0",
|
||||||
|
"serialNumber": null,
|
||||||
|
"actions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"manufacturer": "Acer",
|
||||||
|
"usb": 5,
|
||||||
|
"type": "Motherboard",
|
||||||
|
"model": "AOHAPPY",
|
||||||
|
"ramMaxSize": 4,
|
||||||
|
"ramSlots": 2,
|
||||||
|
"serialNumber": "Base Board Serial Number",
|
||||||
|
"pcmcia": 0,
|
||||||
|
"slots": 1,
|
||||||
|
"firewire": 0,
|
||||||
|
"serial": 1,
|
||||||
|
"version": "V3.05(DDR2)",
|
||||||
|
"biosDate": "2010-08-12T00:00:00",
|
||||||
|
"actions": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"uuid": "9c169711-3d72-4e6c-aabf-de9b3af56b54",
|
||||||
|
"device": {
|
||||||
|
"serialNumber": "LUSEA0D010038879A01601",
|
||||||
|
"type": "Laptop",
|
||||||
|
"sku": null,
|
||||||
|
"model": "AOHAPPY",
|
||||||
|
"chassis": "Netbook",
|
||||||
|
"manufacturer": "Acer",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "BenchmarkRamSysbench",
|
||||||
|
"rate": 19.2586,
|
||||||
|
"elapsed": 19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"severity": "Info",
|
||||||
|
"elapsed": 60,
|
||||||
|
"type": "StressTest"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "V3.05"
|
||||||
|
},
|
||||||
|
"type": "Snapshot",
|
||||||
|
"endTime": "2019-09-10T10:44:41.324414+00:00",
|
||||||
|
"elapsed": 3146,
|
||||||
|
"software": "Workbench",
|
||||||
|
"version": "11.0b9"
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -19,7 +19,7 @@ def test_dependencies():
|
||||||
# Simplejson has a different signature than stdlib json
|
# Simplejson has a different signature than stdlib json
|
||||||
# should be fixed though
|
# should be fixed though
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
import simplejson
|
import simplejson # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyArgumentList
|
# noinspection PyArgumentList
|
||||||
|
@ -88,6 +88,7 @@ def test_api_docs(client: Client):
|
||||||
'/users/login/',
|
'/users/login/',
|
||||||
'/users/logout/',
|
'/users/logout/',
|
||||||
'/versions/',
|
'/versions/',
|
||||||
|
'/workbench/settings/',
|
||||||
}
|
}
|
||||||
assert docs['info'] == {'title': 'Devicehub', 'version': '0.2'}
|
assert docs['info'] == {'title': 'Devicehub', 'version': '0.2'}
|
||||||
assert docs['components']['securitySchemes']['bearerAuth'] == {
|
assert docs['components']['securitySchemes']['bearerAuth'] == {
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
import csv
|
import csv
|
||||||
import hashlib
|
import hashlib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from io import StringIO, BytesIO
|
from io import BytesIO, StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from flask import url_for
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from werkzeug.exceptions import Unauthorized
|
|
||||||
import teal.marshmallow
|
import teal.marshmallow
|
||||||
from ereuse_utils.test import ANY
|
from ereuse_utils.test import ANY
|
||||||
|
from flask import url_for
|
||||||
|
from werkzeug.exceptions import Unauthorized
|
||||||
|
|
||||||
from ereuse_devicehub import auth
|
from ereuse_devicehub import auth
|
||||||
from ereuse_devicehub.client import Client, UserClient
|
from ereuse_devicehub.client import Client, UserClient
|
||||||
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.devicehub import Devicehub
|
from ereuse_devicehub.devicehub import Devicehub
|
||||||
from ereuse_devicehub.resources.user.models import Session
|
from ereuse_devicehub.resources.action.models import Allocate, Live, Snapshot
|
||||||
from ereuse_devicehub.resources.action.models import Snapshot, Allocate, Live
|
|
||||||
from ereuse_devicehub.resources.documents import documents
|
|
||||||
from ereuse_devicehub.resources.tradedocument.models import TradeDocument
|
|
||||||
from ereuse_devicehub.resources.device import models as d
|
from ereuse_devicehub.resources.device import models as d
|
||||||
|
from ereuse_devicehub.resources.documents import documents
|
||||||
|
from ereuse_devicehub.resources.enums import SessionType
|
||||||
|
from ereuse_devicehub.resources.hash_reports import ReportHash
|
||||||
from ereuse_devicehub.resources.lot.models import Lot
|
from ereuse_devicehub.resources.lot.models import Lot
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
from ereuse_devicehub.resources.hash_reports import ReportHash
|
from ereuse_devicehub.resources.tradedocument.models import TradeDocument
|
||||||
from ereuse_devicehub.resources.enums import SessionType
|
from ereuse_devicehub.resources.user.models import Session
|
||||||
from ereuse_devicehub.db import db
|
|
||||||
from tests import conftest
|
from tests import conftest
|
||||||
from tests.conftest import file, yaml2json, json_encode
|
from tests.conftest import file, json_encode, yaml2json
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -33,27 +33,30 @@ def test_erasure_certificate_public_one(user: UserClient, client: Client):
|
||||||
s = file('erase-sectors.snapshot')
|
s = file('erase-sectors.snapshot')
|
||||||
snapshot, _ = user.post(s, res=Snapshot)
|
snapshot, _ = user.post(s, res=Snapshot)
|
||||||
|
|
||||||
doc, response = user.get(res=documents.DocumentDef.t,
|
doc, response = user.get(
|
||||||
item='erasures/{}'.format(
|
res=documents.DocumentDef.t,
|
||||||
snapshot['device']['id']),
|
item='erasures/{}'.format(snapshot['device']['id']),
|
||||||
accept=ANY)
|
accept=ANY,
|
||||||
|
)
|
||||||
assert 'html' in response.content_type
|
assert 'html' in response.content_type
|
||||||
assert '<html' in doc
|
assert '<html' in doc
|
||||||
assert '2018' in doc
|
assert '2018' in doc
|
||||||
|
|
||||||
doc, response = client.get(res=documents.DocumentDef.t,
|
doc, response = client.get(
|
||||||
item='erasures/{}'.format(
|
res=documents.DocumentDef.t,
|
||||||
snapshot['device']['id']),
|
item='erasures/{}'.format(snapshot['device']['id']),
|
||||||
query=[('format', 'PDF')],
|
query=[('format', 'PDF')],
|
||||||
accept='application/pdf')
|
accept='application/pdf',
|
||||||
|
)
|
||||||
assert 'application/pdf' == response.content_type
|
assert 'application/pdf' == response.content_type
|
||||||
|
|
||||||
erasure = next(e for e in snapshot['actions']
|
erasure = next(e for e in snapshot['actions'] if e['type'] == 'EraseSectors')
|
||||||
if e['type'] == 'EraseSectors')
|
|
||||||
|
|
||||||
doc, response = client.get(res=documents.DocumentDef.t,
|
doc, response = client.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='erasures/{}'.format(erasure['id']),
|
item='erasures/{}'.format(erasure['id']),
|
||||||
accept=ANY)
|
accept=ANY,
|
||||||
|
)
|
||||||
assert 'html' in response.content_type
|
assert 'html' in response.content_type
|
||||||
assert '<html' in doc
|
assert '<html' in doc
|
||||||
assert '2018' in doc
|
assert '2018' in doc
|
||||||
|
@ -67,30 +70,32 @@ def test_erasure_certificate_private_query(user: UserClient):
|
||||||
s = file('erase-sectors.snapshot')
|
s = file('erase-sectors.snapshot')
|
||||||
snapshot, response = user.post(s, res=Snapshot)
|
snapshot, response = user.post(s, res=Snapshot)
|
||||||
|
|
||||||
doc, response = user.get(res=documents.DocumentDef.t,
|
doc, response = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='erasures/',
|
item='erasures/',
|
||||||
query=[
|
query=[('filter', {'ids': [snapshot['device']['id']]})],
|
||||||
('filter', {'ids': [snapshot['device']['id']]})],
|
accept=ANY,
|
||||||
accept=ANY)
|
)
|
||||||
assert 'html' in response.content_type
|
assert 'html' in response.content_type
|
||||||
assert '<html' in doc
|
assert '<html' in doc
|
||||||
assert '2018' in doc
|
assert '2018' in doc
|
||||||
|
|
||||||
doc, response = user.get(res=documents.DocumentDef.t,
|
doc, response = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='erasures/',
|
item='erasures/',
|
||||||
query=[
|
query=[('filter', {'ids': [snapshot['device']['id']]}), ('format', 'PDF')],
|
||||||
('filter', {
|
accept='application/pdf',
|
||||||
'ids': [snapshot['device']['id']]}),
|
)
|
||||||
('format', 'PDF')
|
|
||||||
],
|
|
||||||
accept='application/pdf')
|
|
||||||
assert 'application/pdf' == response.content_type
|
assert 'application/pdf' == response.content_type
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
def test_erasure_certificate_wrong_id(client: Client):
|
def test_erasure_certificate_wrong_id(client: Client):
|
||||||
client.get(res=documents.DocumentDef.t, item='erasures/this-is-not-an-id',
|
client.get(
|
||||||
status=teal.marshmallow.ValidationError)
|
res=documents.DocumentDef.t,
|
||||||
|
item='erasures/this-is-not-an-id',
|
||||||
|
status=teal.marshmallow.ValidationError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -98,20 +103,27 @@ def test_export_csv_permitions(user: UserClient, user2: UserClient, client: Clie
|
||||||
"""test export device information in a csv file with others users."""
|
"""test export device information in a csv file with others users."""
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
dev_id = snapshot['device']['id']
|
dev_id = snapshot['device']['id']
|
||||||
csv_user, _ = user.get(res=documents.DocumentDef.t,
|
csv_user, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})])
|
query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})],
|
||||||
|
)
|
||||||
|
|
||||||
csv_user2, _ = user2.get(res=documents.DocumentDef.t,
|
csv_user2, _ = user2.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})])
|
query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})],
|
||||||
|
)
|
||||||
|
|
||||||
_, res = client.get(res=documents.DocumentDef.t,
|
_, res = client.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})], status=401)
|
query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})],
|
||||||
|
status=401,
|
||||||
|
)
|
||||||
assert res.status_code == 401
|
assert res.status_code == 401
|
||||||
|
|
||||||
assert len(csv_user) > 0
|
assert len(csv_user) > 0
|
||||||
|
@ -124,36 +136,46 @@ def test_export_csv_actions(user: UserClient, user2: UserClient, client: Client)
|
||||||
acer = yaml2json('acer.happy.battery.snapshot')
|
acer = yaml2json('acer.happy.battery.snapshot')
|
||||||
snapshot, _ = user.post(json_encode(acer), res=Snapshot)
|
snapshot, _ = user.post(json_encode(acer), res=Snapshot)
|
||||||
device_id = snapshot['device']['id']
|
device_id = snapshot['device']['id']
|
||||||
post_request = {"transaction": "ccc", "name": "John", "endUsers": 1,
|
post_request = {
|
||||||
"devices": [device_id], "description": "aaa",
|
"transaction": "ccc",
|
||||||
|
"name": "John",
|
||||||
|
"endUsers": 1,
|
||||||
|
"devices": [device_id],
|
||||||
|
"description": "aaa",
|
||||||
"finalUserCode": "abcdefjhi",
|
"finalUserCode": "abcdefjhi",
|
||||||
"startTime": "2020-11-01T02:00:00+00:00",
|
"startTime": "2020-11-01T02:00:00+00:00",
|
||||||
"endTime": "2020-12-01T02:00:00+00:00"
|
"endTime": "2020-12-01T02:00:00+00:00",
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=Allocate, data=post_request)
|
user.post(res=Allocate, data=post_request)
|
||||||
hdd = [c for c in acer['components'] if c['type'] == 'HardDrive'][0]
|
hdd = [c for c in acer['components'] if c['type'] == 'HardDrive'][0]
|
||||||
hdd_action = [a for a in hdd['actions']
|
hdd_action = [a for a in hdd['actions'] if a['type'] == 'TestDataStorage'][0]
|
||||||
if a['type'] == 'TestDataStorage'][0]
|
|
||||||
hdd_action['lifetime'] += 1000
|
hdd_action['lifetime'] += 1000
|
||||||
acer.pop('elapsed')
|
acer.pop('elapsed')
|
||||||
acer['licence_version'] = '1.0.0'
|
acer['licence_version'] = '1.0.0'
|
||||||
snapshot, _ = client.post(acer, res=Live)
|
snapshot, _ = client.post(acer, res=Live)
|
||||||
|
|
||||||
csv_user, _ = user.get(res=documents.DocumentDef.t,
|
csv_user, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='actions/',
|
item='actions/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer'], 'ids': [device_id]})])
|
query=[('filter', {'type': ['Computer'], 'ids': [device_id]})],
|
||||||
|
)
|
||||||
|
|
||||||
csv_user2, _ = user2.get(res=documents.DocumentDef.t,
|
csv_user2, _ = user2.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='actions/',
|
item='actions/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer']})])
|
query=[('filter', {'type': ['Computer']})],
|
||||||
|
)
|
||||||
|
|
||||||
_, res = client.get(res=documents.DocumentDef.t,
|
_, res = client.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='actions/',
|
item='actions/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer']})], status=401)
|
query=[('filter', {'type': ['Computer']})],
|
||||||
|
status=401,
|
||||||
|
)
|
||||||
assert res.status_code == 401
|
assert res.status_code == 401
|
||||||
|
|
||||||
assert len(csv_user) > 0
|
assert len(csv_user) > 0
|
||||||
|
@ -167,21 +189,27 @@ def test_live_export_csv2(user: UserClient, client: Client, app: Devicehub):
|
||||||
acer = yaml2json('acer-happy.snapshot-test1')
|
acer = yaml2json('acer-happy.snapshot-test1')
|
||||||
snapshot, _ = user.post(json_encode(acer), res=Snapshot)
|
snapshot, _ = user.post(json_encode(acer), res=Snapshot)
|
||||||
device_id = snapshot['device']['id']
|
device_id = snapshot['device']['id']
|
||||||
post_request = {"transaction": "ccc", "name": "John", "endUsers": 1,
|
post_request = {
|
||||||
"devices": [device_id], "description": "aaa",
|
"transaction": "ccc",
|
||||||
|
"name": "John",
|
||||||
|
"endUsers": 1,
|
||||||
|
"devices": [device_id],
|
||||||
|
"description": "aaa",
|
||||||
"finalUserCode": "abcdefjhi",
|
"finalUserCode": "abcdefjhi",
|
||||||
"startTime": "2020-11-01T02:00:00+00:00",
|
"startTime": "2020-11-01T02:00:00+00:00",
|
||||||
"endTime": "2020-12-01T02:00:00+00:00"
|
"endTime": "2020-12-01T02:00:00+00:00",
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=Allocate, data=post_request)
|
user.post(res=Allocate, data=post_request)
|
||||||
|
|
||||||
acer = yaml2json('acer-happy.live-test1')
|
acer = yaml2json('acer-happy.live-test1')
|
||||||
live, _ = client.post(acer, res=Live)
|
live, _ = client.post(acer, res=Live)
|
||||||
csv_user, _ = user.get(res=documents.DocumentDef.t,
|
csv_user, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='actions/',
|
item='actions/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer'], 'ids': [device_id]})])
|
query=[('filter', {'type': ['Computer'], 'ids': [device_id]})],
|
||||||
|
)
|
||||||
|
|
||||||
assert "4692" in csv_user
|
assert "4692" in csv_user
|
||||||
assert "8692" in csv_user
|
assert "8692" in csv_user
|
||||||
|
@ -195,11 +223,15 @@ def test_live_example2(user: UserClient, client: Client, app: Devicehub):
|
||||||
acer = yaml2json('acer-happy.snapshot-test1')
|
acer = yaml2json('acer-happy.snapshot-test1')
|
||||||
snapshot, _ = user.post(json_encode(acer), res=Snapshot)
|
snapshot, _ = user.post(json_encode(acer), res=Snapshot)
|
||||||
device_id = snapshot['device']['id']
|
device_id = snapshot['device']['id']
|
||||||
post_request = {"transaction": "ccc", "name": "John", "endUsers": 1,
|
post_request = {
|
||||||
"devices": [device_id], "description": "aaa",
|
"transaction": "ccc",
|
||||||
|
"name": "John",
|
||||||
|
"endUsers": 1,
|
||||||
|
"devices": [device_id],
|
||||||
|
"description": "aaa",
|
||||||
"finalUserCode": "abcdefjhi",
|
"finalUserCode": "abcdefjhi",
|
||||||
"startTime": "2020-11-01T02:00:00+00:00",
|
"startTime": "2020-11-01T02:00:00+00:00",
|
||||||
"endTime": "2020-12-01T02:00:00+00:00"
|
"endTime": "2020-12-01T02:00:00+00:00",
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=Allocate, data=post_request)
|
user.post(res=Allocate, data=post_request)
|
||||||
|
@ -217,27 +249,36 @@ def test_export_basic_snapshot(user: UserClient):
|
||||||
"""Test export device information in a csv file."""
|
"""Test export device information in a csv file."""
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
dev_id = snapshot['device']['id']
|
dev_id = snapshot['device']['id']
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})])
|
query=[('filter', {'type': ['Computer'], 'ids': [dev_id]})],
|
||||||
|
)
|
||||||
|
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f, delimiter=';', quotechar='"')
|
obj_csv = csv.reader(f, f, delimiter=';', quotechar='"')
|
||||||
export_csv = list(obj_csv)
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
# Open fixture csv and transform to list
|
# Open fixture csv and transform to list
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('basic.csv').open() as csv_file:
|
with Path(__file__).parent.joinpath('files').joinpath(
|
||||||
|
'basic.csv'
|
||||||
|
).open() as csv_file:
|
||||||
obj_csv = csv.reader(csv_file, delimiter=';', quotechar='"')
|
obj_csv = csv.reader(csv_file, delimiter=';', quotechar='"')
|
||||||
fixture_csv = list(obj_csv)
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
assert isinstance(datetime.strptime(export_csv[1][19], '%c'), datetime), \
|
assert isinstance(
|
||||||
'Register in field is not a datetime'
|
datetime.strptime(export_csv[1][19], '%c'), datetime
|
||||||
|
), 'Register in field is not a datetime'
|
||||||
|
|
||||||
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
assert fixture_csv[1][:19] == export_csv[1][:19], 'Computer information are not equal'
|
assert (
|
||||||
|
fixture_csv[1][:19] == export_csv[1][:19]
|
||||||
|
), 'Computer information are not equal'
|
||||||
assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal'
|
assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal'
|
||||||
assert fixture_csv[1][22:] == export_csv[1][22:], 'Computer information are not equal'
|
assert (
|
||||||
|
fixture_csv[1][22:] == export_csv[1][22:]
|
||||||
|
), 'Computer information are not equal'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -245,13 +286,17 @@ def test_export_basic_snapshot(user: UserClient):
|
||||||
def test_check_insert_hash(app: Devicehub, user: UserClient, client: Client):
|
def test_check_insert_hash(app: Devicehub, user: UserClient, client: Client):
|
||||||
"""Test export device information in a csv file."""
|
"""Test export device information in a csv file."""
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer']})])
|
query=[('filter', {'type': ['Computer']})],
|
||||||
|
)
|
||||||
hash3 = hashlib.sha3_256(csv_str.encode('utf-8')).hexdigest()
|
hash3 = hashlib.sha3_256(csv_str.encode('utf-8')).hexdigest()
|
||||||
assert ReportHash.query.filter_by(hash3=hash3).count() == 1
|
assert ReportHash.query.filter_by(hash3=hash3).count() == 1
|
||||||
result, status = client.get(res=documents.DocumentDef.t, item='check/', query=[('hash', hash3)])
|
result, status = client.get(
|
||||||
|
res=documents.DocumentDef.t, item='check/', query=[('hash', hash3)]
|
||||||
|
)
|
||||||
assert status.status_code == 200
|
assert status.status_code == 200
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
|
@ -266,7 +311,9 @@ def test_check_insert_hash(app: Devicehub, user: UserClient, client: Client):
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
def test_export_extended(app: Devicehub, user: UserClient):
|
def test_export_extended(app: Devicehub, user: UserClient):
|
||||||
"""Test a export device with all information and a lot of components."""
|
"""Test a export device with all information and a lot of components."""
|
||||||
snapshot1, _ = user.post(file('real-eee-1001pxd.snapshot.12'), res=Snapshot, status=201)
|
snapshot1, _ = user.post(
|
||||||
|
file('real-eee-1001pxd.snapshot.12'), res=Snapshot, status=201
|
||||||
|
)
|
||||||
snapshot2, _ = user.post(file('complete.export.snapshot'), res=Snapshot, status=201)
|
snapshot2, _ = user.post(file('complete.export.snapshot'), res=Snapshot, status=201)
|
||||||
dev1_id = snapshot1['device']['id']
|
dev1_id = snapshot1['device']['id']
|
||||||
dev2_id = snapshot2['device']['id']
|
dev2_id = snapshot2['device']['id']
|
||||||
|
@ -276,10 +323,12 @@ def test_export_extended(app: Devicehub, user: UserClient):
|
||||||
db.session.add(pc)
|
db.session.add(pc)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer'], 'ids': [dev1_id, dev2_id]})])
|
query=[('filter', {'type': ['Computer'], 'ids': [dev1_id, dev2_id]})],
|
||||||
|
)
|
||||||
|
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f, delimiter=';', quotechar='"')
|
obj_csv = csv.reader(f, f, delimiter=';', quotechar='"')
|
||||||
|
@ -287,28 +336,50 @@ def test_export_extended(app: Devicehub, user: UserClient):
|
||||||
|
|
||||||
# Open fixture csv and transform to list
|
# Open fixture csv and transform to list
|
||||||
with Path(__file__).parent.joinpath('files').joinpath(
|
with Path(__file__).parent.joinpath('files').joinpath(
|
||||||
'proposal_extended_csv_report.csv').open() as csv_file:
|
'proposal_extended_csv_report.csv'
|
||||||
|
).open() as csv_file:
|
||||||
obj_csv = csv.reader(csv_file, delimiter=';', quotechar='"')
|
obj_csv = csv.reader(csv_file, delimiter=';', quotechar='"')
|
||||||
fixture_csv = list(obj_csv)
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
assert isinstance(datetime.strptime(export_csv[1][19], '%c'), datetime), \
|
assert isinstance(
|
||||||
'Register in field is not a datetime'
|
datetime.strptime(export_csv[1][19], '%c'), datetime
|
||||||
|
), 'Register in field is not a datetime'
|
||||||
|
|
||||||
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
assert fixture_csv[0] == export_csv[0], 'Headers are not equal'
|
||||||
assert fixture_csv[1][:19] == export_csv[1][:19], 'Computer information are not equal'
|
assert (
|
||||||
|
fixture_csv[1][:19] == export_csv[1][:19]
|
||||||
|
), 'Computer information are not equal'
|
||||||
assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal'
|
assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal'
|
||||||
assert fixture_csv[1][22:82] == export_csv[1][22:82], 'Computer information are not equal'
|
assert (
|
||||||
assert fixture_csv[1][83] == export_csv[1][83], 'Computer information are not equal'
|
fixture_csv[1][22:83] == export_csv[1][22:83]
|
||||||
assert fixture_csv[1][86:] == export_csv[1][86:], 'Computer information are not equal'
|
), 'Computer information are not equal'
|
||||||
assert fixture_csv[2][:19] == export_csv[2][:19], 'Computer information are not equal'
|
assert fixture_csv[1][84] == export_csv[1][84], 'Computer information are not equal'
|
||||||
|
assert (
|
||||||
|
fixture_csv[1][87:] == export_csv[1][87:]
|
||||||
|
), 'Computer information are not equal'
|
||||||
|
assert (
|
||||||
|
fixture_csv[2][:19] == export_csv[2][:19]
|
||||||
|
), 'Computer information are not equal'
|
||||||
assert fixture_csv[2][20] == export_csv[2][20], 'Computer information are not equal'
|
assert fixture_csv[2][20] == export_csv[2][20], 'Computer information are not equal'
|
||||||
assert fixture_csv[2][22:82] == export_csv[2][22:82], 'Computer information are not equal'
|
assert (
|
||||||
assert fixture_csv[2][83] == export_csv[2][83], 'Computer information are not equal'
|
fixture_csv[2][22:83] == export_csv[2][22:83]
|
||||||
assert fixture_csv[2][86:106] == export_csv[2][86:106], 'Computer information are not equal'
|
), 'Computer information are not equal'
|
||||||
assert fixture_csv[2][109] == export_csv[2][109], 'Computer information are not equal'
|
assert fixture_csv[2][84] == export_csv[2][84], 'Computer information are not equal'
|
||||||
assert fixture_csv[2][112:133] == export_csv[2][112:133], 'Computer information are not equal'
|
assert (
|
||||||
assert fixture_csv[2][135] == export_csv[2][135], 'Computer information are not equal'
|
fixture_csv[2][87:107] == export_csv[2][87:107]
|
||||||
assert fixture_csv[2][138:] == export_csv[2][138:], 'Computer information are not equal'
|
), 'Computer information are not equal'
|
||||||
|
assert (
|
||||||
|
fixture_csv[2][110] == export_csv[2][110]
|
||||||
|
), 'Computer information are not equal'
|
||||||
|
assert (
|
||||||
|
fixture_csv[2][113:134] == export_csv[2][113:134]
|
||||||
|
), 'Computer information are not equal'
|
||||||
|
assert (
|
||||||
|
fixture_csv[2][136] == export_csv[2][136]
|
||||||
|
), 'Computer information are not equal'
|
||||||
|
assert (
|
||||||
|
fixture_csv[2][139:] == export_csv[2][139:]
|
||||||
|
), 'Computer information are not equal'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
@ -316,9 +387,9 @@ def test_export_empty(user: UserClient):
|
||||||
"""Test to check works correctly exporting csv without any information,
|
"""Test to check works correctly exporting csv without any information,
|
||||||
export a placeholder device.
|
export a placeholder device.
|
||||||
"""
|
"""
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
accept='text/csv',
|
res=documents.DocumentDef.t, accept='text/csv', item='devices/'
|
||||||
item='devices/')
|
)
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f)
|
obj_csv = csv.reader(f, f)
|
||||||
export_csv = list(obj_csv)
|
export_csv = list(obj_csv)
|
||||||
|
@ -330,17 +401,20 @@ def test_export_empty(user: UserClient):
|
||||||
def test_export_computer_monitor(user: UserClient):
|
def test_export_computer_monitor(user: UserClient):
|
||||||
"""Test a export device type computer monitor."""
|
"""Test a export device type computer monitor."""
|
||||||
snapshot, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['ComputerMonitor']})])
|
query=[('filter', {'type': ['ComputerMonitor']})],
|
||||||
|
)
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f)
|
obj_csv = csv.reader(f, f)
|
||||||
export_csv = list(obj_csv)
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
# Open fixture csv and transform to list
|
# Open fixture csv and transform to list
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('computer-monitor.csv').open() \
|
with Path(__file__).parent.joinpath('files').joinpath(
|
||||||
as csv_file:
|
'computer-monitor.csv'
|
||||||
|
).open() as csv_file:
|
||||||
obj_csv = csv.reader(csv_file)
|
obj_csv = csv.reader(csv_file)
|
||||||
fixture_csv = list(obj_csv)
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
@ -356,15 +430,19 @@ def test_export_computer_monitor(user: UserClient):
|
||||||
def test_export_keyboard(user: UserClient):
|
def test_export_keyboard(user: UserClient):
|
||||||
"""Test a export device type keyboard."""
|
"""Test a export device type keyboard."""
|
||||||
snapshot, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Keyboard']})])
|
query=[('filter', {'type': ['Keyboard']})],
|
||||||
|
)
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f)
|
obj_csv = csv.reader(f, f)
|
||||||
export_csv = list(obj_csv)
|
export_csv = list(obj_csv)
|
||||||
# Open fixture csv and transform to list
|
# Open fixture csv and transform to list
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('keyboard.csv').open() as csv_file:
|
with Path(__file__).parent.joinpath('files').joinpath(
|
||||||
|
'keyboard.csv'
|
||||||
|
).open() as csv_file:
|
||||||
obj_csv = csv.reader(csv_file)
|
obj_csv = csv.reader(csv_file)
|
||||||
fixture_csv = list(obj_csv)
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
@ -382,8 +460,9 @@ def test_export_multiple_different_devices(user: UserClient):
|
||||||
computers, keyboards, monitors, etc..)
|
computers, keyboards, monitors, etc..)
|
||||||
"""
|
"""
|
||||||
# Open fixture csv and transform to list
|
# Open fixture csv and transform to list
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('multiples_devices.csv').open() \
|
with Path(__file__).parent.joinpath('files').joinpath(
|
||||||
as csv_file:
|
'multiples_devices.csv'
|
||||||
|
).open() as csv_file:
|
||||||
fixture_csv = list(csv.reader(csv_file))
|
fixture_csv = list(csv.reader(csv_file))
|
||||||
for row in fixture_csv:
|
for row in fixture_csv:
|
||||||
del row[8] # We remove the 'Registered in' column
|
del row[8] # We remove the 'Registered in' column
|
||||||
|
@ -394,10 +473,12 @@ def test_export_multiple_different_devices(user: UserClient):
|
||||||
snapshot_keyboard, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
snapshot_keyboard, _ = user.post(file('keyboard.snapshot'), res=Snapshot)
|
||||||
snapshot_monitor, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
snapshot_monitor, _ = user.post(file('computer-monitor.snapshot'), res=Snapshot)
|
||||||
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
query=[('filter', {'type': ['Computer', 'Keyboard', 'Monitor']})],
|
query=[('filter', {'type': ['Computer', 'Keyboard', 'Monitor']})],
|
||||||
accept='text/csv')
|
accept='text/csv',
|
||||||
|
)
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f)
|
obj_csv = csv.reader(f, f)
|
||||||
export_csv = list(obj_csv)
|
export_csv = list(obj_csv)
|
||||||
|
@ -410,20 +491,25 @@ def test_export_multiple_different_devices(user: UserClient):
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
def test_report_devices_stock_control(user: UserClient, user2: UserClient):
|
def test_report_devices_stock_control(user: UserClient, user2: UserClient):
|
||||||
|
# TODO
|
||||||
"""Test export device information in a csv file."""
|
"""Test export device information in a csv file."""
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
snapshot2, _ = user2.post(file('basic.snapshot2'), res=Snapshot)
|
snapshot2, _ = user2.post(file('basic.snapshot2'), res=Snapshot)
|
||||||
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stock/',
|
item='stock/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer']})])
|
query=[('filter', {'type': ['Computer']})],
|
||||||
|
)
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f)
|
obj_csv = csv.reader(f, f)
|
||||||
export_csv = list(obj_csv)
|
export_csv = list(obj_csv)
|
||||||
|
|
||||||
# Open fixture csv and transform to list
|
# Open fixture csv and transform to list
|
||||||
with Path(__file__).parent.joinpath('files').joinpath('basic-stock.csv').open() as csv_file:
|
with Path(__file__).parent.joinpath('files').joinpath(
|
||||||
|
'basic-stock.csv'
|
||||||
|
).open() as csv_file:
|
||||||
obj_csv = csv.reader(csv_file)
|
obj_csv = csv.reader(csv_file)
|
||||||
fixture_csv = list(obj_csv)
|
fixture_csv = list(obj_csv)
|
||||||
|
|
||||||
|
@ -432,8 +518,12 @@ def test_report_devices_stock_control(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
export_csv[0] = export_csv[0][0].split(';')
|
export_csv[0] = export_csv[0][0].split(';')
|
||||||
export_csv[1] = export_csv[1][0].split(';')
|
export_csv[1] = export_csv[1][0].split(';')
|
||||||
assert isinstance(datetime.strptime(export_csv[1][5], '%c'), datetime), \
|
fixture_csv[0] = fixture_csv[0][0].split(';')
|
||||||
'Register in field is not a datetime'
|
fixture_csv[1] = fixture_csv[1][0].split(';')
|
||||||
|
|
||||||
|
assert isinstance(
|
||||||
|
datetime.strptime(export_csv[1][5], '%c'), datetime
|
||||||
|
), 'Register in field is not a datetime'
|
||||||
|
|
||||||
# Pop dates fields from csv lists to compare them
|
# Pop dates fields from csv lists to compare them
|
||||||
fixture_csv[1] = fixture_csv[1][0].split(";")
|
fixture_csv[1] = fixture_csv[1][0].split(";")
|
||||||
|
@ -450,17 +540,22 @@ def test_report_devices_stock_control(user: UserClient, user2: UserClient):
|
||||||
def test_get_document_lots(user: UserClient, user2: UserClient):
|
def test_get_document_lots(user: UserClient, user2: UserClient):
|
||||||
"""Tests submitting and retreiving all lots."""
|
"""Tests submitting and retreiving all lots."""
|
||||||
|
|
||||||
l, _ = user.post({'name': 'Lot1', 'description': 'comments,lot1,testcomment-lot1,'}, res=Lot)
|
l, _ = user.post(
|
||||||
l, _ = user.post({'name': 'Lot2', 'description': 'comments,lot2,testcomment-lot2,'}, res=Lot)
|
{'name': 'Lot1', 'description': 'comments,lot1,testcomment-lot1,'}, res=Lot
|
||||||
l, _ = user2.post({'name': 'Lot3-User2', 'description': 'comments,lot3,testcomment-lot3,'}, res=Lot)
|
)
|
||||||
|
l, _ = user.post(
|
||||||
|
{'name': 'Lot2', 'description': 'comments,lot2,testcomment-lot2,'}, res=Lot
|
||||||
|
)
|
||||||
|
l, _ = user2.post(
|
||||||
|
{'name': 'Lot3-User2', 'description': 'comments,lot3,testcomment-lot3,'},
|
||||||
|
res=Lot,
|
||||||
|
)
|
||||||
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(res=documents.DocumentDef.t, item='lots/', accept='text/csv')
|
||||||
item='lots/',
|
|
||||||
accept='text/csv')
|
|
||||||
|
|
||||||
csv2_str, _ = user2.get(res=documents.DocumentDef.t,
|
csv2_str, _ = user2.get(
|
||||||
item='lots/',
|
res=documents.DocumentDef.t, item='lots/', accept='text/csv'
|
||||||
accept='text/csv')
|
)
|
||||||
|
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f)
|
obj_csv = csv.reader(f, f)
|
||||||
|
@ -473,10 +568,17 @@ def test_get_document_lots(user: UserClient, user2: UserClient):
|
||||||
assert len(export_csv) == 3
|
assert len(export_csv) == 3
|
||||||
assert len(export2_csv) == 2
|
assert len(export2_csv) == 2
|
||||||
|
|
||||||
assert export_csv[0] == export2_csv[0] == ['Id', 'Name', 'Registered in', 'Description']
|
assert (
|
||||||
|
export_csv[0]
|
||||||
|
== export2_csv[0]
|
||||||
|
== ['Id', 'Name', 'Registered in', 'Description']
|
||||||
|
)
|
||||||
|
|
||||||
assert export_csv[1][1] == 'Lot1' or 'Lot2'
|
assert export_csv[1][1] == 'Lot1' or 'Lot2'
|
||||||
assert export_csv[1][3] == 'comments,lot1,testcomment-lot1,' or 'comments,lot2,testcomment-lot2,'
|
assert (
|
||||||
|
export_csv[1][3] == 'comments,lot1,testcomment-lot1,'
|
||||||
|
or 'comments,lot2,testcomment-lot2,'
|
||||||
|
)
|
||||||
assert export2_csv[1][1] == 'Lot3-User2'
|
assert export2_csv[1][1] == 'Lot3-User2'
|
||||||
assert export2_csv[1][3] == 'comments,lot3,testcomment-lot3,'
|
assert export2_csv[1][3] == 'comments,lot3,testcomment-lot3,'
|
||||||
|
|
||||||
|
@ -485,34 +587,39 @@ def test_get_document_lots(user: UserClient, user2: UserClient):
|
||||||
def test_verify_stamp(user: UserClient, client: Client):
|
def test_verify_stamp(user: UserClient, client: Client):
|
||||||
"""Test verify stamp of one export device information in a csv file."""
|
"""Test verify stamp of one export device information in a csv file."""
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='devices/',
|
item='devices/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer']})])
|
query=[('filter', {'type': ['Computer']})],
|
||||||
|
)
|
||||||
|
|
||||||
response, _ = client.post(res=documents.DocumentDef.t,
|
response, _ = client.post(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stamps/',
|
item='stamps/',
|
||||||
content_type='multipart/form-data',
|
content_type='multipart/form-data',
|
||||||
accept='text/html',
|
accept='text/html',
|
||||||
data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]},
|
data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]},
|
||||||
status=200)
|
status=200,
|
||||||
|
)
|
||||||
assert "alert alert-info" in response
|
assert "alert alert-info" in response
|
||||||
assert not "alert alert-danger" in response
|
assert not "alert alert-danger" in response
|
||||||
|
|
||||||
response, _ = client.post(res=documents.DocumentDef.t,
|
response, _ = client.post(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stamps/',
|
item='stamps/',
|
||||||
content_type='multipart/form-data',
|
content_type='multipart/form-data',
|
||||||
accept='text/html',
|
accept='text/html',
|
||||||
data={'docUpload': [(BytesIO(b'abc'), 'example.csv')]},
|
data={'docUpload': [(BytesIO(b'abc'), 'example.csv')]},
|
||||||
status=200)
|
status=200,
|
||||||
|
)
|
||||||
|
|
||||||
assert not "alert alert-info" in response
|
assert not "alert alert-info" in response
|
||||||
assert "alert alert-danger" in response
|
assert "alert alert-danger" in response
|
||||||
|
|
||||||
response, _ = client.get(res=documents.DocumentDef.t,
|
response, _ = client.get(
|
||||||
item='stamps/',
|
res=documents.DocumentDef.t, item='stamps/', accept='text/html', status=200
|
||||||
accept='text/html',
|
)
|
||||||
status=200)
|
|
||||||
|
|
||||||
assert not "alert alert-info" in response
|
assert not "alert alert-info" in response
|
||||||
assert not "alert alert-danger" in response
|
assert not "alert alert-danger" in response
|
||||||
|
@ -522,20 +629,23 @@ def test_verify_stamp(user: UserClient, client: Client):
|
||||||
def test_verify_stamp_log_info(user: UserClient, client: Client):
|
def test_verify_stamp_log_info(user: UserClient, client: Client):
|
||||||
"""Test verify stamp of one export lots-info in a csv file."""
|
"""Test verify stamp of one export lots-info in a csv file."""
|
||||||
|
|
||||||
l, _ = user.post({'name': 'Lot1', 'description': 'comments,lot1,testcomment-lot1,'}, res=Lot)
|
l, _ = user.post(
|
||||||
l, _ = user.post({'name': 'Lot2', 'description': 'comments,lot2,testcomment-lot2,'}, res=Lot)
|
{'name': 'Lot1', 'description': 'comments,lot1,testcomment-lot1,'}, res=Lot
|
||||||
|
)
|
||||||
|
l, _ = user.post(
|
||||||
|
{'name': 'Lot2', 'description': 'comments,lot2,testcomment-lot2,'}, res=Lot
|
||||||
|
)
|
||||||
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(res=documents.DocumentDef.t, item='lots/', accept='text/csv')
|
||||||
item='lots/',
|
|
||||||
accept='text/csv')
|
|
||||||
|
|
||||||
response, _ = client.post(res=documents.DocumentDef.t,
|
response, _ = client.post(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stamps/',
|
item='stamps/',
|
||||||
content_type='multipart/form-data',
|
content_type='multipart/form-data',
|
||||||
accept='text/html',
|
accept='text/html',
|
||||||
data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')),
|
data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]},
|
||||||
'example.csv')]},
|
status=200,
|
||||||
status=200)
|
)
|
||||||
assert "alert alert-info" in response
|
assert "alert alert-info" in response
|
||||||
|
|
||||||
|
|
||||||
|
@ -545,18 +655,21 @@ def test_verify_stamp_devices_stock(user: UserClient, client: Client):
|
||||||
|
|
||||||
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot)
|
||||||
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stock/',
|
item='stock/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer']})])
|
query=[('filter', {'type': ['Computer']})],
|
||||||
|
)
|
||||||
|
|
||||||
response, _ = client.post(res=documents.DocumentDef.t,
|
response, _ = client.post(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stamps/',
|
item='stamps/',
|
||||||
content_type='multipart/form-data',
|
content_type='multipart/form-data',
|
||||||
accept='text/html',
|
accept='text/html',
|
||||||
data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')),
|
data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]},
|
||||||
'example.csv')]},
|
status=200,
|
||||||
status=200)
|
)
|
||||||
assert "alert alert-info" in response
|
assert "alert alert-info" in response
|
||||||
|
|
||||||
|
|
||||||
|
@ -566,11 +679,15 @@ def test_verify_stamp_csv_actions(user: UserClient, client: Client):
|
||||||
acer = yaml2json('acer.happy.battery.snapshot')
|
acer = yaml2json('acer.happy.battery.snapshot')
|
||||||
snapshot, _ = user.post(json_encode(acer), res=Snapshot)
|
snapshot, _ = user.post(json_encode(acer), res=Snapshot)
|
||||||
device_id = snapshot['device']['id']
|
device_id = snapshot['device']['id']
|
||||||
post_request = {"transaction": "ccc", "name": "John", "endUsers": 1,
|
post_request = {
|
||||||
"devices": [device_id], "description": "aaa",
|
"transaction": "ccc",
|
||||||
|
"name": "John",
|
||||||
|
"endUsers": 1,
|
||||||
|
"devices": [device_id],
|
||||||
|
"description": "aaa",
|
||||||
"finalUserCode": "abcdefjhi",
|
"finalUserCode": "abcdefjhi",
|
||||||
"startTime": "2020-11-01T02:00:00+00:00",
|
"startTime": "2020-11-01T02:00:00+00:00",
|
||||||
"endTime": "2020-12-01T02:00:00+00:00"
|
"endTime": "2020-12-01T02:00:00+00:00",
|
||||||
}
|
}
|
||||||
|
|
||||||
user.post(res=Allocate, data=post_request)
|
user.post(res=Allocate, data=post_request)
|
||||||
|
@ -581,18 +698,21 @@ def test_verify_stamp_csv_actions(user: UserClient, client: Client):
|
||||||
acer['licence_version'] = '1.0.0'
|
acer['licence_version'] = '1.0.0'
|
||||||
snapshot, _ = client.post(acer, res=Live)
|
snapshot, _ = client.post(acer, res=Live)
|
||||||
|
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='actions/',
|
item='actions/',
|
||||||
accept='text/csv',
|
accept='text/csv',
|
||||||
query=[('filter', {'type': ['Computer']})])
|
query=[('filter', {'type': ['Computer']})],
|
||||||
|
)
|
||||||
|
|
||||||
response, _ = client.post(res=documents.DocumentDef.t,
|
response, _ = client.post(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stamps/',
|
item='stamps/',
|
||||||
content_type='multipart/form-data',
|
content_type='multipart/form-data',
|
||||||
accept='text/html',
|
accept='text/html',
|
||||||
data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')),
|
data={'docUpload': [(BytesIO(bytes(csv_str, 'utf-8')), 'example.csv')]},
|
||||||
'example.csv')]},
|
status=200,
|
||||||
status=200)
|
)
|
||||||
assert "alert alert-info" in response
|
assert "alert alert-info" in response
|
||||||
|
|
||||||
|
|
||||||
|
@ -602,35 +722,38 @@ def test_verify_stamp_erasure_certificate(user: UserClient, client: Client):
|
||||||
s = file('erase-sectors.snapshot')
|
s = file('erase-sectors.snapshot')
|
||||||
snapshot, response = user.post(s, res=Snapshot)
|
snapshot, response = user.post(s, res=Snapshot)
|
||||||
|
|
||||||
doc, _ = user.get(res=documents.DocumentDef.t,
|
doc, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='erasures/',
|
item='erasures/',
|
||||||
query=[('filter', {'ids': [snapshot['device']['id']]})],
|
query=[('filter', {'ids': [snapshot['device']['id']]})],
|
||||||
accept=ANY)
|
accept=ANY,
|
||||||
|
)
|
||||||
|
|
||||||
response, _ = client.post(res=documents.DocumentDef.t,
|
response, _ = client.post(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stamps/',
|
item='stamps/',
|
||||||
content_type='multipart/form-data',
|
content_type='multipart/form-data',
|
||||||
accept='text/html',
|
accept='text/html',
|
||||||
data={'docUpload': [(BytesIO(bytes(doc, 'utf-8')),
|
data={'docUpload': [(BytesIO(bytes(doc, 'utf-8')), 'example.csv')]},
|
||||||
'example.csv')]},
|
status=200,
|
||||||
status=200)
|
)
|
||||||
assert "alert alert-danger" in response
|
assert "alert alert-danger" in response
|
||||||
|
|
||||||
doc, _ = user.get(res=documents.DocumentDef.t,
|
doc, _ = user.get(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='erasures/',
|
item='erasures/',
|
||||||
query=[
|
query=[('filter', {'ids': [snapshot['device']['id']]}), ('format', 'PDF')],
|
||||||
('filter', {'ids': [snapshot['device']['id']]}),
|
accept='application/pdf',
|
||||||
('format', 'PDF')
|
)
|
||||||
],
|
|
||||||
accept='application/pdf')
|
|
||||||
|
|
||||||
response, _ = client.post(res=documents.DocumentDef.t,
|
response, _ = client.post(
|
||||||
|
res=documents.DocumentDef.t,
|
||||||
item='stamps/',
|
item='stamps/',
|
||||||
content_type='multipart/form-data',
|
content_type='multipart/form-data',
|
||||||
accept='text/html',
|
accept='text/html',
|
||||||
data={'docUpload': [(BytesIO(doc),
|
data={'docUpload': [(BytesIO(doc), 'example.csv')]},
|
||||||
'example.csv')]},
|
status=200,
|
||||||
status=200)
|
)
|
||||||
assert "alert alert-info" in response
|
assert "alert alert-info" in response
|
||||||
|
|
||||||
|
|
||||||
|
@ -640,10 +763,9 @@ def test_get_document_internal_stats(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
# csv_str, _ = user.get(res=documents.DocumentDef.t,
|
# csv_str, _ = user.get(res=documents.DocumentDef.t,
|
||||||
# item='internalstats/')
|
# item='internalstats/')
|
||||||
csv_str, _ = user.get(res=documents.DocumentDef.t,
|
csv_str, _ = user.get(
|
||||||
item='internalstats/',
|
res=documents.DocumentDef.t, item='internalstats/', accept='text/csv', query=[]
|
||||||
accept='text/csv',
|
)
|
||||||
query=[])
|
|
||||||
|
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f)
|
obj_csv = csv.reader(f, f)
|
||||||
|
@ -651,10 +773,9 @@ def test_get_document_internal_stats(user: UserClient, user2: UserClient):
|
||||||
|
|
||||||
assert len(export_csv) == 1
|
assert len(export_csv) == 1
|
||||||
|
|
||||||
csv_str, _ = user2.get(res=documents.DocumentDef.t,
|
csv_str, _ = user2.get(
|
||||||
item='internalstats/',
|
res=documents.DocumentDef.t, item='internalstats/', accept='text/csv', query=[]
|
||||||
accept='text/csv',
|
)
|
||||||
query=[])
|
|
||||||
|
|
||||||
f = StringIO(csv_str)
|
f = StringIO(csv_str)
|
||||||
obj_csv = csv.reader(f, f)
|
obj_csv = csv.reader(f, f)
|
||||||
|
@ -675,8 +796,9 @@ def test_get_wbconf(user: UserClient):
|
||||||
assert 'WB_ERASE =' in env
|
assert 'WB_ERASE =' in env
|
||||||
# assert 'WB_ERASE = True' in env
|
# assert 'WB_ERASE = True' in env
|
||||||
|
|
||||||
session = Session.query.filter_by(user_id=user.user['id'],
|
session = Session.query.filter_by(
|
||||||
type=SessionType.Internal).first()
|
user_id=user.user['id'], type=SessionType.Internal
|
||||||
|
).first()
|
||||||
token = session.token
|
token = session.token
|
||||||
token = auth.Auth.encode(session.token)
|
token = auth.Auth.encode(session.token)
|
||||||
assert token in env
|
assert token in env
|
||||||
|
@ -694,7 +816,7 @@ def test_trade_documents(user: UserClient):
|
||||||
'filename': 'test.pdf',
|
'filename': 'test.pdf',
|
||||||
'hash': 'bbbbbbbb',
|
'hash': 'bbbbbbbb',
|
||||||
'url': 'http://www.ereuse.org/',
|
'url': 'http://www.ereuse.org/',
|
||||||
'lot': lot['id']
|
'lot': lot['id'],
|
||||||
}
|
}
|
||||||
doc, _ = user.post(res=TradeDocument, data=request_post)
|
doc, _ = user.post(res=TradeDocument, data=request_post)
|
||||||
assert doc['filename'] == request_post['filename']
|
assert doc['filename'] == request_post['filename']
|
||||||
|
@ -710,13 +832,15 @@ def test_trade_documents_with_weight(user: UserClient):
|
||||||
|
|
||||||
lot, _ = user.post({'name': 'MyLot'}, res=Lot)
|
lot, _ = user.post({'name': 'MyLot'}, res=Lot)
|
||||||
# long url
|
# long url
|
||||||
url = 'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa',
|
url = (
|
||||||
|
'http://www.ereuse.org/apapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapapaapaapaapaapaapaapaapaapaapaaaa',
|
||||||
|
)
|
||||||
request_post = {
|
request_post = {
|
||||||
'filename': 'test.pdf',
|
'filename': 'test.pdf',
|
||||||
'hash': 'bbbbbbbb',
|
'hash': 'bbbbbbbb',
|
||||||
'url': url,
|
'url': url,
|
||||||
'weight': 15,
|
'weight': 15,
|
||||||
'lot': lot['id']
|
'lot': lot['id'],
|
||||||
}
|
}
|
||||||
doc, _ = user.post(res=TradeDocument, data=request_post)
|
doc, _ = user.post(res=TradeDocument, data=request_post)
|
||||||
assert doc['weight'] == request_post['weight']
|
assert doc['weight'] == request_post['weight']
|
||||||
|
|
|
@ -224,11 +224,12 @@ def test_export_devices(user3: UserClientFlask):
|
||||||
), 'Computer information are not equal'
|
), 'Computer information are not equal'
|
||||||
assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal'
|
assert fixture_csv[1][20] == export_csv[1][20], 'Computer information are not equal'
|
||||||
assert (
|
assert (
|
||||||
fixture_csv[1][22:82] == export_csv[1][22:82]
|
fixture_csv[1][22:83] == export_csv[1][22:83]
|
||||||
), 'Computer information are not equal'
|
), 'Computer information are not equal'
|
||||||
assert fixture_csv[1][83] == export_csv[1][83], 'Computer information are not equal'
|
|
||||||
|
assert fixture_csv[1][84] == export_csv[1][84], 'Computer information are not equal'
|
||||||
assert (
|
assert (
|
||||||
fixture_csv[1][86:] == export_csv[1][86:]
|
fixture_csv[1][88:] == export_csv[1][88:]
|
||||||
), 'Computer information are not equal'
|
), 'Computer information are not equal'
|
||||||
|
|
||||||
|
|
||||||
|
@ -842,3 +843,26 @@ def test_action_datawipe(user3: UserClientFlask):
|
||||||
assert dev.actions[-1].type == 'DataWipe'
|
assert dev.actions[-1].type == 'DataWipe'
|
||||||
assert 'Action "DataWipe" created successfully!' in body
|
assert 'Action "DataWipe" created successfully!' in body
|
||||||
assert dev.devicehub_id in body
|
assert dev.devicehub_id in body
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb_settings(user3: UserClientFlask):
|
||||||
|
uri = '/workbench/settings/'
|
||||||
|
body, status = user3.get(uri)
|
||||||
|
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert "Download your settings for Workbench" in body
|
||||||
|
assert "Workbench Settings" in body
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_wb_settings_register(user3: UserClientFlask):
|
||||||
|
uri = '/workbench/settings/?opt=register'
|
||||||
|
body, status = user3.get(uri)
|
||||||
|
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert "TOKEN = " in body
|
||||||
|
assert "URL = https://" in body
|
||||||
|
assert "/api/inventory/" in body
|
||||||
|
|
Reference in New Issue