Compare commits

...

3 Commits

6 changed files with 231 additions and 0 deletions

20
admin/forms.py Normal file
View File

@ -0,0 +1,20 @@
from django import forms
from utils.device import create_annotation, create_doc, create_index
from utils.save_snapshots import move_json, save_in_disk
from django.forms import formset_factory
class CustomStatusLabelForm(forms.Form):
annotation_name = forms.CharField(
label="Annotation Name",
max_length=50,
widget=forms.TextInput(attrs={'class': 'form-control'})
)
annotation_state = forms.CharField(
label="Possible State",
max_length=50,
widget=forms.TextInput(attrs={'class': 'form-control'})
)
CustomStatusLabelFormSet = formset_factory(CustomStatusLabelForm, extra=1)

View File

@ -10,9 +10,15 @@
<div class="row">
<div class="col">
<a href="{% url 'admin:institution' user.institution.pk %}" class="btn btn-green-admin">
{% translate "Institution" %}
</a>
<a href="{% url 'admin:reserved'%}" class="btn btn-green-admin">
{% translate "Reserved Annotations" %}
</a>
</div>
</div>

View File

@ -0,0 +1,129 @@
{% extends "base.html" %}
{% load i18n %}
{% load django_bootstrap5 %}
{% block content %}
<div class="container mt-5">
<div class="row mb-4">
<div class="col">
<h3 class="text-center">{{ subtitle }}</h3>
</div>
</div>
<form role="form" method="post" novalidate>
{% csrf_token %}
<div class="mb-3">
{{ form.annotation_name.label_tag }}
{{ form.annotation_name }}
</div>
<h5 class="mt-4">{% translate "Possible States" %}</h5>
<div id="formset">
{{ formset.management_form }}
{% for form in formset %}
<div class="row mb-3 formset-form">
<div class="col-md-10">
{% bootstrap_field form.annotation_state show_label=False %}
</div>
<div class="col-md-2 d-flex align-items-center">
{% if forloop.first %}
<button type="button" class="btn btn-sm btn-success add-form">
&#43;
</button>
{% endif %}
<button type="button" class="btn btn-sm btn-danger remove-form ms-2">
&minus;
</button>
</div>
</div>
{% endfor %}
</div>
<div class="container">
<a class="btn btn-grey" href="{% url 'admin:panel' %}">{% translate "Cancel" %}</a>
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
</div>
</form>
</div>
<script>
//fix this stub chatgpt code
document.addEventListener('DOMContentLoaded', function() {
var formsetDiv = document.getElementById('formset');
var totalForms = document.getElementById('id_form-TOTAL_FORMS');
function updateElementIndex(el, prefix, index) {
var idRegex = new RegExp('(' + prefix + '-\\d+-)');
var replacement = prefix + '-' + index + '-';
if (el.id) el.id = el.id.replace(idRegex, replacement);
if (el.name) el.name = el.name.replace(idRegex, replacement);
}
function addForm(e) {
e.preventDefault();
var formCount = parseInt(totalForms.value);
var newForm = document.querySelector('.formset-form').cloneNode(true);
newForm.querySelectorAll('input').forEach(function(input) {
updateElementIndex(input, 'form', formCount);
input.value = '';
});
// Remove any hidden inputs (management form fields)
newForm.querySelectorAll('input[type="hidden"]').forEach(function(hiddenInput) {
hiddenInput.remove();
});
// Attach event listeners to the new buttons
var addButton = newForm.querySelector('.add-form');
var removeButton = newForm.querySelector('.remove-form');
addButton.addEventListener('click', addForm);
removeButton.addEventListener('click', removeForm);
// Ensure only the first form has the add button
formsetDiv.querySelectorAll('.add-form').forEach(function(button, index) {
if (index > 0) {
button.remove();
}
});
formsetDiv.appendChild(newForm);
totalForms.value = formCount + 1;
}
function removeForm(e) {
e.preventDefault();
var formToRemove = e.target.closest('.formset-form');
formToRemove.remove();
var forms = formsetDiv.querySelectorAll('.formset-form');
totalForms.value = forms.length;
// Re-index form elements
forms.forEach(function(form, index) {
form.querySelectorAll('input').forEach(function(input) {
updateElementIndex(input, 'form', index);
});
});
// Ensure only the first form has the add button
formsetDiv.querySelectorAll('.add-form').forEach(function(button, index) {
if (index > 0) {
button.remove();
}
});
}
// Initial event listeners
document.querySelectorAll('.add-form').forEach(function(button) {
button.addEventListener('click', addForm);
});
document.querySelectorAll('.remove-form').forEach(function(button) {
button.addEventListener('click', removeForm);
});
});
</script>
{% endblock %}

