user-panel #9
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
3
api/admin.py
Normal file
3
api/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
api/apps.py
Normal file
6
api/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ApiConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'api'
|
1
api/forms.py
Normal file
1
api/forms.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
39
api/migrations/0001_initial.py
Normal file
39
api/migrations/0001_initial.py
Normal file
|
@ -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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
0
api/migrations/__init__.py
Normal file
0
api/migrations/__init__.py
Normal file
9
api/models.py
Normal file
9
api/models.py
Normal file
|
@ -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)
|
67
api/tables.py
Normal file
67
api/tables.py
Normal file
|
@ -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('<i class="bi bi-trash"></i>')
|
||||||
|
|
||||||
|
|
||||||
|
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('<i class="bi bi-eye"></i>')
|
||||||
|
|
||||||
|
# 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")
|
||||||
|
|
100
api/templates/custom_table.html
Normal file
100
api/templates/custom_table.html
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
{% load django_tables2 %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block table-wrapper %}
|
||||||
|
<div class="table-container">
|
||||||
|
{% block table %}
|
||||||
|
<table class= "table table-striped table-sm">
|
||||||
|
{% block table.thead %}
|
||||||
|
{% if table.show_header %}
|
||||||
|
<thead {{ table.attrs.thead.as_html }}>
|
||||||
|
<tr>
|
||||||
|
{% for column in table.columns %}
|
||||||
|
<th scope="col" {{ column.attrs.th.as_html }}>
|
||||||
|
{% if column.orderable %}
|
||||||
|
<a type="button" class="btn btn-grey border border-dark"
|
||||||
|
href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a>
|
||||||
|
{% else %}
|
||||||
|
{{ column.header }}
|
||||||
|
{% endif %}
|
||||||
|
</th>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock table.thead %}
|
||||||
|
{% block table.tbody %}
|
||||||
|
<tbody {{ table.attrs.tbody.as_html }}>
|
||||||
|
{% for row in table.paginated_rows %}
|
||||||
|
{% block table.tbody.row %}
|
||||||
|
<tr {{ row.attrs.as_html }}>
|
||||||
|
{% for column, cell in row.items %}
|
||||||
|
<td {{ column.attrs.td.as_html }}>{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endblock table.tbody.row %}
|
||||||
|
{% empty %}
|
||||||
|
{% if table.empty_text %}
|
||||||
|
{% block table.tbody.empty_text %}
|
||||||
|
<tr><td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td></tr>
|
||||||
|
{% endblock table.tbody.empty_text %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
{% endblock table.tbody %}
|
||||||
|
{% block table.tfoot %}
|
||||||
|
{% if table.has_footer %}
|
||||||
|
<tfoot {{ table.attrs.tfoot.as_html }}>
|
||||||
|
<tr>
|
||||||
|
{% for column in table.columns %}
|
||||||
|
<td {{ column.attrs.tf.as_html }}>{{ column.footer }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock table.tfoot %}
|
||||||
|
</table>
|
||||||
|
{% endblock table %}
|
||||||
|
|
||||||
|
{% block pagination %}
|
||||||
|
{% if table.page and table.paginator.num_pages > 1 %}
|
||||||
|
<ul class="pagination">
|
||||||
|
{% if table.page.has_previous %}
|
||||||
|
{% block pagination.previous %}
|
||||||
|
<li class="previous">
|
||||||
|
<a type="button" class="btn btn-grey border border-dark" href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">
|
||||||
|
{% trans 'Previous' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endblock pagination.previous %}
|
||||||
|
{% endif %}
|
||||||
|
{% if table.page.has_previous or table.page.has_next %}
|
||||||
|
{% block pagination.range %}
|
||||||
|
{% for p in table.page|table_page_range:table.paginator %}
|
||||||
|
<li {% if p == table.page.number %}class="active"{% endif %}>
|
||||||
|
<a type="button" class="btn btn-grey{% if p == table.page.number %}-selected{% endif %}
|
||||||
|
border border-dark"
|
||||||
|
{% if p == '...' %}
|
||||||
|
href="#">
|
||||||
|
{% else %}
|
||||||
|
href="{% querystring table.prefixed_page_field=p %}">
|
||||||
|
{% endif %}
|
||||||
|
{{ p }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock pagination.range %}
|
||||||
|
{% endif %}
|
||||||
|
{% if table.page.has_next %}
|
||||||
|
{% block pagination.next %}
|
||||||
|
<li class="next">
|
||||||
|
<a type="button" class="btn btn-grey border border-dark" href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">
|
||||||
|
{% trans 'Next' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endblock pagination.next %}
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock pagination %}
|
||||||
|
</div>
|
||||||
|
{% endblock table-wrapper %}
|
14
api/templates/token.html
Normal file
14
api/templates/token.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load render_table from django_tables2 %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>
|
||||||
|
<i class="{{ icon }}"></i>
|
||||||
|
{{ subtitle }}
|
||||||
|
</h3>
|
||||||
|
{% render_table table %}
|
||||||
|
<div class="form-actions-no-box">
|
||||||
|
<a class="btn btn-green-admin" href="{% url 'api:new_token' %}">{% translate "Generate a new token" %} <i class="bi bi-plus"></i></a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
13
api/urls.py
Normal file
13
api/urls.py
Normal file
|
@ -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/<int:pk>/del', views.TokenDeleteView.as_view(), name='delete_token'),
|
||||||
|
]
|
114
api/views.py
Normal file
114
api/views.py
Normal file
|
@ -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')
|
||||||
|
|
|
@ -39,7 +39,7 @@ class DashboardView(LoginRequiredMixin):
|
||||||
'section': self.section,
|
'section': self.section,
|
||||||
'path': resolve(self.request.path).url_name,
|
'path': resolve(self.request.path).url_name,
|
||||||
'user': self.request.user,
|
'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
|
return context
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class DetailsMixin(DashboardView, TemplateView):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.pk = kwargs['pk']
|
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)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
|
@ -163,6 +163,12 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link fw-bold" href="{% url 'api:tokens' %}">
|
||||||
|
<i class="bi-menu-button-wide icon_sidebar"></i>
|
||||||
|
{% trans 'Tokens' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -113,14 +113,14 @@ class Device:
|
||||||
self.lots = [x.lot for x in DeviceLot.objects.filter(device_id=self.id)]
|
self.lots = [x.lot for x in DeviceLot.objects.filter(device_id=self.id)]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_unassigned(cls, user, offset=0, limit=None):
|
def get_unassigned(cls, institution, offset=0, limit=None):
|
||||||
|
|
||||||
sql = """
|
sql = """
|
||||||
SELECT DISTINCT t1.value from evidence_annotation as t1
|
SELECT DISTINCT t1.value from evidence_annotation as t1
|
||||||
left join lot_devicelot as t2 on t1.value = t2.device_id
|
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(
|
""".format(
|
||||||
user=user.id,
|
institution=institution.id,
|
||||||
type=Annotation.Type.SYSTEM,
|
type=Annotation.Type.SYSTEM,
|
||||||
)
|
)
|
||||||
if limit:
|
if limit:
|
||||||
|
@ -134,19 +134,19 @@ class Device:
|
||||||
annotations = cursor.fetchall()
|
annotations = cursor.fetchall()
|
||||||
|
|
||||||
devices = [cls(id=x[0]) for x in annotations]
|
devices = [cls(id=x[0]) for x in annotations]
|
||||||
count = cls.get_unassigned_count(user)
|
count = cls.get_unassigned_count(institution)
|
||||||
return devices, count
|
return devices, count
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_unassigned_count(cls, user):
|
def get_unassigned_count(cls, institution):
|
||||||
|
|
||||||
sql = """
|
sql = """
|
||||||
SELECT count(DISTINCT t1.value) from evidence_annotation as t1
|
SELECT count(DISTINCT t1.value) from evidence_annotation as t1
|
||||||
left join lot_devicelot as t2 on t1.value = t2.device_id
|
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(
|
""".format(
|
||||||
user=user.id,
|
institution=institution.id,
|
||||||
type=Annotation.Type.SYSTEM,
|
type=Annotation.Type.SYSTEM,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -170,22 +170,17 @@ class Device:
|
||||||
def manufacturer(self):
|
def manufacturer(self):
|
||||||
if not self.last_evidence:
|
if not self.last_evidence:
|
||||||
self.get_last_evidence()
|
self.get_last_evidence()
|
||||||
return self.last_evidence.doc['device']['manufacturer']
|
return self.last_evidence.get_manufacturer()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
if not self.last_evidence:
|
if not self.last_evidence:
|
||||||
self.get_last_evidence()
|
self.get_last_evidence()
|
||||||
return self.last_evidence.doc['device']['type']
|
return self.last_evidence.get_chassis()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model(self):
|
def model(self):
|
||||||
if not self.last_evidence:
|
if not self.last_evidence:
|
||||||
self.get_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']
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from django.http import Http404
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
@ -98,7 +99,7 @@ class DetailsView(DashboardView, TemplateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
self.object.initial()
|
self.object.initial()
|
||||||
lot_tags = LotTag.objects.filter(owner=self.request.user)
|
lot_tags = LotTag.objects.filter(owner=self.request.user.institution)
|
||||||
context.update({
|
context.update({
|
||||||
'object': self.object,
|
'object': self.object,
|
||||||
'snapshot': self.object.get_last_evidence(),
|
'snapshot': self.object.get_last_evidence(),
|
||||||
|
@ -119,20 +120,22 @@ class AddAnnotationView(DashboardView, CreateView):
|
||||||
form.instance.owner = self.request.user.institution
|
form.instance.owner = self.request.user.institution
|
||||||
form.instance.user = self.request.user
|
form.instance.user = self.request.user
|
||||||
form.instance.uuid = self.annotation.uuid
|
form.instance.uuid = self.annotation.uuid
|
||||||
form.instance.uuid = self.annotation.uuid
|
|
||||||
form.instance.type = Annotation.Type.USER
|
form.instance.type = Annotation.Type.USER
|
||||||
response = super().form_valid(form)
|
response = super().form_valid(form)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
pk = self.kwargs.get('pk')
|
pk = self.kwargs.get('pk')
|
||||||
|
institution = self.request.user.institution
|
||||||
self.annotation = Annotation.objects.filter(
|
self.annotation = Annotation.objects.filter(
|
||||||
owner=self.request.user.institution,
|
owner=institution,
|
||||||
value=pk,
|
value=pk,
|
||||||
type=Annotation.Type.SYSTEM
|
type=Annotation.Type.SYSTEM
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if not self.annotation:
|
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])
|
self.success_url = reverse_lazy('device:details', args=[pk])
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
@ -156,11 +159,16 @@ class AddDocumentView(DashboardView, CreateView):
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
pk = self.kwargs.get('pk')
|
pk = self.kwargs.get('pk')
|
||||||
|
institution = self.request.user.institution
|
||||||
self.annotation = Annotation.objects.filter(
|
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()
|
).first()
|
||||||
|
|
||||||
if not self.annotation:
|
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])
|
self.success_url = reverse_lazy('device:details', args=[pk])
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
|
@ -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!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='[]', cast=Csv())
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
@ -43,6 +43,7 @@ INSTALLED_APPS = [
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
'django_bootstrap5',
|
'django_bootstrap5',
|
||||||
|
'django_tables2',
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
"login",
|
"login",
|
||||||
"user",
|
"user",
|
||||||
|
@ -54,6 +55,7 @@ INSTALLED_APPS = [
|
||||||
"documents",
|
"documents",
|
||||||
"dashboard",
|
"dashboard",
|
||||||
"admin",
|
"admin",
|
||||||
|
"api",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,4 +26,5 @@ urlpatterns = [
|
||||||
path("admin/", include("admin.urls")),
|
path("admin/", include("admin.urls")),
|
||||||
path("user/", include("user.urls")),
|
path("user/", include("user.urls")),
|
||||||
path("lot/", include("lot.urls")),
|
path("lot/", include("lot.urls")),
|
||||||
|
path('api/', include('api.urls')),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,8 @@ services:
|
||||||
build:
|
build:
|
||||||
dockerfile: docker/devicehub-django.Dockerfile
|
dockerfile: docker/devicehub-django.Dockerfile
|
||||||
environment:
|
environment:
|
||||||
DEBUG: true
|
- DEBUG=true
|
||||||
|
- ALLOWED_HOSTS=*
|
||||||
volumes:
|
volumes:
|
||||||
- .:/opt/devicehub-django
|
- .:/opt/devicehub-django
|
||||||
ports:
|
ports:
|
||||||
|
|
22
docker-reset.sh
Executable file
22
docker-reset.sh
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Copyright (c) 2024 Pedro <copyright@cas.cat>
|
||||||
|
# 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; -*-
|
|
@ -21,7 +21,9 @@ deploy() {
|
||||||
# inspired by https://medium.com/analytics-vidhya/django-with-docker-and-docker-compose-python-part-2-8415976470cc
|
# inspired by https://medium.com/analytics-vidhya/django-with-docker-and-docker-compose-python-part-2-8415976470cc
|
||||||
echo "INFO detected NEW deployment"
|
echo "INFO detected NEW deployment"
|
||||||
./manage.py migrate
|
./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
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
evidence/migrations/0004_alter_annotation_owner.py
Normal file
22
evidence/migrations/0004_alter_annotation_owner.py
Normal file
|
@ -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"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,8 +1,9 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from dmidecode import DMIParse
|
||||||
from django.db import models
|
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 evidence.xapian import search
|
||||||
from user.models import User, Institution
|
from user.models import User, Institution
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ class Evidence:
|
||||||
self.owner = None
|
self.owner = None
|
||||||
self.doc = None
|
self.doc = None
|
||||||
self.created = None
|
self.created = None
|
||||||
|
self.dmi = None
|
||||||
self.annotations = []
|
self.annotations = []
|
||||||
|
|
||||||
self.get_owner()
|
self.get_owner()
|
||||||
|
@ -62,6 +64,11 @@ class Evidence:
|
||||||
for xa in matches:
|
for xa in matches:
|
||||||
self.doc = json.loads(xa.document.get_data())
|
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):
|
def get_time(self):
|
||||||
if not self.doc:
|
if not self.doc:
|
||||||
self.get_doc()
|
self.get_doc()
|
||||||
|
@ -73,6 +80,32 @@ class Evidence:
|
||||||
def components(self):
|
def components(self):
|
||||||
return self.doc.get('components', [])
|
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
|
@classmethod
|
||||||
def get_all(cls, user):
|
def get_all(cls, user):
|
||||||
return Annotation.objects.filter(
|
return Annotation.objects.filter(
|
||||||
|
|
|
@ -4,9 +4,29 @@ import shutil
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from dmidecode import DMIParse
|
||||||
from evidence.xapian import index
|
from evidence.xapian import index
|
||||||
from evidence.models import Evidence, Annotation
|
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:
|
class Build:
|
||||||
|
@ -33,6 +53,9 @@ class Build:
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_hid_14(self):
|
def get_hid_14(self):
|
||||||
|
if self.json.get("software") == "EreuseWorkbench":
|
||||||
|
hid = self.get_hid(self.json)
|
||||||
|
else:
|
||||||
device = self.json['device']
|
device = self.json['device']
|
||||||
manufacturer = device.get("manufacturer", '')
|
manufacturer = device.get("manufacturer", '')
|
||||||
model = device.get("model", '')
|
model = device.get("model", '')
|
||||||
|
@ -40,6 +63,7 @@ class Build:
|
||||||
serial_number = device.get("serialNumber", '')
|
serial_number = device.get("serialNumber", '')
|
||||||
sku = device.get("sku", '')
|
sku = device.get("sku", '')
|
||||||
hid = f"{manufacturer}{model}{chassis}{serial_number}{sku}"
|
hid = f"{manufacturer}{model}{chassis}{serial_number}{sku}"
|
||||||
|
|
||||||
return hashlib.sha3_256(hid.encode()).hexdigest()
|
return hashlib.sha3_256(hid.encode()).hexdigest()
|
||||||
|
|
||||||
def create_annotations(self):
|
def create_annotations(self):
|
||||||
|
@ -53,3 +77,40 @@ class Build:
|
||||||
key=k,
|
key=k,
|
||||||
value=v
|
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}"
|
||||||
|
|
|
@ -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"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -6,14 +6,14 @@ from utils.constants import (
|
||||||
STR_EXTEND_SIZE,
|
STR_EXTEND_SIZE,
|
||||||
)
|
)
|
||||||
|
|
||||||
from user.models import User
|
from user.models import Institution
|
||||||
# from device.models import Device
|
# from device.models import Device
|
||||||
from evidence.models import Annotation
|
# from evidence.models import Annotation
|
||||||
|
|
||||||
|
|
||||||
class LotTag(models.Model):
|
class LotTag(models.Model):
|
||||||
name = models.CharField(max_length=STR_SIZE, blank=False, null=False)
|
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):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -31,7 +31,7 @@ class Lot(models.Model):
|
||||||
code = models.CharField(max_length=STR_SIZE, blank=True, null=True)
|
code = models.CharField(max_length=STR_SIZE, blank=True, null=True)
|
||||||
description = 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)
|
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)
|
type = models.ForeignKey(LotTag, on_delete=models.CASCADE)
|
||||||
|
|
||||||
def add(self, v):
|
def add(self, v):
|
||||||
|
@ -52,7 +52,7 @@ class LotAnnotation(models.Model):
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
lot = models.ForeignKey(Lot, on_delete=models.CASCADE)
|
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)
|
type = models.SmallIntegerField(choices=Type)
|
||||||
key = models.CharField(max_length=STR_EXTEND_SIZE)
|
key = models.CharField(max_length=STR_EXTEND_SIZE)
|
||||||
value = models.CharField(max_length=STR_EXTEND_SIZE)
|
value = models.CharField(max_length=STR_EXTEND_SIZE)
|
||||||
|
|
28
lot/views.py
28
lot/views.py
|
@ -28,7 +28,7 @@ class NewLotView(DashboardView, CreateView):
|
||||||
)
|
)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.owner = self.request.user
|
form.instance.owner = self.request.user.institution
|
||||||
response = super().form_valid(form)
|
response = super().form_valid(form)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -83,8 +83,8 @@ class AddToLotView(DashboardView, FormView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
lots = Lot.objects.filter(owner=self.request.user)
|
lots = Lot.objects.filter(owner=self.request.user.institution)
|
||||||
lot_tags = LotTag.objects.filter(owner=self.request.user)
|
lot_tags = LotTag.objects.filter(owner=self.request.user.institution)
|
||||||
context.update({
|
context.update({
|
||||||
'lots': lots,
|
'lots': lots,
|
||||||
'lot_tags':lot_tags,
|
'lot_tags':lot_tags,
|
||||||
|
@ -93,7 +93,7 @@ class AddToLotView(DashboardView, FormView):
|
||||||
|
|
||||||
def get_form(self):
|
def get_form(self):
|
||||||
form = super().get_form()
|
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
|
return form
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
@ -123,10 +123,10 @@ class LotsTagsView(DashboardView, TemplateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
self.pk = kwargs.get('pk')
|
self.pk = kwargs.get('pk')
|
||||||
context = super().get_context_data(**kwargs)
|
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.title += " {}".format(tag.name)
|
||||||
self.breadcrumb += " {}".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({
|
context.update({
|
||||||
'lots': lots,
|
'lots': lots,
|
||||||
'title': self.title,
|
'title': self.title,
|
||||||
|
@ -144,7 +144,7 @@ class LotAddDocumentView(DashboardView, CreateView):
|
||||||
fields = ("key", "value")
|
fields = ("key", "value")
|
||||||
|
|
||||||
def form_valid(self, form):
|
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.lot = self.lot
|
||||||
form.instance.type = LotAnnotation.Type.DOCUMENT
|
form.instance.type = LotAnnotation.Type.DOCUMENT
|
||||||
response = super().form_valid(form)
|
response = super().form_valid(form)
|
||||||
|
@ -152,7 +152,7 @@ class LotAddDocumentView(DashboardView, CreateView):
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
pk = self.kwargs.get('pk')
|
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])
|
self.success_url = reverse_lazy('lot:documents', args=[pk])
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
@ -166,10 +166,10 @@ class LotDocumentsView(DashboardView, TemplateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
self.pk = kwargs.get('pk')
|
self.pk = kwargs.get('pk')
|
||||||
context = super().get_context_data(**kwargs)
|
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(
|
documents = LotAnnotation.objects.filter(
|
||||||
lot=lot,
|
lot=lot,
|
||||||
owner=self.request.user,
|
owner=self.request.user.institution,
|
||||||
type=LotAnnotation.Type.DOCUMENT,
|
type=LotAnnotation.Type.DOCUMENT,
|
||||||
)
|
)
|
||||||
context.update({
|
context.update({
|
||||||
|
@ -189,10 +189,10 @@ class LotAnnotationsView(DashboardView, TemplateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
self.pk = kwargs.get('pk')
|
self.pk = kwargs.get('pk')
|
||||||
context = super().get_context_data(**kwargs)
|
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(
|
annotations = LotAnnotation.objects.filter(
|
||||||
lot=lot,
|
lot=lot,
|
||||||
owner=self.request.user,
|
owner=self.request.user.institution,
|
||||||
type=LotAnnotation.Type.USER,
|
type=LotAnnotation.Type.USER,
|
||||||
)
|
)
|
||||||
context.update({
|
context.update({
|
||||||
|
@ -213,7 +213,7 @@ class LotAddAnnotationView(DashboardView, CreateView):
|
||||||
fields = ("key", "value")
|
fields = ("key", "value")
|
||||||
|
|
||||||
def form_valid(self, form):
|
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.lot = self.lot
|
||||||
form.instance.type = LotAnnotation.Type.USER
|
form.instance.type = LotAnnotation.Type.USER
|
||||||
response = super().form_valid(form)
|
response = super().form_valid(form)
|
||||||
|
@ -221,7 +221,7 @@ class LotAddAnnotationView(DashboardView, CreateView):
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
pk = self.kwargs.get('pk')
|
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])
|
self.success_url = reverse_lazy('lot:annotations', args=[pk])
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
|
@ -3,9 +3,10 @@ Django==5.0.6
|
||||||
django-bootstrap5==24.2
|
django-bootstrap5==24.2
|
||||||
django-extensions==3.2.3
|
django-extensions==3.2.3
|
||||||
djangorestframework==3.15.1
|
djangorestframework==3.15.1
|
||||||
|
django-tables2==2.6.0
|
||||||
python-decouple==3.3
|
python-decouple==3.3
|
||||||
py-dmidecode==0.1.3
|
py-dmidecode==0.1.3
|
||||||
pandas==2.2.2
|
pandas==2.2.2
|
||||||
xlrd==2.0.1
|
xlrd==2.0.1
|
||||||
odfpy==1.4.1
|
odfpy==1.4.1
|
||||||
|
pytz==2024.2
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from user.models import Institution
|
from user.models import Institution
|
||||||
|
from lot.models import LotTag
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Create a new Institution"
|
help = "Create a new Institution"
|
||||||
|
@ -9,4 +9,17 @@ class Command(BaseCommand):
|
||||||
parser.add_argument('name', type=str, help='institution')
|
parser.add_argument('name', type=str, help='institution')
|
||||||
|
|
||||||
def handle(self, *args, **kwargs):
|
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
|
||||||
|
)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from lot.models import LotTag
|
|
||||||
from user.models import Institution
|
from user.models import Institution
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,17 +30,5 @@ class Command(BaseCommand):
|
||||||
password=password,
|
password=password,
|
||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
)
|
)
|
||||||
self.u.set_password(password)
|
self.u.set_password(self.password)
|
||||||
self.u.save()
|
self.u.save()
|
||||||
|
|
||||||
def create_lot_tags(self):
|
|
||||||
tags = [
|
|
||||||
"Entrada",
|
|
||||||
"Salida",
|
|
||||||
"Temporal"
|
|
||||||
]
|
|
||||||
for tag in tags:
|
|
||||||
LotTag.objects.create(
|
|
||||||
name=tag,
|
|
||||||
owner=self.u
|
|
||||||
)
|
|
||||||
|
|
|
@ -20,3 +20,21 @@ HID_ALGO1 = [
|
||||||
ALGOS = {
|
ALGOS = {
|
||||||
"hidalgo1": HID_ALGO1,
|
"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'},
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue