Merge pull request 'Added pagination and sorting by column to the admin tables' (#87) from extend-tables into release

Reviewed-on: https://gitea.pangea.org/trustchain-oc1-orchestral/IdHub/pulls/87
This commit is contained in:
cayop 2024-01-04 19:48:02 +00:00
commit 8316e6d5a0
15 changed files with 617 additions and 209 deletions

View File

@ -34,7 +34,7 @@ class ImportForm(forms.Form):
(x.did, x.label) for x in DID.objects.filter(user=self.user) (x.did, x.label) for x in DID.objects.filter(user=self.user)
] ]
self.fields['schema'].choices = [ self.fields['schema'].choices = [
(x.id, x.name()) for x in Schemas.objects.filter() (x.id, x.name) for x in Schemas.objects.filter()
] ]
def clean_did(self): def clean_did(self):

View File

@ -1,25 +1,255 @@
import django_tables2 as tables import django_tables2 as tables
from django.utils.translation import gettext_lazy as _ from django.utils.html import format_html
from idhub.models import Rol, Event
from idhub.models import (
Rol,
Event,
Service,
VerificableCredential,
DID,
File_datas,
Schemas
)
from idhub_auth.models import User from idhub_auth.models import User
class ButtonColumn(tables.Column):
attrs = {
"a": {
"type": "button",
"class": "text-primary",
}
}
# 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-eye"></i>')
class UserTable(tables.Table): class UserTable(tables.Table):
view_user = ButtonColumn(
linkify={
"viewname": "idhub:admin_people",
"args": [tables.A("pk")]
},
orderable=False
)
membership = tables.Column(empty_values=())
role = tables.Column(empty_values=())
def render_view_user(self):
return format_html('<i class="bi bi-eye"></i>')
def render_membership(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: class Meta:
model = User model = User
template_name = "idhub/custom_table.html" template_name = "idhub/custom_table.html"
fields = ("first_name", "last_name", "email", "is_active", "is_admin") fields = ("last_name", "first_name", "email", "membership", "role",
"view_user")
class RolesTable(tables.Table): class RolesTable(tables.Table):
view_role = ButtonColumn(
linkify={
"viewname": "idhub:admin_rol_edit",
"args": [tables.A("pk")]
},
orderable=False
)
delete_role = ButtonColumn(
linkify={
"viewname": "idhub:admin_rol_del",
"args": [tables.A("pk")]
},
orderable=False
)
def render_view_role(self):
return format_html('<i class="bi bi-pencil-square"></i>')
def render_delete_role(self):
return format_html('<i class="bi bi-trash">')
class Meta: class Meta:
model = Rol model = Rol
template_name = "idhub/custom_table.html" template_name = "idhub/custom_table.html"
fields = ("name", "description") fields = ("name", "description")
class ServicesTable(tables.Table):
domain = tables.Column(verbose_name="Service")
role = tables.Column(empty_values=())
edit_service = ButtonColumn(
linkify={
"viewname": "idhub:admin_service_edit",
"args": [tables.A("pk")]
},
orderable=False
)
delete_service = ButtonColumn(
linkify={
"viewname": "idhub:admin_service_del",
"args": [tables.A("pk")]
},
orderable=False
)
def render_role(self, record):
return record.get_roles()
def render_edit_service(self):
return format_html('<i class="bi bi-pencil-square"></i>')
def render_delete_service(self):
return format_html('<i class="bi bi-trash">')
def order_role(self, queryset, is_descending):
queryset = queryset.order_by(
("-" if is_descending else "") + "rol"
)
return (queryset, True)
class Meta:
model = Service
template_name = "idhub/custom_table.html"
fields = ("domain", "description", "role",
"edit_service", "delete_service")
class DashboardTable(tables.Table): class DashboardTable(tables.Table):
class Meta: class Meta:
model = Event model = Event
template_name = "idhub/custom_table.html" template_name = "idhub/custom_table.html"
fields = ("type", "message", "created") fields = ("type", "message", "created")
class CredentialTable(tables.Table):
type = tables.Column(empty_values=())
# Pending VerificableCredential description fix
details = tables.Column(empty_values=(), orderable=False)
issued_on = tables.Column(verbose_name="Issued")
view_credential = ButtonColumn(
linkify={
"viewname": "idhub:admin_credential",
"args": [tables.A("pk")]
},
orderable=False
)
def render_type(self, record):
return record.type()
def render_details(self, record):
return record.description()
def render_view_credential(self):
return format_html('<i class="bi bi-eye"></i>')
def order_type(self, queryset, is_descending):
queryset = queryset.order_by(
("-" if is_descending else "") + "schema__type"
)
return (queryset, True)
class Meta:
model = VerificableCredential
template_name = "idhub/custom_table.html"
fields = ("type", "details", "issued_on", "status", "user")
class DIDTable(tables.Table):
created_at = tables.Column(verbose_name="Date")
did = tables.Column(verbose_name="ID")
edit_did = ButtonColumn(
linkify={
"viewname": "idhub:admin_dids_edit",
"args": [tables.A("pk")]
},
orderable=False,
verbose_name="Edit DID"
)
delete_template_code = """<a class="text-danger"
href="javascript:void()"
data-bs-toggle="modal"
data-bs-target="#confirm-delete-{{ record.id }}"
title="Remove"
><i class="bi bi-trash"></i></a>"""
delete_did = tables.TemplateColumn(template_code=delete_template_code,
orderable=False,
verbose_name="Delete DID")
def render_edit_did(self):
return format_html('<i class="bi bi-pencil-square"></i>')
class Meta:
model = DID
template_name = "idhub/custom_table.html"
fields = ("created_at", "label", "did", "edit_did", "delete_did")
class DataTable(tables.Table):
created_at = tables.Column(verbose_name="Date")
file_name = tables.Column(verbose_name="File")
class Meta:
model = File_datas
template_name = "idhub/custom_table.html"
fields = ("created_at", "file_name", "success")
class TemplateTable(tables.Table):
view_schema = ButtonColumn(
linkify={
"viewname": "idhub:admin_schemas_download",
"args": [tables.A("pk")]
},
orderable=False
)
delete_template_code = """<a class="text-danger"
href="javascript:void()"
data-bs-toggle="modal"
data-bs-target="#confirm-delete-{{ record.id }}"
title="Remove"
><i class="bi bi-trash"></i></a>"""
delete_schema = tables.TemplateColumn(template_code=delete_template_code,
orderable=False,
verbose_name="Delete schema")
_name = tables.Column(verbose_name="Name")
_description = tables.Column(verbose_name="Description")
class Meta:
model = Schemas
template_name = "idhub/custom_table.html"
fields = ("created_at", "file_schema", "_name", "_description",
"view_schema", "delete_schema")

View File

@ -32,7 +32,14 @@ from idhub.admin.forms import (
UserRolForm, UserRolForm,
) )
from idhub.admin.tables import ( from idhub.admin.tables import (
DashboardTable DashboardTable,
UserTable,
RolesTable,
ServicesTable,
CredentialTable,
DIDTable,
DataTable,
TemplateTable
) )
from idhub.models import ( from idhub.models import (
DID, DID,
@ -82,10 +89,12 @@ class ImportExport(AdminView):
section = "ImportExport" section = "ImportExport"
class PeopleListView(People, TemplateView): class PeopleListView(People, SingleTableView):
template_name = "idhub/admin/people.html" template_name = "idhub/admin/people.html"
subtitle = _('View users') subtitle = _('View users')
icon = 'bi bi-person' icon = 'bi bi-person'
table_class = UserTable
model = User
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -94,6 +103,11 @@ class PeopleListView(People, TemplateView):
}) })
return context return context
def get_queryset(self, **kwargs):
queryset = super().get_queryset(**kwargs)
return queryset
class PeopleView(People, TemplateView): class PeopleView(People, TemplateView):
template_name = "idhub/admin/user.html" template_name = "idhub/admin/user.html"
@ -398,17 +412,20 @@ class PeopleRolDeleteView(PeopleView):
return redirect('idhub:admin_people_edit', user.id) return redirect('idhub:admin_people_edit', user.id)
class RolesView(AccessControl): class RolesView(AccessControl, SingleTableView):
template_name = "idhub/admin/roles.html" template_name = "idhub/admin/roles.html"
subtitle = _('Manage roles') subtitle = _('Manage roles')
table_class = RolesTable
icon = '' icon = ''
model = Rol
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) queryset = kwargs.pop('object_list', None)
context.update({ if queryset is None:
'roles': Rol.objects, self.object_list = self.model.objects.all()
})
return context return super().get_context_data(**kwargs)
class RolRegisterView(AccessControl, CreateView): class RolRegisterView(AccessControl, CreateView):
template_name = "idhub/admin/rol_register.html" template_name = "idhub/admin/rol_register.html"
@ -461,17 +478,20 @@ class RolDeleteView(AccessControl):
return redirect('idhub:admin_roles') return redirect('idhub:admin_roles')
class ServicesView(AccessControl): class ServicesView(AccessControl, SingleTableView):
template_name = "idhub/admin/services.html" template_name = "idhub/admin/services.html"
table_class = ServicesTable
subtitle = _('Manage services') subtitle = _('Manage services')
icon = '' icon = ''
model = Service
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) queryset = kwargs.pop('object_list', None)
context.update({ if queryset is None:
'services': Service.objects, self.object_list = self.model.objects.all()
})
return context return super().get_context_data(**kwargs)
class ServiceRegisterView(AccessControl, CreateView): class ServiceRegisterView(AccessControl, CreateView):
template_name = "idhub/admin/service_register.html" template_name = "idhub/admin/service_register.html"
@ -534,17 +554,19 @@ class ServiceDeleteView(AccessControl):
return redirect('idhub:admin_services') return redirect('idhub:admin_services')
class CredentialsView(Credentials): class CredentialsView(Credentials, SingleTableView):
template_name = "idhub/admin/credentials.html" template_name = "idhub/admin/credentials.html"
table_class = CredentialTable
subtitle = _('View credentials') subtitle = _('View credentials')
icon = '' icon = ''
model = VerificableCredential
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) queryset = kwargs.pop('object_list', None)
context.update({ if queryset is None:
'credentials': VerificableCredential.objects, self.object_list = self.model.objects.all()
})
return context return super().get_context_data(**kwargs)
class CredentialView(Credentials): class CredentialView(Credentials):
@ -620,19 +642,26 @@ class DeleteCredentialsView(Credentials):
return redirect(self.success_url) return redirect(self.success_url)
class DidsView(Credentials): class DidsView(Credentials, SingleTableView):
template_name = "idhub/admin/dids.html" template_name = "idhub/admin/dids.html"
table_class = DIDTable
subtitle = _('Manage identities (DID)') subtitle = _('Manage identities (DID)')
icon = 'bi bi-patch-check-fill' icon = 'bi bi-patch-check-fill'
wallet = True wallet = True
model = DID
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
queryset = kwargs.pop('object_list', None)
if queryset is None:
self.object_list = self.model.objects.all()
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context.update({ context.update({
'dids': DID.objects.filter(user=self.request.user), 'dids': DID.objects.filter(user=self.request.user),
}) })
return context return context
class DidRegisterView(Credentials, CreateView): class DidRegisterView(Credentials, CreateView):
template_name = "idhub/admin/did_register.html" template_name = "idhub/admin/did_register.html"
subtitle = _('Add a new organizational identity (DID)') subtitle = _('Add a new organizational identity (DID)')
@ -702,17 +731,19 @@ class WalletConfigIssuesView(Credentials):
wallet = True wallet = True
class SchemasView(SchemasMix): class SchemasView(SchemasMix, SingleTableView):
template_name = "idhub/admin/schemas.html" template_name = "idhub/admin/schemas.html"
table_class = TemplateTable
subtitle = _('View credential templates') subtitle = _('View credential templates')
icon = '' icon = ''
model = Schemas
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) queryset = kwargs.pop('object_list', None)
context.update({ if queryset is None:
'schemas': Schemas.objects, self.object_list = self.model.objects.all()
})
return context return super().get_context_data(**kwargs)
class SchemasDeleteView(SchemasMix): class SchemasDeleteView(SchemasMix):
@ -840,10 +871,12 @@ class SchemasImportAddView(SchemasMix):
return data return data
class ImportView(ImportExport, TemplateView): class ImportView(ImportExport, SingleTableView):
template_name = "idhub/admin/import.html" template_name = "idhub/admin/import.html"
table_class = DataTable
subtitle = _('Import data') subtitle = _('Import data')
icon = '' icon = ''
model = File_datas
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -888,4 +921,3 @@ class ImportAddView(ImportExport, FormView):
else: else:
messages.error(self.request, _("Error importing the file!")) messages.error(self.request, _("Error importing the file!"))
return super().form_valid(form) return super().form_valid(form)

