Merge pull request 'Added pagination and column sorting to the admin dashboard table' (#80) from tables2-admin-dashboard into main

Reviewed-on: https://gitea.pangea.org/trustchain-oc1-orchestral/IdHub/pulls/80
This commit is contained in:
cayop 2023-11-27 10:53:43 +00:00
commit 5eb606c4a4
12 changed files with 287 additions and 30 deletions

28
idhub/admin/tables.py Normal file
View file

@ -0,0 +1,28 @@
import django_tables2 as tables
from idhub.models import Rol, Event
from idhub_auth.models import User
class UserTable(tables.Table):
class Meta:
model = User
template_name = "idhub/custom_table.html"
fields = ("first_name", "last_name", "email", "is_active", "is_admin")
class RolesTable(tables.Table):
class Meta:
model = Rol
template_name = "idhub/custom_table.html"
fields = ("name", "description")
class DashboardTable(tables.Table):
type = tables.Column(verbose_name="Event")
message = tables.Column(verbose_name="Description")
created = tables.Column(verbose_name="Date")
class Meta:
model = Event
template_name = "idhub/custom_table.html"
fields = ("type", "message", "created")

View file

@ -5,6 +5,7 @@ import pandas as pd
from pathlib import Path from pathlib import Path
from jsonschema import validate from jsonschema import validate
from smtplib import SMTPException from smtplib import SMTPException
from django_tables2 import SingleTableView
from django.conf import settings from django.conf import settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -30,6 +31,9 @@ from idhub.admin.forms import (
SchemaForm, SchemaForm,
UserRolForm, UserRolForm,
) )
from idhub.admin.tables import (
DashboardTable
)
from idhub.models import ( from idhub.models import (
DID, DID,
Event, Event,
@ -43,19 +47,15 @@ from idhub.models import (
) )
class DashboardView(AdminView, TemplateView): class DashboardView(AdminView, SingleTableView):
template_name = "idhub/admin/dashboard.html" template_name = "idhub/admin/dashboard.html"
table_class = DashboardTable
title = _('Dashboard') title = _('Dashboard')
subtitle = _('Events') subtitle = _('Events')
icon = 'bi bi-bell' icon = 'bi bi-bell'
section = "Home" section = "Home"
model = Event
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'events': Event.objects.filter(user=None),
})
return context
class People(AdminView): class People(AdminView):
title = _("User management") title = _("User management")

View file

@ -1,29 +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-grey border border-dark">{% trans 'Event' %}</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 'Date' %}</button></th>
</tr>
</thead>
<tbody>
{% for ev in events %}
<tr>
<td>{{ ev.get_type_name }}</td>
<td>{{ ev.message }}</td>
<td>{{ ev.created }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %} {% endblock %}

View file

@ -0,0 +1,98 @@
{% 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 %}>
{% if p == '...' %}
<a type="button" class="btn btn-grey border border-dark" href="#">{{ p }}</a>
{% else %}
<a type="button" class="btn btn-grey border border-dark" href="{% querystring table.prefixed_page_field=p %}">
{{ p }}
</a>
{% endif %}
</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 %}

View file

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

0
idhub/tests/__init__.py Normal file
View file

View file

@ -0,0 +1,17 @@
from django.test import TestCase
from idhub.models import Event
from idhub_auth.models import User
class EventModelTest(TestCase):
@classmethod
def setUpTestData(cls):
user = User.objects.create(email='testuser@example.org')
Event.objects.create(message='Test Event', type=1, user=user)
def test_event_creation(self):
event = Event.objects.get(id=1)
self.assertEqual(event.message, 'Test Event')
self.assertEqual(event.get_type_name(), 'User registered')
# Add more tests for other model methods and properties

View file

