-
-
- {% trans 'Placeholders' %}
-
-
diff --git a/dashboard/templates/unassigned_devices.html b/dashboard/templates/unassigned_devices.html
index 14085bb..8356019 100644
--- a/dashboard/templates/unassigned_devices.html
+++ b/dashboard/templates/unassigned_devices.html
@@ -25,6 +25,9 @@
diff --git a/dashboard/urls.py b/dashboard/urls.py
index 835e708..cf19b31 100644
--- a/dashboard/urls.py
+++ b/dashboard/urls.py
@@ -1,10 +1,11 @@
from django.urls import path
-from dashboard import views
+from dashboard import views
app_name = 'dashboard'
urlpatterns = [
- path("", views.UnassignedDevicesView.as_view(), name="unassigned_devices"),
+ path("", views.UnassignedDevicesView.as_view(), name="unassigned"),
+ path("all", views.AllDevicesView.as_view(), name="all_device"),
path("/", views.LotDashboardView.as_view(), name="lot"),
path("search", views.SearchView.as_view(), name="search"),
]
diff --git a/dashboard/views.py b/dashboard/views.py
index 4917bb8..369567b 100644
--- a/dashboard/views.py
+++ b/dashboard/views.py
@@ -22,6 +22,16 @@ class UnassignedDevicesView(InventaryMixin):
return Device.get_unassigned(self.request.user.institution, offset, limit)
+class AllDevicesView(InventaryMixin):
+ template_name = "unassigned_devices.html"
+ section = "All"
+ title = _("All Devices")
+ breadcrumb = "Devices / All Devices"
+
+ def get_devices(self, user, offset, limit):
+ return Device.get_all(self.request.user.institution, offset, limit)
+
+
class LotDashboardView(InventaryMixin, DetailsMixin):
template_name = "unassigned_devices.html"
section = "dashboard_lot"
diff --git a/device/models.py b/device/models.py
index a385895..22b13dc 100644
--- a/device/models.py
+++ b/device/models.py
@@ -136,6 +136,87 @@ class Device:
self.lots = [
x.lot for x in DeviceLot.objects.filter(device_id=self.id)]
+ @classmethod
+ def get_all(cls, institution, offset=0, limit=None):
+ sql = """
+ WITH RankedProperties AS (
+ SELECT
+ t1.value,
+ t1.key,
+ ROW_NUMBER() OVER (
+ PARTITION BY t1.uuid
+ ORDER BY
+ CASE
+ WHEN t1.key = 'CUSTOM_ID' THEN 1
+ WHEN t1.key = '{algorithm}' THEN 2
+ END,
+ t1.created DESC
+ ) AS row_num
+ FROM evidence_systemproperty AS t1
+ WHERE t1.owner_id = {institution}
+ AND t1.key IN ('CUSTOM_ID', '{algorithm}')
+ )
+ SELECT DISTINCT
+ value
+ FROM
+ RankedProperties
+ WHERE
+ row_num = 1
+ """.format(
+ institution=institution.id,
+ algorithm=institution.algorithm,
+ )
+ if limit:
+ sql += " limit {} offset {}".format(int(limit), int(offset))
+
+ sql += ";"
+
+ annotations = []
+ with connection.cursor() as cursor:
+ cursor.execute(sql)
+ annotations = cursor.fetchall()
+
+ devices = [cls(id=x[0]) for x in annotations]
+ count = cls.get_all_count(institution)
+ return devices, count
+
+ @classmethod
+ def get_all_count(cls, institution):
+
+ sql = """
+ WITH RankedProperties AS (
+ SELECT
+ t1.value,
+ t1.key,
+ ROW_NUMBER() OVER (
+ PARTITION BY t1.uuid
+ ORDER BY
+ CASE
+ WHEN t1.key = 'CUSTOM_ID' THEN 1
+ WHEN t1.key = '{algorithm}' THEN 2
+ END,
+ t1.created DESC
+ ) AS row_num
+
+ FROM evidence_systemproperty AS t1
+ WHERE t1.owner_id = {institution}
+ AND t1.key IN ('CUSTOM_ID', '{algorithm}')
+ )
+ SELECT
+ COUNT(DISTINCT value)
+ FROM
+ RankedProperties
+ WHERE
+ row_num = 1
+ """.format(
+ institution=institution.id,
+ algorithm=institution.algorithm
+ )
+ with connection.cursor() as cursor:
+ cursor.execute(sql)
+ return cursor.fetchall()[0][0]
+
+
@classmethod
def get_unassigned(cls, institution, offset=0, limit=None):
@@ -149,8 +230,7 @@ class Device:
ORDER BY
CASE
WHEN t1.key = 'CUSTOM_ID' THEN 1
- WHEN t1.key = 'ereuse24' THEN 2
- ELSE 3
+ WHEN t1.key = '{algorithm}' THEN 2
END,
t1.created DESC
) AS row_num
@@ -158,6 +238,7 @@ class Device:
LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id
WHERE t2.device_id IS NULL
AND t1.owner_id = {institution}
+ AND t1.key IN ('CUSTOM_ID', '{algorithm}')
)
SELECT DISTINCT
value
@@ -167,6 +248,7 @@ class Device:
row_num = 1
""".format(
institution=institution.id,
+ algorithm=institution.algorithm
)
if limit:
sql += " limit {} offset {}".format(int(limit), int(offset))
@@ -195,8 +277,7 @@ class Device:
ORDER BY
CASE
WHEN t1.key = 'CUSTOM_ID' THEN 1
- WHEN t1.key = 'ereuse24' THEN 2
- ELSE 3
+ WHEN t1.key = '{algorithm}' THEN 2
END,
t1.created DESC
) AS row_num
@@ -204,6 +285,7 @@ class Device:
LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id
WHERE t2.device_id IS NULL
AND t1.owner_id = {institution}
+ AND t1.key IN ('CUSTOM_ID', '{algorithm}')
)
SELECT
COUNT(DISTINCT value)
@@ -213,6 +295,7 @@ class Device:
row_num = 1
""".format(
institution=institution.id,
+ algorithm=institution.algorithm
)
with connection.cursor() as cursor:
cursor.execute(sql)
@@ -230,16 +313,14 @@ class Device:
ORDER BY
CASE
WHEN t1.key = 'CUSTOM_ID' THEN 1
- WHEN t1.key = 'ereuse24' THEN 2
- ELSE 3
+ WHEN t1.key = '{algorithm}' THEN 2
END,
t1.created DESC
) AS row_num
FROM evidence_systemproperty AS t1
- LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id
- WHERE t2.device_id IS NULL
- AND t1.owner_id = {institution}
+ WHERE t1.owner_id = {institution}
AND t1.uuid = '{uuid}'
+ AND t1.key IN ('CUSTOM_ID', '{algorithm}')
)
SELECT DISTINCT
value
@@ -250,6 +331,7 @@ class Device:
""".format(
uuid=uuid.replace("-", ""),
institution=institution.id,
+ algorithm=institution.algorithm,
)
properties = []
@@ -274,6 +356,12 @@ class Device:
self.get_last_evidence()
return self.last_evidence.get_manufacturer()
+ @property
+ def updated(self):
+ """get timestamp from last evidence created"""
+ self.get_last_evidence()
+ return self.last_evidence.created
+
@property
def serial_number(self):
self.get_last_evidence()
diff --git a/device/templates/new_device.html b/device/templates/new_device.html
index 320790d..46b4ce6 100644
--- a/device/templates/new_device.html
+++ b/device/templates/new_device.html
@@ -78,7 +78,7 @@
{% endfor %}
diff --git a/device/views.py b/device/views.py
index 7ae34f3..b2b3192 100644
--- a/device/views.py
+++ b/device/views.py
@@ -40,7 +40,7 @@ class NewDeviceView(DashboardView, FormView):
template_name = "new_device.html"
title = _("New Device")
breadcrumb = "Device / New Device"
- success_url = reverse_lazy('dashboard:unassigned_devices')
+ success_url = reverse_lazy('dashboard:unassigned')
form_class = DeviceFormSet
def form_valid(self, form):
diff --git a/evidence/mixin_parse.py b/evidence/mixin_parse.py
index 819edf8..0fb6c36 100644
--- a/evidence/mixin_parse.py
+++ b/evidence/mixin_parse.py
@@ -27,7 +27,7 @@ class BuildMix:
hid = ""
for f in algorithm:
if hasattr(self, f):
- hid += getattr(self, f)
+ hid += getattr(self, f) or ''
return hid
def generate_chids(self):
diff --git a/evidence/templates/upload.html b/evidence/templates/upload.html
index 6337b3e..e2d7aff 100644
--- a/evidence/templates/upload.html
+++ b/evidence/templates/upload.html
@@ -11,8 +11,8 @@
@@ -22,7 +22,7 @@
{% bootstrap_form form alert_error_type="none" error_css_class="alert alert-danger alert-icon alert-icon-border" %}
diff --git a/login/views.py b/login/views.py
index beb0368..885cc86 100644
--- a/login/views.py
+++ b/login/views.py
@@ -17,18 +17,18 @@ class LoginView(auth_views.LoginView):
template_name = 'login.html'
extra_context = {
'title': _('Login'),
- 'success_url': reverse_lazy('dashboard:unassigned_devices'),
+ 'success_url': reverse_lazy('dashboard:unassigned'),
'commit_id': settings.COMMIT,
}
def get(self, request, *args, **kwargs):
self.extra_context['success_url'] = request.GET.get(
'next',
- reverse_lazy('dashboard:unassigned_devices')
+ reverse_lazy('dashboard:unassigned')
)
if not self.request.user.is_anonymous:
- return redirect(reverse_lazy('dashboard:unassigned_devices'))
-
+ return redirect(reverse_lazy('dashboard:unassigned'))
+
return super().get(request, *args, **kwargs)
def form_valid(self, form):
@@ -72,4 +72,3 @@ class PasswordResetView(auth_views.PasswordResetView):
except Exception as err:
logger.error(err)
return HttpResponseRedirect(self.success_url)
-
diff --git a/lot/migrations/0007_lottag_inbox.py b/lot/migrations/0007_lottag_inbox.py
new file mode 100644
index 0000000..78c08b1
--- /dev/null
+++ b/lot/migrations/0007_lottag_inbox.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.0.6 on 2025-02-17 10:47
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('lot', '0006_lotproperty_property_unique_type_key_lot'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='lottag',
+ name='inbox',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/lot/models.py b/lot/models.py
index e468932..7ab5ae5 100644
--- a/lot/models.py
+++ b/lot/models.py
@@ -15,6 +15,7 @@ class LotTag(models.Model):
name = models.CharField(max_length=STR_SIZE, blank=False, null=False)
owner = models.ForeignKey(Institution, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
+ inbox = models.BooleanField(default=False)
def __str__(self):
return self.name
@@ -45,7 +46,7 @@ class Lot(models.Model):
for d in DeviceLot.objects.filter(lot=self, device_id=v):
d.delete()
-class LotProperty (Property):
+class LotProperty(Property):
lot = models.ForeignKey(Lot, on_delete=models.CASCADE)
class Type(models.IntegerChoices):
diff --git a/lot/templates/delete_lot.html b/lot/templates/delete_lot.html
index 95fe1d6..babdb89 100644
--- a/lot/templates/delete_lot.html
+++ b/lot/templates/delete_lot.html
@@ -30,7 +30,7 @@
{% endif %}
{% bootstrap_form form %}
diff --git a/lot/templates/list_lots.html b/lot/templates/list_lots.html
index 4024387..50caf8b 100644
--- a/lot/templates/list_lots.html
+++ b/lot/templates/list_lots.html
@@ -11,20 +11,26 @@
diff --git a/lot/templates/new_lot.html b/lot/templates/new_lot.html
index 8f2df37..c4c2cc8 100644
--- a/lot/templates/new_lot.html
+++ b/lot/templates/new_lot.html
@@ -24,7 +24,7 @@
{% endif %}
{% bootstrap_form form %}
diff --git a/lot/urls.py b/lot/urls.py
index 3456f91..4ddd452 100644
--- a/lot/urls.py
+++ b/lot/urls.py
@@ -9,7 +9,7 @@ urlpatterns = [
path("edit//", views.EditLotView.as_view(), name="edit"),
path("add/devices/", views.AddToLotView.as_view(), name="add_devices"),
path("del/devices/", views.DelToLotView.as_view(), name="del_devices"),
- path("tag//", views.LotsTagsView.as_view(), name="tag"),
+ path("group//", views.LotsTagsView.as_view(), name="tags"),
path("/property", views.LotPropertiesView.as_view(), name="properties"),
path("/property/add", views.AddLotPropertyView.as_view(), name="add_property"),
path("/property/update", views.UpdateLotPropertyView.as_view(), name="update_property"),
diff --git a/lot/views.py b/lot/views.py
index 6acc58c..a64405f 100644
--- a/lot/views.py
+++ b/lot/views.py
@@ -18,7 +18,7 @@ class NewLotView(DashboardView, CreateView):
template_name = "new_lot.html"
title = _("New lot")
breadcrumb = "lot / New lot"
- success_url = reverse_lazy('dashboard:unassigned_devices')
+ success_url = reverse_lazy('dashboard:unassigned')
model = Lot
fields = (
"type",
@@ -28,6 +28,14 @@ class NewLotView(DashboardView, CreateView):
"closed",
)
+ def get_form(self):
+ form = super().get_form()
+ form.fields["type"].queryset = LotTag.objects.filter(
+ owner=self.request.user.institution,
+ inbox=False
+ )
+ return form
+
def form_valid(self, form):
form.instance.owner = self.request.user.institution
form.instance.user = self.request.user
@@ -39,7 +47,7 @@ class DeleteLotView(DashboardView, DeleteView):
template_name = "delete_lot.html"
title = _("Delete lot")
breadcrumb = "lot / Delete lot"
- success_url = reverse_lazy('dashboard:unassigned_devices')
+ success_url = reverse_lazy('dashboard:unassigned')
model = Lot
fields = (
"type",
@@ -58,7 +66,7 @@ class EditLotView(DashboardView, UpdateView):
template_name = "new_lot.html"
title = _("Edit lot")
breadcrumb = "Lot / Edit lot"
- success_url = reverse_lazy('dashboard:unassigned_devices')
+ success_url = reverse_lazy('dashboard:unassigned')
model = Lot
fields = (
"type",
@@ -84,7 +92,7 @@ class AddToLotView(DashboardView, FormView):
template_name = "list_lots.html"
title = _("Add to lots")
breadcrumb = "lot / add to lots"
- success_url = reverse_lazy('dashboard:unassigned_devices')
+ success_url = reverse_lazy('dashboard:unassigned')
form_class = LotsForm
def get_context_data(self, **kwargs):
@@ -125,7 +133,7 @@ class LotsTagsView(DashboardView, TemplateView):
template_name = "lots.html"
title = _("lots")
breadcrumb = _("lots") + " /"
- success_url = reverse_lazy('dashboard:unassigned_devices')
+ success_url = reverse_lazy('dashboard:unassigned')
def get_context_data(self, **kwargs):
self.pk = kwargs.get('pk')
diff --git a/user/management/commands/add_institution.py b/user/management/commands/add_institution.py
index b7a4a26..346d2fc 100644
--- a/user/management/commands/add_institution.py
+++ b/user/management/commands/add_institution.py
@@ -2,6 +2,7 @@ from django.core.management.base import BaseCommand
from user.models import Institution
from lot.models import LotTag
+
class Command(BaseCommand):
help = "Create a new Institution"
@@ -13,6 +14,11 @@ class Command(BaseCommand):
self.create_lot_tags()
def create_lot_tags(self):
+ LotTag.objects.create(
+ inbox=True,
+ name="Inbox",
+ owner=self.institution
+ )
tags = [
"Entrada",
"Salida",
diff --git a/user/migrations/0002_institution_algorithm.py b/user/migrations/0002_institution_algorithm.py
new file mode 100644
index 0000000..3f4660d
--- /dev/null
+++ b/user/migrations/0002_institution_algorithm.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.0.6 on 2025-02-18 08:52
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('user', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='institution',
+ name='algorithm',
+ field=models.CharField(choices=[('ereuse24', 'ereuse24'), ('ereuse22', 'ereuse22')], default='ereuse24', max_length=30),
+ ),
+ ]
diff --git a/user/models.py b/user/models.py
index 5fd8424..a289270 100644
--- a/user/models.py
+++ b/user/models.py
@@ -1,8 +1,10 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
+from utils.constants import ALGOS
-# Create your models here.
+
+ALGORITHMS = [(x, x) for x in ALGOS.keys()]
class Institution(models.Model):
@@ -27,6 +29,7 @@ class Institution(models.Model):
blank=True,
null=True
)
+ algorithm = models.CharField(max_length=30, choices=ALGORITHMS, default='ereuse24')
class UserManager(BaseUserManager):