View File

@ -0,0 +1,87 @@
# Generated by Django 4.2.5 on 2024-01-03 18:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('idhub', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='schemas',
name='description',
field=models.CharField(max_length=250, null=True),
),
migrations.AddField(
model_name='schemas',
name='name',
field=models.CharField(max_length=250, null=True),
),
migrations.AlterField(
model_name='did',
name='label',
field=models.CharField(max_length=50, verbose_name='Label'),
),
migrations.AlterField(
model_name='event',
name='created',
field=models.DateTimeField(auto_now=True, verbose_name='Date'),
),
migrations.AlterField(
model_name='event',
name='message',
field=models.CharField(max_length=350, verbose_name='Description'),
),
migrations.AlterField(
model_name='event',
name='type',
field=models.PositiveSmallIntegerField(
choices=[
(1, 'User registered'),
(2, 'User welcomed'),
(3, 'Data update requested by user'),
(4, 'Data update requested. Pending approval by administrator'),
(5, "User's data updated by admin"),
(6, 'Your data updated by admin'),
(7, 'User deactivated by admin'),
(8, 'DID created by user'),
(9, 'DID created'),
(10, 'DID deleted'),
(11, 'Credential deleted by user'),
(12, 'Credential deleted'),
(13, 'Credential issued for user'),
(14, 'Credential issued'),
(15, 'Credential presented by user'),
(16, 'Credential presented'),
(17, 'Credential enabled'),
(18, 'Credential available'),
(19, 'Credential revoked by admin'),
(20, 'Credential revoked'),
(21, 'Role created by admin'),
(22, 'Role modified by admin'),
(23, 'Role deleted by admin'),
(24, 'Service created by admin'),
(25, 'Service modified by admin'),
(26, 'Service deleted by admin'),
(27, 'Organisational DID created by admin'),
(28, 'Organisational DID deleted by admin'),
(29, 'User deactivated'),
(30, 'User activated'),
],
verbose_name='Event',
),
),
migrations.AlterField(
model_name='userrol',
name='service',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='users',
to='idhub.service',
verbose_name='Service',
),
),
]

