diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/admin.py b/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/api/apps.py b/api/apps.py new file mode 100644 index 0000000..66656fd --- /dev/null +++ b/api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'api' diff --git a/api/forms.py b/api/forms.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/api/forms.py @@ -0,0 +1 @@ + diff --git a/api/migrations/0001_initial.py b/api/migrations/0001_initial.py new file mode 100644 index 0000000..22c5a1f --- /dev/null +++ b/api/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 5.0.6 on 2024-09-19 15:09 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Token", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("token", models.UUIDField()), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/api/migrations/__init__.py b/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/models.py b/api/models.py new file mode 100644 index 0000000..b8bbc24 --- /dev/null +++ b/api/models.py @@ -0,0 +1,9 @@ +from django.db import models +from user.models import User + +# Create your models here. + + +class Token(models.Model): + token = models.UUIDField() + owner = models.ForeignKey(User, on_delete=models.CASCADE) diff --git a/api/tables.py b/api/tables.py new file mode 100644 index 0000000..ac1cc7a --- /dev/null +++ b/api/tables.py @@ -0,0 +1,67 @@ +import django_tables2 as tables +from django.utils.html import format_html +from django.utils.translation import gettext_lazy as _ + +from api.models import Token + + +class ButtonColumn(tables.Column): + attrs = { + "a": { + "type": "button", + "class": "text-danger", + "title": "Remove", + } + } + # it makes no sense to order a column of buttons + orderable = False + # django_tables will only call the render function if it doesn't find + # any empty values in the data, so we stop it from matching the data + # to any value considered empty + empty_values = () + + def render(self): + return format_html('') + + +class TokensTable(tables.Table): + delete = ButtonColumn( + verbose_name=_("Delete"), + linkify={ + "viewname": "api:delete_token", + "args": [tables.A("pk")] + }, + orderable=False + ) + + token = tables.Column(verbose_name=_("Token"), empty_values=()) + + def render_view_user(self): + return format_html('') + + # def render_token(self, record): + # return record.get_memberships() + + # def order_membership(self, queryset, is_descending): + # # TODO: Test that this doesn't return more rows than it should + # queryset = queryset.order_by( + # ("-" if is_descending else "") + "memberships__type" + # ) + + # return (queryset, True) + + # def render_role(self, record): + # return record.get_roles() + + # def order_role(self, queryset, is_descending): + # queryset = queryset.order_by( + # ("-" if is_descending else "") + "roles" + # ) + + # return (queryset, True) + + class Meta: + model = Token + template_name = "custom_table.html" + fields = ("token", "view_user") + diff --git a/api/templates/custom_table.html b/api/templates/custom_table.html new file mode 100644 index 0000000..496ddec --- /dev/null +++ b/api/templates/custom_table.html @@ -0,0 +1,100 @@ +{% load django_tables2 %} +{% load i18n %} +{% block table-wrapper %} +
+ {% block table %} + + {% block table.thead %} + {% if table.show_header %} + + + {% for column in table.columns %} + + {% endfor %} + + + {% endif %} + {% endblock table.thead %} + {% block table.tbody %} + + {% for row in table.paginated_rows %} + {% block table.tbody.row %} + + {% for column, cell in row.items %} + + {% endfor %} + + {% endblock table.tbody.row %} + {% empty %} + {% if table.empty_text %} + {% block table.tbody.empty_text %} + + {% endblock table.tbody.empty_text %} + {% endif %} + {% endfor %} + + {% endblock table.tbody %} + {% block table.tfoot %} + {% if table.has_footer %} + + + {% for column in table.columns %} + + {% endfor %} + + + {% endif %} + {% endblock table.tfoot %} +
+ {% if column.orderable %} + {{ column.header }} + {% else %} + {{ column.header }} + {% endif %} +
{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}
{{ table.empty_text }}
{{ column.footer }}
+ {% endblock table %} + + {% block pagination %} + {% if table.page and table.paginator.num_pages > 1 %} + + {% endif %} + {% endblock pagination %} +
+{% endblock table-wrapper %} diff --git a/api/templates/token.html b/api/templates/token.html new file mode 100644 index 0000000..5185090 --- /dev/null +++ b/api/templates/token.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block content %} +

+ + {{ subtitle }} +