View File

@ -10,4 +10,5 @@ urlpatterns = [
path("users/edit/<int:pk>", views.EditUserView.as_view(), name="edit_user"),
path("users/delete/<int:pk>", views.DeleteUserView.as_view(), name="delete_user"),
path("institution/<int:pk>", views.InstitutionView.as_view(), name="institution"),
path("reserved", views.AddReservedAnnotationView.as_view(), name="reserved"),
]

View File

@ -8,9 +8,15 @@ from django.views.generic.edit import (
UpdateView,
DeleteView,
)
from django.views.generic import FormView
import logging
from dashboard.mixins import DashboardView, Http403
from user.models import User, Institution
from admin.email import NotifyActivateUserByEmail
from admin.forms import CustomStatusLabelForm, CustomStatusLabelFormSet
from evidence.models import Annotation
logger = logging.getLogger('dhub')
class AdminView(DashboardView):
@ -124,3 +130,46 @@ class InstitutionView(AdminView, UpdateView):
self.object = self.request.user.institution
kwargs = super().get_form_kwargs()
return kwargs
class AddReservedAnnotationView(AdminView, FormView):
template_name = "reserved.html"
title = _("New Custom State Labels")
breadcrumb = "Admin / Custom State Labels (new name?)"
success_url = reverse_lazy('admin:panel')
model = Annotation
form_class = CustomStatusLabelForm
formset_class = CustomStatusLabelFormSet
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['formset'] = self.formset_class(self.request.POST)
else:
context['formset'] = self.formset_class()
context['subtitle'] = _("Add Custom Status Label")
return context
def form_valid(self, form):
context = self.get_context_data()
formset = context['formset']
if formset.is_valid():
annotation_name = form.cleaned_data['annotation_name']
logger.info("Saving to db: " + annotation_name)
annotation = Annotation.objects.create(name=annotation_name)
for form in formset:
state = form.cleaned_data.get('annotation_state')
if state:
PossibleValue.objects.create(annotation=annotation, value=state)
logger.info("Saved to db: " + annotation_name)
self.success_message = _("Custom status label has been added.")
self.success_url = reverse_lazy('admin:panel')
return super().form_valid(form)
else:
logger.error("Formset is not valid")
logger.error(formset.errors)
return self.form_invalid(form)

View File

@ -15,6 +15,7 @@ class Annotation(models.Model):
USER = 1, "User"
DOCUMENT = 2, "Document"
ERASE_SERVER = 3, "EraseServer"
ADMIN = 4, "Admin"
created = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
@ -28,7 +29,32 @@ class Annotation(models.Model):
constraints = [
models.UniqueConstraint(fields=["type", "key", "uuid"], name="unique_type_key_uuid")
]
#TODO: check if this works properly
def clean(self):
super().clean()
if self.type == self.Type.ADMIN:
if Annotation.objects.filter(type=self.Type.ADMIN, key=self.key).exists():
raise ValidationError(f"The key '{self.key}' is already reserved by admin.")
else:
if Annotation.objects.filter(type=self.Type.ADMIN, key=self.key).exists():
raise ValidationError(f"The key '{self.key}' is reserved by admin and cannot be used.")
class AllowedValue(models.Model):
annotation = models.ForeignKey(Annotation, on_delete=models.CASCADE, limit_choices_to={'type': Annotation.Type.ADMIN})
value = models.CharField(max_length=50)
def __str__(self):
return self.value
#This represents a change on a System-type Annotation
class Action(models.Model):
annotation = models.ForeignKey(Annotation, on_delete=models.CASCADE, limit_choices_to={'type': Annotation.Type.ADMIN})
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(Institution, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
comment = models.CharField(max_length=250)
class Evidence:
def __init__(self, uuid):