View File

@ -436,6 +436,8 @@ class Schemas(models.Model):
file_schema = models.CharField(max_length=250) file_schema = models.CharField(max_length=250)
data = models.TextField() data = models.TextField()
created_at = models.DateTimeField(auto_now=True) created_at = models.DateTimeField(auto_now=True)
_name = models.CharField(max_length=250, null=True, db_column='name')
_description = models.CharField(max_length=250, null=True, db_column='description')
@property @property
def get_schema(self): def get_schema(self):
@ -443,12 +445,33 @@ class Schemas(models.Model):
return {} return {}
return json.loads(self.data) return json.loads(self.data)
def _update_and_get_field(self, field_attr, schema_key):
field_value = getattr(self, field_attr)
if not field_value:
field_value = self.get_schema.get(schema_key, '')
self._update_model_field(field_attr, field_value)
return field_value
def _update_model_field(self, field_attr, field_value):
if field_value:
setattr(self, field_attr, field_value)
self.save(update_fields=[field_attr])
@property
def name(self): def name(self):
return self.get_schema.get('name', '') return self._update_and_get_field('_name', 'name')
@name.setter
def name(self, value):
self._name = value
@property
def description(self): def description(self):
return self.get_schema.get('description', '') return self._update_and_get_field('_description', 'description')
@description.setter
def description(self, value):
self._description = value
class VerificableCredential(models.Model): class VerificableCredential(models.Model):
""" """

View File

@ -1,39 +1,11 @@
{% extends "idhub/base_admin.html" %} {% extends "idhub/base_admin.html" %}
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %}
{% block content %} {% block content %}
<h3> <h3>
<i class="{{ icon }}"></i> <i class="{{ icon }}"></i>
{{ subtitle }} {{ subtitle }}
</h3> </h3>
<div class="row mt-5"> {% render_table table %}
<div class="col">
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Type' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Details' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Issued' %}</button></th>
<th scope="col" class="text-center"><button type="button" class="btn btn-grey border border-dark">{% trans 'Status' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'User' %}</button></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for f in credentials.all %}
<tr style="font-size:15px;">
<td>{{ f.type }}</td>
<td>{{ f.description }}</td>
<td>{{ f.get_issued_on }}</td>
<td class="text-center">{{ f.get_status }}</td>
<td>{{ f.user.email }}</td>
<td><a href="{% url 'idhub:admin_credential' f.id %}" class="text-primary" title="{% trans 'View' %}"><i class="bi bi-eye"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,5 +1,6 @@
{% extends "idhub/base_admin.html" %} {% extends "idhub/base_admin.html" %}
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %}
{% block content %} {% block content %}
<h3> <h3>
@ -8,6 +9,8 @@
</h3> </h3>
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
{% render_table table %}
{% comment %}
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-sm"> <table class="table table-striped table-sm">
<thead> <thead>
@ -31,6 +34,7 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endcomment %}
<div class="form-actions-no-box"> <div class="form-actions-no-box">
<a class="btn btn-green-admin" href="{% url 'idhub:admin_dids_new' %}">{% translate "Add identity" %} <i class="bi bi-plus"></i></a> <a class="btn btn-green-admin" href="{% url 'idhub:admin_dids_new' %}">{% translate "Add identity" %} <i class="bi bi-plus"></i></a>
</div> </div>