@ -0,0 +1,65 @@
from datetime import datetime
from django.test import TestCase
from django.urls import reverse
from idhub_auth.models import User
from idhub.admin.tables import DashboardTable
from idhub.models import Event
class AdminDashboardTableTest(TestCase):
def setUp(self):
self.admin_user = User.objects.create_superuser(
email='adminuser@example.org',
password='adminpass12')
@classmethod
def setUpTestData(cls):
# Creating test events with different dates
Event.objects.create(message='Event 1', type=1,
created=datetime(2023, 1, 3))
Event.objects.create(message='Event 2', type=2,
created=datetime(2023, 1, 2))
Event.objects.create(message='Event 3', type=3,
created=datetime(2023, 1, 25))
def test_sorting_by_date(self):
# Create the table
table = DashboardTable(Event.objects.all())
# Apply sorting
table.order_by = 'created'
# Fetch the sorted records
sorted_records = list(table.rows)
# Verify the order is as expected
self.assertTrue(sorted_records[0].record.created
< sorted_records[1].record.created)
self.assertTrue(sorted_records[1].record.created
< sorted_records[2].record.created)
def test_table_in_template(self):
self.client.login(email='adminuser@example.org', password='adminpass12')
response = self.client.get(reverse('idhub:admin_dashboard'))
self.assertTemplateUsed(response, 'idhub/custom_table.html')
def test_table_data(self):
Event.objects.create(message="test_event", type=2)
Event.objects.create(message="test_event", type=9)
table = DashboardTable(Event.objects.all())
self.assertTrue(isinstance(table, DashboardTable))
self.assertEqual(len(table.rows), Event.objects.count())
def test_table_columns(self):
table = DashboardTable(Event.objects.all())
expected_columns = ['type', 'message', 'created']
for column in expected_columns:
self.assertIn(column, table.columns)
def test_pagination(self):
# TODO
pass

View file

@ -0,0 +1,18 @@
from django.urls import reverse
from django.test import Client, TestCase
from idhub_auth.models import User
class TemplateTest(TestCase):
def setUp(self):
self.client = Client()
self.admin_user = User.objects.create_superuser(
email='adminuser@example.org',
password='adminpass12')
def test_dashboard_template(self):
self.client.login(email='adminuser@example.org', password='adminpass12')
response = self.client.get(reverse('idhub:admin_dashboard'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'idhub/base_admin.html')

50
idhub/tests/test_views.py Normal file
View file

@ -0,0 +1,50 @@
from django.urls import reverse
from django.test import TestCase
from idhub_auth.models import User
class AdminDashboardViewTest(TestCase):
def setUp(self):
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')
def test_view_url_exists_at_desired_location(self):
response = self.client.get('/admin/dashboard/', follow=True)
self.assertEqual(response.status_code, 200)
def test_view_redirects_to_login_when_not_authenticated(self):
response = self.client.get(reverse("idhub:admin_dashboard"),
follow=True)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'auth/login.html')
def test_view_redirects_on_incorrect_login_attempt(self):
self.client.login(email='adminuser@example.org', password='wrongpass')
response = self.client.get(reverse('idhub:admin_dashboard'))
self.assertEqual(response.status_code, 302)
def test_view_redirects_to_login_on_incorrect_login_attempt(self):
self.client.login(email='adminuser@example.org', password='wrongpass')
response = self.client.get(reverse('idhub:admin_dashboard'),
follow=True)
self.assertTemplateUsed(response, 'auth/login.html')
def test_login_admin_user(self):
self.client.login(email='adminuser@example.org', password='adminpass12')
response = self.client.get(reverse('idhub:admin_dashboard'))
self.assertEqual(response.status_code, 200)
def test_view_uses_correct_template(self):
self.client.login(email='adminuser@example.org', password='adminpass12')
response = self.client.get(reverse('idhub:admin_dashboard'))
self.assertTemplateUsed(response, 'idhub/admin/dashboard.html')

View file

@ -1,6 +1,7 @@
django==4.2.5 django==4.2.5
django-bootstrap5==23.3 django-bootstrap5==23.3
django-extensions==3.2.3 django-extensions==3.2.3
django-tables2==2.6.0
black==23.9.1 black==23.9.1
python-decouple==3.8 python-decouple==3.8
jsonschema==4.19.1 jsonschema==4.19.1

View file

@ -70,6 +70,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django_extensions', 'django_extensions',
'django_bootstrap5', 'django_bootstrap5',
'django_tables2',
'idhub_auth', 'idhub_auth',
'idhub' 'idhub'
] ]