+{% render_table table %} +
+ {% translate "Generate a new token" %} +
+{% endblock %} diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 0000000..f3c8028 --- /dev/null +++ b/api/urls.py @@ -0,0 +1,13 @@ +from api import views + +from django.urls import path + + +app_name = 'api' + +urlpatterns = [ + path('snapshot/', views.NewSnapshot, name='new_snapshot'), + path('tokens/', views.TokenView.as_view(), name='tokens'), + path('tokens/new', views.TokenNewView.as_view(), name='new_token'), + path('tokens//del', views.TokenDeleteView.as_view(), name='delete_token'), +] diff --git a/api/views.py b/api/views.py new file mode 100644 index 0000000..fbccb79 --- /dev/null +++ b/api/views.py @@ -0,0 +1,114 @@ +import json + +from django.shortcuts import get_object_or_404, redirect +from django.utils.translation import gettext_lazy as _ +from django.views.decorators.csrf import csrf_exempt +from django.core.exceptions import ValidationError +from django.views.generic.edit import DeleteView +from django.views.generic.base import View +from django.http import JsonResponse +from django_tables2 import SingleTableView +from uuid import uuid4 + +from dashboard.mixins import DashboardView +from evidence.models import Annotation +from evidence.parse import Build +from user.models import User +from api.models import Token +from api.tables import TokensTable + + +def save_in_disk(data, user): + pass + + +@csrf_exempt +def NewSnapshot(request): + # Accept only posts + if request.method != 'POST': + return JsonResponse({'error': 'Invalid request method'}, status=400) + + # Authentication + # auth_header = request.headers.get('Authorization') + # if not auth_header or not auth_header.startswith('Bearer '): + # return JsonResponse({'error': 'Invalid or missing token'}, status=401) + + # token = auth_header.split(' ')[1] + # tk = Token.objects.filter(token=token).first() + # if not tk: + # return JsonResponse({'error': 'Invalid or missing token'}, status=401) + + # Validation snapshot + try: + data = json.loads(request.body) + except json.JSONDecodeError: + return JsonResponse({'error': 'Invalid JSON'}, status=400) + + # try: + # Build(data, None, check=True) + # except Exception: + # return JsonResponse({'error': 'Invalid Snapshot'}, status=400) + + exist_annotation = Annotation.objects.filter( + uuid=data['uuid'] + ).first() + + if exist_annotation: + raise ValidationError("error: the snapshot {} exist".format(data['uuid'])) + + # Process snapshot + # save_in_disk(data, tk.user) + + try: + # Build(data, tk.user) + user = User.objects.get(email="user@example.org") + Build(data, user) + except Exception: + return JsonResponse({'status': 'fail'}, status=200) + + return JsonResponse({'status': 'success'}, status=200) + + + + +class TokenView(DashboardView, SingleTableView): + template_name = "token.html" + title = _("Credential management") + section = "Credential" + subtitle = _('Managament Tokens') + icon = 'bi bi-key' + model = Token + table_class = TokensTable + + def get_queryset(self): + """ + Override the get_queryset method to filter events based on the user type. + """ + return Token.objects.filter().order_by("-id") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context.update({ + 'tokens': Token.objects, + }) + return context + + +class TokenDeleteView(DashboardView, DeleteView): + model = Token + + def get(self, request, *args, **kwargs): + self.pk = kwargs['pk'] + self.object = get_object_or_404(self.model, pk=self.pk, owner=self.request.user) + self.object.delete() + + return redirect('api:tokens') + + +class TokenNewView(DashboardView, View): + + def get(self, request, *args, **kwargs): + Token.objects.create(token=uuid4(), owner=self.request.user) + + return redirect('api:tokens') + diff --git a/dashboard/mixins.py b/dashboard/mixins.py index 8b70934..df6e206 100644 --- a/dashboard/mixins.py +++ b/dashboard/mixins.py @@ -39,7 +39,7 @@ class DashboardView(LoginRequiredMixin): 'section': self.section, 'path': resolve(self.request.path).url_name, 'user': self.request.user, - 'lot_tags': LotTag.objects.filter(owner=self.request.user) + 'lot_tags': LotTag.objects.filter(owner=self.request.user.institution) }) return context @@ -58,7 +58,7 @@ class DetailsMixin(DashboardView, TemplateView): def get(self, request, *args, **kwargs): self.pk = kwargs['pk'] - self.object = get_object_or_404(self.model, pk=self.pk, owner=self.request.user) + self.object = get_object_or_404(self.model, pk=self.pk, owner=self.request.user.institution) return super().get(request, *args, **kwargs) def get_context_data(self, **kwargs): diff --git a/dashboard/templates/base.html b/dashboard/templates/base.html index 97d090a..5c92024 100644 --- a/dashboard/templates/base.html +++ b/dashboard/templates/base.html @@ -163,6 +163,12 @@ + diff --git a/device/models.py b/device/models.py index 6264130..b283433 100644 --- a/device/models.py +++ b/device/models.py @@ -113,14 +113,14 @@ class Device: self.lots = [x.lot for x in DeviceLot.objects.filter(device_id=self.id)] @classmethod - def get_unassigned(cls, user, offset=0, limit=None): + def get_unassigned(cls, institution, offset=0, limit=None): sql = """ SELECT DISTINCT t1.value from evidence_annotation as t1 left join lot_devicelot as t2 on t1.value = t2.device_id - where t2.device_id is null and owner_id=={user} and type=={type} + where t2.device_id is null and owner_id=={institution} and type=={type} """.format( - user=user.id, + institution=institution.id, type=Annotation.Type.SYSTEM, ) if limit: @@ -134,19 +134,19 @@ class Device: annotations = cursor.fetchall() devices = [cls(id=x[0]) for x in annotations] - count = cls.get_unassigned_count(user) + count = cls.get_unassigned_count(institution) return devices, count @classmethod - def get_unassigned_count(cls, user): + def get_unassigned_count(cls, institution): sql = """ SELECT count(DISTINCT t1.value) from evidence_annotation as t1 left join lot_devicelot as t2 on t1.value = t2.device_id - where t2.device_id is null and owner_id=={user} and type=={type}; + where t2.device_id is null and owner_id=={institution} and type=={type}; """.format( - user=user.id, + institution=institution.id, type=Annotation.Type.SYSTEM, ) @@ -170,22 +170,17 @@ class Device: def manufacturer(self): if not self.last_evidence: self.get_last_evidence() - return self.last_evidence.doc['device']['manufacturer'] + return self.last_evidence.get_manufacturer() @property def type(self): if not self.last_evidence: self.get_last_evidence() - return self.last_evidence.doc['device']['type'] + return self.last_evidence.get_chassis() @property def model(self): if not self.last_evidence: self.get_last_evidence() - return self.last_evidence.doc['device']['model'] + return self.last_evidence.get_model() - @property - def type(self): - if not self.last_evidence: - self.get_last_evidence() - return self.last_evidence.doc['device']['type'] diff --git a/device/views.py b/device/views.py index 05183b0..85ba7da 100644 --- a/device/views.py +++ b/device/views.py @@ -1,5 +1,6 @@ import json +from django.http import Http404 from django.urls import reverse_lazy from django.shortcuts import get_object_or_404 from django.utils.translation import gettext_lazy as _ @@ -98,7 +99,7 @@ class DetailsView(DashboardView, TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) self.object.initial() - lot_tags = LotTag.objects.filter(owner=self.request.user) + lot_tags = LotTag.objects.filter(owner=self.request.user.institution) context.update({ 'object': self.object, 'snapshot': self.object.get_last_evidence(), @@ -119,20 +120,22 @@ class AddAnnotationView(DashboardView, CreateView): form.instance.owner = self.request.user.institution form.instance.user = self.request.user form.instance.uuid = self.annotation.uuid - form.instance.uuid = self.annotation.uuid form.instance.type = Annotation.Type.USER response = super().form_valid(form) return response def get_form_kwargs(self): pk = self.kwargs.get('pk') + institution = self.request.user.institution self.annotation = Annotation.objects.filter( - owner=self.request.user.institution, + owner=institution, value=pk, type=Annotation.Type.SYSTEM ).first() + if not self.annotation: - get_object_or_404(Annotation, pk=0, owner=self.request.user.institution) + raise Http404 + self.success_url = reverse_lazy('device:details', args=[pk]) kwargs = super().get_form_kwargs() return kwargs @@ -156,11 +159,16 @@ class AddDocumentView(DashboardView, CreateView): def get_form_kwargs(self): pk = self.kwargs.get('pk') + institution = self.request.user.institution self.annotation = Annotation.objects.filter( - owner=self.request.user.institution, value=pk, type=Annotation.Type.SYSTEM + owner=institution, + value=pk, + type=Annotation.Type.SYSTEM ).first() + if not self.annotation: - get_object_or_404(Annotation, pk=0, owner=self.request.user.institution) + raise Http404 + self.success_url = reverse_lazy('device:details', args=[pk]) kwargs = super().get_form_kwargs() return kwargs diff --git a/dhub/settings.py b/dhub/settings.py index e09fd13..bb0f874 100644 --- a/dhub/settings.py +++ b/dhub/settings.py @@ -29,7 +29,7 @@ SECRET_KEY = "django-insecure-1p8rs@qf$$l^!vsbetagojw23kw@1ez(qi8^(s0t!wyh!l3 # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='[]', cast=Csv()) # Application definition @@ -43,6 +43,7 @@ INSTALLED_APPS = [ "django.contrib.staticfiles", 'django_extensions', 'django_bootstrap5', + 'django_tables2', "rest_framework", "login", "user", @@ -54,6 +55,7 @@ INSTALLED_APPS = [ "documents", "dashboard", "admin", + "api", ] diff --git a/dhub/urls.py b/dhub/urls.py index e9cec23..fba9bd3 100644 --- a/dhub/urls.py +++ b/dhub/urls.py @@ -26,4 +26,5 @@ urlpatterns = [ path("admin/", include("admin.urls")), path("user/", include("user.urls")), path("lot/", include("lot.urls")), + path('api/', include('api.urls')), ] diff --git a/docker-compose.yml b/docker-compose.yml index b6e2d01..a106f6e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,8 @@ services: build: dockerfile: docker/devicehub-django.Dockerfile environment: - DEBUG: true + - DEBUG=true + - ALLOWED_HOSTS=* volumes: - .:/opt/devicehub-django ports: diff --git a/docker-reset.sh b/docker-reset.sh new file mode 100755 index 0000000..9264b93 --- /dev/null +++ b/docker-reset.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Copyright (c) 2024 Pedro +# SPDX-License-Identifier: AGPL-3.0-or-later + +set -e +set -u +# DEBUG +set -x + +main() { + # remove old database + sudo rm -vf db/* + docker compose down + docker compose build + docker compose up +} + +main "${@}" + +# written in emacs +# -*- mode: shell-script; -*- diff --git a/docker/devicehub-django.entrypoint.sh b/docker/devicehub-django.entrypoint.sh index 158406e..68e3760 100644 --- a/docker/devicehub-django.entrypoint.sh +++ b/docker/devicehub-django.entrypoint.sh @@ -21,7 +21,9 @@ deploy() { # inspired by https://medium.com/analytics-vidhya/django-with-docker-and-docker-compose-python-part-2-8415976470cc echo "INFO detected NEW deployment" ./manage.py migrate - ./manage.py add_user user@example.org 1234 + ./manage.py add_institution example-org + # TODO: one error on add_user, and you don't add user anymore + ./manage.py add_user example-org user@example.org 1234 fi } diff --git a/evidence/migrations/0004_alter_annotation_owner.py b/evidence/migrations/0004_alter_annotation_owner.py new file mode 100644 index 0000000..26c9bda --- /dev/null +++ b/evidence/migrations/0004_alter_annotation_owner.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.6 on 2024-09-18 10:55 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("evidence", "0003_alter_annotation_type"), + ("user", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="annotation", + name="owner", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="user.institution" + ), + ), + ] diff --git a/evidence/models.py b/evidence/models.py index 0e077a5..5a28481 100644 --- a/evidence/models.py +++ b/evidence/models.py @@ -1,8 +1,9 @@ import json +from dmidecode import DMIParse from django.db import models -from utils.constants import STR_SM_SIZE, STR_EXTEND_SIZE +from utils.constants import STR_SM_SIZE, STR_EXTEND_SIZE, CHASSIS_DH from evidence.xapian import search from user.models import User, Institution @@ -33,6 +34,7 @@ class Evidence: self.owner = None self.doc = None self.created = None + self.dmi = None self.annotations = [] self.get_owner() @@ -61,6 +63,11 @@ class Evidence: for xa in matches: self.doc = json.loads(xa.document.get_data()) + + if self.doc.get("software") == "EreuseWorkbench": + dmidecode_raw = self.doc["data"]["dmidecode"] + self.dmi = DMIParse(dmidecode_raw) + def get_time(self): if not self.doc: @@ -73,6 +80,32 @@ class Evidence: def components(self): return self.doc.get('components', []) + def get_manufacturer(self): + if self.doc.get("software") != "EreuseWorkbench": + return self.doc['device']['manufacturer'] + + return self.dmi.manufacturer().strip() + + def get_model(self): + if self.doc.get("software") != "EreuseWorkbench": + return self.doc['device']['model'] + + return self.dmi.model().strip() + + def get_chassis(self): + if self.doc.get("software") != "EreuseWorkbench": + return self.doc['device']['model'] + + chassis = self.dmi.get("Chassis")[0].get("Type", '_virtual') + lower_type = chassis.lower() + + for k, v in CHASSIS_DH.items(): + if lower_type in v: + return k + return "" + + + @classmethod def get_all(cls, user): return Annotation.objects.filter( diff --git a/evidence/parse.py b/evidence/parse.py index b329bcd..548040e 100644 --- a/evidence/parse.py +++ b/evidence/parse.py @@ -4,9 +4,29 @@ import shutil import hashlib from datetime import datetime +from dmidecode import DMIParse from evidence.xapian import index from evidence.models import Evidence, Annotation -from utils.constants import ALGOS +from utils.constants import ALGOS, CHASSIS_DH + + +def get_network_cards(child, nets): + if child['id'] == 'network': + nets.append(child) + if child.get('children'): + [get_network_cards(x, nets) for x in child['children']] + + +def get_mac(lshw): + nets = [] + + get_network_cards(json.loads(lshw), nets) + nets_sorted = sorted(nets, key=lambda x: x['businfo']) + # This funcion get the network card integrated in motherboard + # integrate = [x for x in nets if "pci@0000:00:" in x.get('businfo', '')] + + if nets_sorted: + return nets_sorted[0]['serial'] class Build: @@ -33,13 +53,17 @@ class Build: } def get_hid_14(self): - device = self.json['device'] - manufacturer = device.get("manufacturer", '') - model = device.get("model", '') - chassis = device.get("chassis", '') - serial_number = device.get("serialNumber", '') - sku = device.get("sku", '') - hid = f"{manufacturer}{model}{chassis}{serial_number}{sku}" + if self.json.get("software") == "EreuseWorkbench": + hid = self.get_hid(self.json) + else: + device = self.json['device'] + manufacturer = device.get("manufacturer", '') + model = device.get("model", '') + chassis = device.get("chassis", '') + serial_number = device.get("serialNumber", '') + sku = device.get("sku", '') + hid = f"{manufacturer}{model}{chassis}{serial_number}{sku}" + return hashlib.sha3_256(hid.encode()).hexdigest() def create_annotations(self): @@ -53,3 +77,40 @@ class Build: key=k, value=v ) + + def get_chassis_dh(self): + chassis = self.get_chassis() + lower_type = chassis.lower() + for k, v in CHASSIS_DH.items(): + if lower_type in v: + return k + return self.default + + def get_sku(self): + return self.dmi.get("System")[0].get("SKU Number", "n/a").strip() + + def get_chassis(self): + return self.dmi.get("Chassis")[0].get("Type", '_virtual') + + def get_hid(self, snapshot): + dmidecode_raw = snapshot["data"]["dmidecode"] + self.dmi = DMIParse(dmidecode_raw) + + manufacturer = self.dmi.manufacturer().strip() + model = self.dmi.model().strip() + chassis = self.get_chassis_dh() + serial_number = self.dmi.serial_number() + sku = self.get_sku() + + if not snapshot["data"].get('lshw'): + return f"{manufacturer}{model}{chassis}{serial_number}{sku}" + + lshw = snapshot["data"]["lshw"] + # mac = get_mac2(hwinfo_raw) or "" + mac = get_mac(lshw) or "" + if not mac: + print("WARNING!! No there are MAC address") + else: + print(f"{manufacturer}{model}{chassis}{serial_number}{sku}{mac}") + + return f"{manufacturer}{model}{chassis}{serial_number}{sku}{mac}" diff --git a/lot/migrations/0003_alter_lot_owner_alter_lotannotation_owner_and_more.py b/lot/migrations/0003_alter_lot_owner_alter_lotannotation_owner_and_more.py new file mode 100644 index 0000000..806c6f1 --- /dev/null +++ b/lot/migrations/0003_alter_lot_owner_alter_lotannotation_owner_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.0.6 on 2024-09-18 10:55 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("lot", "0002_lotannotation"), + ("user", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="lot", + name="owner", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="user.institution" + ), + ), + migrations.AlterField( + model_name="lotannotation", + name="owner", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="user.institution" + ), + ), + migrations.AlterField( + model_name="lottag", + name="owner", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="user.institution" + ), + ), + ] diff --git a/lot/models.py b/lot/models.py index 9078459..3926f29 100644 --- a/lot/models.py +++ b/lot/models.py @@ -6,14 +6,14 @@ from utils.constants import ( STR_EXTEND_SIZE, ) -from user.models import User +from user.models import Institution # from device.models import Device -from evidence.models import Annotation +# from evidence.models import Annotation class LotTag(models.Model): name = models.CharField(max_length=STR_SIZE, blank=False, null=False) - owner = models.ForeignKey(User, on_delete=models.CASCADE) + owner = models.ForeignKey(Institution, on_delete=models.CASCADE) def __str__(self): return self.name @@ -31,7 +31,7 @@ class Lot(models.Model): code = models.CharField(max_length=STR_SIZE, blank=True, null=True) description = models.CharField(max_length=STR_SIZE, blank=True, null=True) closed = models.BooleanField(default=True) - owner = models.ForeignKey(User, on_delete=models.CASCADE) + owner = models.ForeignKey(Institution, on_delete=models.CASCADE) type = models.ForeignKey(LotTag, on_delete=models.CASCADE) def add(self, v): @@ -52,7 +52,7 @@ class LotAnnotation(models.Model): created = models.DateTimeField(auto_now_add=True) lot = models.ForeignKey(Lot, on_delete=models.CASCADE) - owner = models.ForeignKey(User, on_delete=models.CASCADE) + owner = models.ForeignKey(Institution, on_delete=models.CASCADE) type = models.SmallIntegerField(choices=Type) key = models.CharField(max_length=STR_EXTEND_SIZE) value = models.CharField(max_length=STR_EXTEND_SIZE) diff --git a/lot/views.py b/lot/views.py index 280450e..46252c8 100644 --- a/lot/views.py +++ b/lot/views.py @@ -28,7 +28,7 @@ class NewLotView(DashboardView, CreateView): ) def form_valid(self, form): - form.instance.owner = self.request.user + form.instance.owner = self.request.user.institution response = super().form_valid(form) return response @@ -83,8 +83,8 @@ class AddToLotView(DashboardView, FormView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - lots = Lot.objects.filter(owner=self.request.user) - lot_tags = LotTag.objects.filter(owner=self.request.user) + lots = Lot.objects.filter(owner=self.request.user.institution) + lot_tags = LotTag.objects.filter(owner=self.request.user.institution) context.update({ 'lots': lots, 'lot_tags':lot_tags, @@ -93,7 +93,7 @@ class AddToLotView(DashboardView, FormView): def get_form(self): form = super().get_form() - form.fields["lots"].queryset = Lot.objects.filter(owner=self.request.user) + form.fields["lots"].queryset = Lot.objects.filter(owner=self.request.user.institution) return form def form_valid(self, form): @@ -123,10 +123,10 @@ class LotsTagsView(DashboardView, TemplateView): def get_context_data(self, **kwargs): self.pk = kwargs.get('pk') context = super().get_context_data(**kwargs) - tag = get_object_or_404(LotTag, owner=self.request.user, id=self.pk) + tag = get_object_or_404(LotTag, owner=self.request.user.institution, id=self.pk) self.title += " {}".format(tag.name) self.breadcrumb += " {}".format(tag.name) - lots = Lot.objects.filter(owner=self.request.user).filter(type=tag) + lots = Lot.objects.filter(owner=self.request.user.institution).filter(type=tag) context.update({ 'lots': lots, 'title': self.title, @@ -144,7 +144,7 @@ class LotAddDocumentView(DashboardView, CreateView): fields = ("key", "value") def form_valid(self, form): - form.instance.owner = self.request.user + form.instance.owner = self.request.user.institution form.instance.lot = self.lot form.instance.type = LotAnnotation.Type.DOCUMENT response = super().form_valid(form) @@ -152,7 +152,7 @@ class LotAddDocumentView(DashboardView, CreateView): def get_form_kwargs(self): pk = self.kwargs.get('pk') - self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user) + self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user.institution) self.success_url = reverse_lazy('lot:documents', args=[pk]) kwargs = super().get_form_kwargs() return kwargs @@ -166,10 +166,10 @@ class LotDocumentsView(DashboardView, TemplateView): def get_context_data(self, **kwargs): self.pk = kwargs.get('pk') context = super().get_context_data(**kwargs) - lot = get_object_or_404(Lot, owner=self.request.user, id=self.pk) + lot = get_object_or_404(Lot, owner=self.request.user.institution, id=self.pk) documents = LotAnnotation.objects.filter( lot=lot, - owner=self.request.user, + owner=self.request.user.institution, type=LotAnnotation.Type.DOCUMENT, ) context.update({ @@ -189,10 +189,10 @@ class LotAnnotationsView(DashboardView, TemplateView): def get_context_data(self, **kwargs): self.pk = kwargs.get('pk') context = super().get_context_data(**kwargs) - lot = get_object_or_404(Lot, owner=self.request.user, id=self.pk) + lot = get_object_or_404(Lot, owner=self.request.user.institution, id=self.pk) annotations = LotAnnotation.objects.filter( lot=lot, - owner=self.request.user, + owner=self.request.user.institution, type=LotAnnotation.Type.USER, ) context.update({ @@ -213,7 +213,7 @@ class LotAddAnnotationView(DashboardView, CreateView): fields = ("key", "value") def form_valid(self, form): - form.instance.owner = self.request.user + form.instance.owner = self.request.user.institution form.instance.lot = self.lot form.instance.type = LotAnnotation.Type.USER response = super().form_valid(form) @@ -221,7 +221,7 @@ class LotAddAnnotationView(DashboardView, CreateView): def get_form_kwargs(self): pk = self.kwargs.get('pk') - self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user) + self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user.institution) self.success_url = reverse_lazy('lot:annotations', args=[pk]) kwargs = super().get_form_kwargs() return kwargs diff --git a/requirements.txt b/requirements.txt index 9d4492f..217d120 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,9 +3,10 @@ Django==5.0.6 django-bootstrap5==24.2 django-extensions==3.2.3 djangorestframework==3.15.1 +django-tables2==2.6.0 python-decouple==3.3 py-dmidecode==0.1.3 pandas==2.2.2 xlrd==2.0.1 odfpy==1.4.1 - +pytz==2024.2 diff --git a/user/management/commands/add_institution.py b/user/management/commands/add_institution.py index 622dff8..b7a4a26 100644 --- a/user/management/commands/add_institution.py +++ b/user/management/commands/add_institution.py @@ -1,6 +1,6 @@ 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" @@ -9,4 +9,17 @@ class Command(BaseCommand): parser.add_argument('name', type=str, help='institution') def handle(self, *args, **kwargs): - Institution.objects.create(name=kwargs['name']) + self.institution = Institution.objects.create(name=kwargs['name']) + self.create_lot_tags() + + def create_lot_tags(self): + tags = [ + "Entrada", + "Salida", + "Temporal" + ] + for tag in tags: + LotTag.objects.create( + name=tag, + owner=self.institution + ) diff --git a/user/management/commands/add_user.py b/user/management/commands/add_user.py index f4cc780..d6d3bd8 100644 --- a/user/management/commands/add_user.py +++ b/user/management/commands/add_user.py @@ -1,6 +1,5 @@ from django.core.management.base import BaseCommand from django.contrib.auth import get_user_model -from lot.models import LotTag from user.models import Institution @@ -31,17 +30,5 @@ class Command(BaseCommand): password=password, is_admin=is_admin, ) - self.u.set_password(password) + self.u.set_password(self.password) self.u.save() - - def create_lot_tags(self): - tags = [ - "Entrada", - "Salida", - "Temporal" - ] - for tag in tags: - LotTag.objects.create( - name=tag, - owner=self.u - ) diff --git a/utils/constants.py b/utils/constants.py index 2fd9a13..e481d6c 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -20,3 +20,21 @@ HID_ALGO1 = [ ALGOS = { "hidalgo1": HID_ALGO1, } + + +CHASSIS_DH = { + 'Tower': {'desktop', 'low-profile', 'tower', 'server'}, + 'Docking': {'docking'}, + 'AllInOne': {'all-in-one'}, + 'Microtower': {'mini-tower', 'space-saving', 'mini'}, + 'PizzaBox': {'pizzabox'}, + 'Lunchbox': {'lunchbox'}, + 'Stick': {'stick'}, + 'Netbook': {'notebook', 'sub-notebook'}, + 'Handheld': {'handheld'}, + 'Laptop': {'portable', 'laptop'}, + 'Convertible': {'convertible'}, + 'Detachable': {'detachable'}, + 'Tablet': {'tablet'}, + 'Virtual': {'_virtual'}, +}