View File

@ -1,36 +1,14 @@
{% extends "idhub/base_admin.html" %} {% extends "idhub/base_admin.html" %}
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %}
{% block content %} {% block content %}
<h3> <h3>
<i class="{{ icon }}"></i> <i class="{{ icon }}"></i>
{{ subtitle }} {{ subtitle }}
</h3> </h3>
<div class="row mt-5"> {% render_table table %}
<div class="col"> <div class="form-actions-no-box">
<div class="table-responsive"> <a class="btn btn-green-admin" href="{% url 'idhub:admin_import_add' %}">{% translate "Import data" %} <i class="bi bi-plus"></i></a>
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Created' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'File' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Success' %}</button></th>
</tr>
</thead>
<tbody>
{% for f in dates.all %}
<tr style="font-size:15px;">
<td>{{ f.created_at }}</td>
<td>{{ f.file_name }}</td>
<td>{% if f.success %}<i class="bi bi-check-circle text-primary"></i>{% else %}<i class="bi bi-x-circle text-danger"></i>{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="form-actions-no-box">
<a class="btn btn-green-admin" href="{% url 'idhub:admin_import_add' %}">{% translate "Import data" %} <i class="bi bi-plus"></i></a>
</div>
</div>
</div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,39 +1,11 @@
{% extends "idhub/base_admin.html" %} {% extends "idhub/base_admin.html" %}
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %}
{% block content %} {% block content %}
<h3> <h3>
<i class="{{ icon }}"></i> <i class="{{ icon }}"></i>
{{ subtitle }} {{ subtitle }}
</h3> </h3>
<div class="table-responsive"> {% render_table table %}
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col"><button type="button" class="btn btn-green-admin border border-dark">{% trans 'Last name' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'First name' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">Email</button></th>
<th scope="col" class="text-center"><button type="button" class="btn btn-grey border border-dark">{% trans 'Membership' %}</button></th>
<th scope="col" class="text-center"><button type="button" class="btn btn-grey border border-dark">{% trans 'Role' %}</button></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.last_name|default:'' }}</td>
<td>{{ user.first_name|default:'' }}</td>
<td>{{ user.email }}</td>
<td class="text-center">
{{ user.get_memberships }}
</td>
<td class="text-center">
{{ user.get_roles }}
</td>
<td><a type="button" class="text-primary" href="{% url 'idhub:admin_people' user.id %}" title="{% trans 'View' %}"><i class="bi bi-eye"></i></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %} {% endblock %}

View File

@ -1,5 +1,6 @@
{% extends "idhub/base_admin.html" %} {% extends "idhub/base_admin.html" %}
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %}
{% block content %} {% block content %}
<h3> <h3>
@ -8,31 +9,10 @@
</h3> </h3>
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
<div class="table-responsive"> {% render_table table %}
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Role' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Description' %}</button></th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for rol in roles.all %}
<tr>
<td>{{ rol.name }}</td>
<td>{{ rol.description|default:""}}</td>
<td><a href="{% url 'idhub:admin_rol_edit' rol.id %}" title="{% trans 'Edit' %}"><i class="bi bi-pencil-square"></i></a></td>
<td><a class="text-danger" href="{% url 'idhub:admin_rol_del' rol.id %}" title="{% trans 'Delete' %}"><i class="bi bi-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="form-actions-no-box"> <div class="form-actions-no-box">
<a class="btn btn-green-admin" href="{% url 'idhub:admin_rol_new' %}">{% translate "Add Role" %} <i class="bi bi-plus"></i></a> <a class="btn btn-green-admin" href="{% url 'idhub:admin_rol_new' %}">{% translate "Add Role" %} <i class="bi bi-plus"></i></a>
</div> </div>
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,47 +1,19 @@
{% extends "idhub/base_admin.html" %} {% extends "idhub/base_admin.html" %}
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %}
{% block content %} {% block content %}
<h3> <h3>
<i class="{{ icon }}"></i> <i class="{{ icon }}"></i>
{{ subtitle }} {{ subtitle }}
</h3> </h3>
<div class="row mt-5"> {% render_table table %}
<div class="col"> <div class="form-actions-no-box">
<div class="table-responsive"> <a class="btn btn-green-admin" href="{% url 'idhub:admin_schemas_import' %}">{% translate "Add template" %} <i class="bi bi-plus"></i></a>
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Created' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Template file' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Name' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Description' %}</button></th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for schema in schemas.all %}
<tr style="font-size:15px;">
<td>{{ schema.created_at }}</td>
<td>{{ schema.file_schema }}</td>
<td>{{ schema.name }}</td>
<td>{{ schema.description }}</td>
<td><a class="text-primary" href="{% url 'idhub:admin_schemas_download' schema.id %}" target="_blank" title="{% trans 'View' %}"><i class="bi bi-eye"></i></a></td>
<td><a class="text-danger" href="jacascript:void()" data-bs-toggle="modal" data-bs-target="#confirm-delete-{{ schema.id }}" title="{% trans 'Remove' %}"><i class="bi bi-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="form-actions-no-box">
<a class="btn btn-green-admin" href="{% url 'idhub:admin_schemas_import' %}">{% translate "Add template" %} <i class="bi bi-plus"></i></a>
</div>
</div>
</div>
</div> </div>
<!-- Modal --> <!-- Modal -->
{% for schema in schemas.all %} {% for schema in object_list %}
<div class="modal" id="confirm-delete-{{ schema.id}}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal" id="confirm-delete-{{ schema.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">

View File

@ -1,5 +1,6 @@
{% extends "idhub/base_admin.html" %} {% extends "idhub/base_admin.html" %}
{% load i18n %} {% load i18n %}
{% load render_table from django_tables2 %}
{% block content %} {% block content %}
<h3> <h3>
@ -8,33 +9,10 @@
</h3> </h3>
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
<div class="table-responsive"> {% render_table table %}
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Service' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Description' %}</button></th>
<th scope="col"><button type="button" class="btn btn-grey border border-dark">{% trans 'Role' %}</button></th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for service in services.all %}
<tr>
<td>{{ service.domain }}</td>
<td>{{ service.description }}</td>
<td>{{ service.get_roles }}</td>
<td><a href="{% url 'idhub:admin_service_edit' service.id %}" title="{% trans 'Edit' %}"><i class="bi bi-pencil-square"></i></a></td>
<td><a class="text-danger" href="{% url 'idhub:admin_service_del' service.id %}" title="{% trans 'Delete' %}"><i class="bi bi-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="form-actions-no-box"> <div class="form-actions-no-box">
<a class="btn btn-green-admin" href="{% url 'idhub:admin_service_new' %}">{% translate "Add service" %} <i class="bi bi-plus"></i></a> <a class="btn btn-green-admin" href="{% url 'idhub:admin_service_new' %}">{% translate "Add service" %} <i class="bi bi-plus"></i></a>
</div> </div>
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,5 +1,5 @@
from django.test import TestCase from django.test import TestCase
from idhub.models import Event from idhub.models import Event, Membership, Rol, UserRol, Service
from idhub_auth.models import User from idhub_auth.models import User
@ -14,4 +14,60 @@ class EventModelTest(TestCase):
self.assertEqual(event.message, 'Test Event') self.assertEqual(event.message, 'Test Event')
self.assertEqual(event.get_type_name(), 'User registered') self.assertEqual(event.get_type_name(), 'User registered')
# Add more tests for other model methods and properties
class UserTest(TestCase):
"""
Tests the very basic aspects of the User model,
like field properties and methods behaving as expected.
Further testing is recommended.
"""
def setUp(self):
self.user = User.objects.create(
email="test@example.com",
is_admin=True,
first_name="Dummy",
last_name="Dummyson"
)
def test_field_properties(self):
user = User.objects.get(email="test@example.com")
self.assertEqual(user._meta.get_field('email').max_length, 255)
self.assertTrue(user._meta.get_field('email').unique)
self.assertTrue(user._meta.get_field('is_active').default)
self.assertFalse(user._meta.get_field('is_admin').default)
def test_string_representation(self):
self.assertEqual(str(self.user), "test@example.com")
def test_has_perm(self):
self.assertTrue(self.user.has_perm(None))
def test_has_module_perms(self):
self.assertTrue(self.user.has_module_perms(None))
def test_is_staff_property(self):
self.assertTrue(self.user.is_staff)
def test_get_memberships(self):
Membership.objects.create(user=self.user,
type=Membership.Types.BENEFICIARY)
Membership.objects.create(user=self.user,
type=Membership.Types.EMPLOYEE)
# We test for the length because the order in which the string
# is given in get_memberships is non-deterministic
self.assertEqual(len(self.user.get_memberships()),
len("Beneficiary, Employee"))
def test_get_roles(self):
user = User.objects.get(email="test@example.com")
service = Service.objects.create(domain="Test Service")
role1 = Rol.objects.create(name="Role 1")
role2 = Rol.objects.create(name="Role 2")
service.rol.add(role1, role2)
UserRol.objects.create(user=user, service=service)
# We test for the length because the order in which the string
# is given in get_roles is non-deterministic
self.assertEqual(len(user.get_roles()), len("Role 1, Role 2"))

View File

@ -1,11 +1,13 @@
from datetime import datetime from datetime import datetime
from unittest.mock import MagicMock
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from django.core.exceptions import FieldError
from idhub_auth.models import User from idhub_auth.models import User
from idhub.admin.tables import DashboardTable from idhub.admin.tables import DashboardTable, UserTable, TemplateTable
from idhub.models import Event from idhub.models import Event, Membership, Rol, UserRol, Service, Schemas
class AdminDashboardTableTest(TestCase): class AdminDashboardTableTest(TestCase):
@ -63,3 +65,82 @@ class AdminDashboardTableTest(TestCase):
def test_pagination(self): def test_pagination(self):
# TODO # TODO
pass pass
class UserTableTest(TestCase):
def setUp(self):
self.user1 = User.objects.create(email="user1@example.com")
self.user2 = User.objects.create(email="user2@example.com")
Membership.objects.create(user=self.user1,
type=Membership.Types.BENEFICIARY)
# Set up roles and services
service = Service.objects.create(domain="Test Service")
role = Rol.objects.create(name="Role 1")
service.rol.add(role)
UserRol.objects.create(user=self.user1, service=service)
self.table = UserTable(User.objects.all())
def test_membership_column_render(self):
# Get the user instance for the first row
user = self.table.rows[0].record
# Use the render_membership method of UserTable
rendered_column = self.table.columns['membership'].render(user)
self.assertIn("Beneficiary", str(rendered_column))
def test_role_column_render(self):
# Get the user instance for the first row
user = self.table.rows[0].record
# Use the render_role method of UserTable
rendered_column = self.table.columns['role'].render(user)
self.assertIn("Role 1", str(rendered_column))
class TemplateTableTest(TestCase):
def setUp(self):
self.table = TemplateTable(Schemas.objects.all())
self.create_schemas(amount=3)
def create_schemas(self, amount):
for i in range(amount):
self.create_schemas_object("testname" + str(i), "testdesc" + str(i))
def create_schemas_object(self, name, description):
data = self.format_data_for_json_reader(name, description)
Schemas.objects.create(
type="testy",
file_schema="filey",
data=data,
created_at=datetime.now()
)
def format_data_for_json_reader(self, name, description):
return '{"name": "'+name+'", "description": "'+description+'"}'
def test_order_table_by_name_throws_no_exception(self):
try:
# Apply sorting
self.table.order_by = 'name'
except FieldError:
self.fail("Ordering template table by name raised FieldError")
def test_order_table_by_name_correctly_orders(self):
table = TemplateTable(Schemas.objects.all(), order_by="name")
# Fetch the sorted records
sorted_records = list(table.rows)
# Verify the order is as expected
self.assertLess(sorted_records[0].record.name,
sorted_records[1].record.name)
self.assertLess(sorted_records[1].record.name,
sorted_records[2].record.name)
def test_order_table_by_description_works(self):
try:
# Apply sorting
self.table.order_by = 'description'
except FieldError:
self.fail("Ordering template table by description raised FieldError")

View File

@ -1,7 +1,8 @@
from django.urls import reverse from django.urls import reverse
from django.test import TestCase from django.test import TestCase, RequestFactory
from idhub_auth.models import User from idhub_auth.models import User
from idhub.admin.views import PeopleListView
class AdminDashboardViewTest(TestCase): class AdminDashboardViewTest(TestCase):
@ -38,13 +39,55 @@ class AdminDashboardViewTest(TestCase):
self.assertTemplateUsed(response, 'auth/login.html') self.assertTemplateUsed(response, 'auth/login.html')
def test_login_admin_user(self): def test_login_admin_user(self):
self.client.login(email='adminuser@example.org', password='adminpass12') self.client.login(email='adminuser@example.org',
password='adminpass12')
response = self.client.get(reverse('idhub:admin_dashboard')) response = self.client.get(reverse('idhub:admin_dashboard'))
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_view_uses_correct_template(self): def test_view_uses_correct_template(self):
self.client.login(email='adminuser@example.org', password='adminpass12') self.client.login(email='adminuser@example.org',
password='adminpass12')
response = self.client.get(reverse('idhub:admin_dashboard')) response = self.client.get(reverse('idhub:admin_dashboard'))
self.assertTemplateUsed(response, 'idhub/admin/dashboard.html') self.assertTemplateUsed(response, 'idhub/admin/dashboard.html')
class PeopleListViewTest(TestCase):
def setUp(self):
# Set up a RequestFactory to create mock requests
self.factory = RequestFactory()
# Create some user instances for testing
self.user = User.objects.create_user(email='normaluser@example.org',
password='testpass12')
self.admin_user = User.objects.create_superuser(
email='adminuser@example.org',
password='adminpass12')
# Create a request object for the view
self.request = self.factory.get(reverse('idhub:admin_people_list'))
self.request.user = self.admin_user
def test_template_used(self):
response = PeopleListView.as_view()(self.request)
self.assertEqual(response.template_name[0], "idhub/admin/people.html")
def test_context_data(self):
response = PeopleListView.as_view()(self.request)
self.assertIn('users', response.context_data)
# Assuming 2 users were created
self.assertEqual(len(response.context_data['users']), 2)
def test_get_queryset(self):
view = PeopleListView()
view.setup(self.request)
queryset = view.get_queryset()
# Assuming 2 users in the database
self.assertEqual(queryset.count(), 2)