diff --git a/.gitea/workflows/ci-pipeline.yaml b/.gitea/workflows/ci-pipeline.yaml new file mode 100644 index 0000000..44eece3 --- /dev/null +++ b/.gitea/workflows/ci-pipeline.yaml @@ -0,0 +1,52 @@ +name: Django CI Pipeline + +on: + push: + branches: + - main + - release + - testing-pipeline + +jobs: + test: + + runs-on: ubuntu-22.04 + + env: + SECRET_KEY: "t3st-dummy-s3cr3t-k3y" + STATIC_ROOT: "tmp/static/" + MEDIA_ROOT: "tmp/media/" + + steps: + - uses: actions/checkout@v4 + + - name: Install python3-venv + id: install + run: | + apt-get update + apt-get install python3 python3-venv python3-pip -y + + - name: Check Python version + run: | + python3 --version + echo "Python version: $(python3 --version)" + + - name: Create virtual environment + run: | + python3 -m venv venv + source venv/bin/activate + echo "Virtual environment created successfully" + # https://docs.github.com/en/actions/learn-github-actions/contexts#steps-context + if: steps.install.outcome == 'success' + + - name: Install dependencies + run: | + source venv/bin/activate + pip install --upgrade pip + pip install -r requirements.txt + + - name: Run tests + run: | + source venv/bin/activate + python manage.py test + diff --git a/idhub/admin/forms.py b/idhub/admin/forms.py index 268ce95..4264e03 100644 --- a/idhub/admin/forms.py +++ b/idhub/admin/forms.py @@ -74,7 +74,7 @@ class ImportForm(forms.Form): (x.did, x.label) for x in dids.filter(eidas1=False) ] self.fields['schema'].choices = [ - (x.id, x.name()) for x in Schemas.objects.filter() + (x.id, x.name) for x in Schemas.objects.filter() ] if dids.filter(eidas1=True).exists(): choices = [("", "")] diff --git a/idhub/admin/tables.py b/idhub/admin/tables.py index 79c22ba..e4d4ea2 100644 --- a/idhub/admin/tables.py +++ b/idhub/admin/tables.py @@ -1,25 +1,255 @@ import django_tables2 as tables -from django.utils.translation import gettext_lazy as _ -from idhub.models import Rol, Event +from django.utils.html import format_html + +from idhub.models import ( + Rol, + Event, + Service, + VerificableCredential, + DID, + File_datas, + Schemas +) 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('') + + 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('') + + 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: model = User 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): + 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('') + + def render_delete_role(self): + return format_html('') + class Meta: model = Rol template_name = "idhub/custom_table.html" 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('') + + def render_delete_service(self): + return format_html('') + + 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 Meta: model = Event template_name = "idhub/custom_table.html" 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('') + + 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 = """""" + delete_did = tables.TemplateColumn(template_code=delete_template_code, + orderable=False, + verbose_name="Delete DID") + + def render_edit_did(self): + return format_html('') + + 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 = """""" + 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") diff --git a/idhub/admin/views.py b/idhub/admin/views.py index 37d6076..3773cab 100644 --- a/idhub/admin/views.py +++ b/idhub/admin/views.py @@ -8,6 +8,7 @@ from smtplib import SMTPException from django_tables2 import SingleTableView from django.conf import settings +from django.template.loader import get_template from django.utils.translation import gettext_lazy as _ from django.views.generic.base import TemplateView, View from django.views.generic.edit import ( @@ -35,7 +36,14 @@ from idhub.admin.forms import ( ImportCertificateForm, ) from idhub.admin.tables import ( - DashboardTable + DashboardTable, + UserTable, + RolesTable, + ServicesTable, + CredentialTable, + DIDTable, + DataTable, + TemplateTable ) from idhub.models import ( DID, @@ -120,10 +128,12 @@ class ImportExport(AdminView): section = "ImportExport" -class PeopleListView(People, TemplateView): +class PeopleListView(People, SingleTableView): template_name = "idhub/admin/people.html" subtitle = _('View users') icon = 'bi bi-person' + table_class = UserTable + model = User def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -132,6 +142,11 @@ class PeopleListView(People, TemplateView): }) return context + def get_queryset(self, **kwargs): + queryset = super().get_queryset(**kwargs) + + return queryset + class PeopleView(People, TemplateView): template_name = "idhub/admin/user.html" @@ -440,17 +455,20 @@ class PeopleRolDeleteView(PeopleView): return redirect('idhub:admin_people_edit', user.id) -class RolesView(AccessControl): +class RolesView(AccessControl, SingleTableView): template_name = "idhub/admin/roles.html" subtitle = _('Manage roles') + table_class = RolesTable icon = '' + model = Rol def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context.update({ - 'roles': Rol.objects, - }) - return context + queryset = kwargs.pop('object_list', None) + if queryset is None: + self.object_list = self.model.objects.all() + + return super().get_context_data(**kwargs) + class RolRegisterView(AccessControl, CreateView): template_name = "idhub/admin/rol_register.html" @@ -504,17 +522,20 @@ class RolDeleteView(AccessControl): return redirect('idhub:admin_roles') -class ServicesView(AccessControl): +class ServicesView(AccessControl, SingleTableView): template_name = "idhub/admin/services.html" + table_class = ServicesTable subtitle = _('Manage services') icon = '' + model = Service def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context.update({ - 'services': Service.objects, - }) - return context + queryset = kwargs.pop('object_list', None) + if queryset is None: + self.object_list = self.model.objects.all() + + return super().get_context_data(**kwargs) + class ServiceRegisterView(AccessControl, CreateView): template_name = "idhub/admin/service_register.html" @@ -578,17 +599,19 @@ class ServiceDeleteView(AccessControl): return redirect('idhub:admin_services') -class CredentialsView(Credentials): +class CredentialsView(Credentials, SingleTableView): template_name = "idhub/admin/credentials.html" + table_class = CredentialTable subtitle = _('View credentials') icon = '' + model = VerificableCredential def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context.update({ - 'credentials': VerificableCredential.objects, - }) - return context + queryset = kwargs.pop('object_list', None) + if queryset is None: + self.object_list = self.model.objects.all() + + return super().get_context_data(**kwargs) class CredentialView(Credentials): @@ -667,19 +690,26 @@ class DeleteCredentialsView(Credentials): return redirect(self.success_url) -class DidsView(Credentials): +class DidsView(Credentials, SingleTableView): template_name = "idhub/admin/dids.html" + table_class = DIDTable subtitle = _('Manage identities (DID)') icon = 'bi bi-patch-check-fill' wallet = True + model = DID 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.update({ 'dids': DID.objects.filter(user=self.request.user), }) return context + class DidRegisterView(Credentials, CreateView): template_name = "idhub/admin/did_register.html" subtitle = _('Add a new organizational identity (DID)') @@ -699,7 +729,6 @@ class DidRegisterView(Credentials, CreateView): return super().form_valid(form) - class DidEditView(Credentials, UpdateView): template_name = "idhub/admin/did_register.html" subtitle = _('Organization Identities (DID)') @@ -767,19 +796,21 @@ class WalletConfigIssuesView(Credentials, FormView): return super().form_valid(form) -class SchemasView(SchemasMix): +class SchemasView(SchemasMix, SingleTableView): template_name = "idhub/admin/schemas.html" + table_class = TemplateTable subtitle = _('View credential templates') icon = '' + model = Schemas def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context.update({ - 'schemas': Schemas.objects, - }) - return context + queryset = kwargs.pop('object_list', None) + if queryset is None: + self.object_list = self.model.objects.all() + + return super().get_context_data(**kwargs) + - class SchemasDeleteView(SchemasMix): def get(self, request, *args, **kwargs): @@ -869,14 +900,14 @@ class SchemasImportView(SchemasMix): if not Schemas.objects.filter(file_schema=x).exists()] return schemas - + class SchemasImportAddView(SchemasMix): def get(self, request, *args, **kwargs): self.check_valid_user() file_name = kwargs['file_schema'] schemas_files = os.listdir(settings.SCHEMAS_DIR) - if not file_name in schemas_files: + if file_name not in schemas_files: messages.error(self.request, f"The schema {file_name} not exist!") return redirect('idhub:admin_schemas_import') @@ -897,7 +928,19 @@ class SchemasImportAddView(SchemasMix): except Exception: messages.error(self.request, _('This is not a valid schema!')) return - schema = Schemas.objects.create(file_schema=file_name, data=data, type=title) + + _name = json.dumps(ldata.get('name', '')) + _description = json.dumps(ldata.get('description', '')) + + schema = Schemas.objects.create( + file_schema=file_name, + data=data, + type=title, + _name=_name, + _description=_description, + # template_description=_description + template_description=self.get_description() + ) schema.save() return schema @@ -909,11 +952,27 @@ class SchemasImportAddView(SchemasMix): return data + def get_template_description(self): + context = {} + template_name = 'credentials/{}'.format( + self.schema.file_schema + ) + tmpl = get_template(template_name) + return tmpl.render(context) -class ImportView(ImportExport, TemplateView): + def get_description(self): + for des in json.loads(self.get_template_description()).get('description', []): + if settings.LANGUAGE_CODE == des.get('lang'): + return des.get('value', '') + return '' + + +class ImportView(ImportExport, SingleTableView): template_name = "idhub/admin/import.html" + table_class = DataTable subtitle = _('Import data') icon = '' + model = File_datas def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) @@ -965,4 +1024,3 @@ class ImportAddView(NotifyActivateUserByEmail, ImportExport, FormView): messages.error(self.request, e) return super().form_valid(form) - diff --git a/idhub/management/commands/create_example_data.py b/idhub/management/commands/create_example_data.py new file mode 100644 index 0000000..4ee24bb --- /dev/null +++ b/idhub/management/commands/create_example_data.py @@ -0,0 +1,293 @@ +import random +from typing import Type, Callable, List + +from django.core.management.base import BaseCommand +from django.db import models, IntegrityError + +from idhub.models import Event, Rol, Service, UserRol +from idhub_auth.models import User + +from faker import Faker + +DEFAULT_OBJECTS_CREATED: int = 30 +RANDOM_STRING_LENGTH: int = 30 +EMAIL_RANDOM_STRING_LENGTH: int = 10 +EXISTING_EVENTS: int = 30 + +fake = Faker() + + +class Command(BaseCommand): + """ + Populate the database with dummy values. + You can either specify which model to create objects for, + or create objects for all models. + If no data is specified it will create 30 events, users, + services, roles, and user roles. + """ + + help = 'Populate the database with dummy values for testing.' + + def __init__(self, *args, **kwargs): + """ + In the context of a Django management command, + initializing lists like created_users in the constructor ensures + that each run of the command has its own set of users, services, + roles, etc., and does not unintentionally share or retain state + across different invocations of the command. + """ + super().__init__(*args, **kwargs) + self.created_users = [] + self.created_services = [] + self.created_roles = [] + + def handle(self, *args, **options): + any_option_used = self.process_options(options) + + if not any_option_used: + self.create_all() + + def process_options(self, input_options): + option_methods = { + 'events': self.create_events, + 'users': self.create_users, + 'superusers': self.create_superusers, + 'services': self.create_services, + 'roles': self.create_roles, + 'userroles': self.create_user_roles, + 'userrole': self.create_specific_user_role, + 'register': self.register_user, + 'register_superuser': self.register_superuser, + 'amount': self.create_all + } + + any_option_used = self.match_input_to_options(input_options, + option_methods) + + return any_option_used + + def match_input_to_options(self, input_options, option_methods): + any_option_used = False + + for option, method in option_methods.items(): + is_valid_option = option in input_options and input_options[ + option] is not None + if is_valid_option: + self.call_selected_method(input_options, method, option) + any_option_used = True + + return any_option_used + + def call_selected_method(self, input_options, method, option): + args = self.create_argument_list(input_options, option) + method(*args) + + def create_argument_list(self, input_options, option): + if isinstance(input_options[option], list): + args = input_options[option] + else: + args = [input_options[option]] + return args + + def add_arguments(self, parser): + parser.add_argument( + '--amount', type=int, action='store', + help='Create N objects (includes events, users, services, roles, and user roles)' + ) + parser.add_argument('--events', type=int, + help='Create the specified number of events') + parser.add_argument('--users', type=int, + help='Create the specified number of users') + parser.add_argument('--superusers', type=int, + help='Create the specified number of superusers') + parser.add_argument('--services', type=int, + help='Create the specified number of services') + parser.add_argument('--roles', type=int, + help='Create the specified number of roles') + parser.add_argument('--userroles', type=int, + help='Create the specified number of user roles') + parser.add_argument( + '--userrole', nargs=2, type=str, + help='Create a user role for user U and service S', + metavar=('U', 'S'), + ) + parser.add_argument('--register', nargs=2, type=str, + help='Create a user with email EMAIL and password PW', + metavar=('EMAIL', 'PW')) + parser.add_argument('--register-superuser', nargs=2, type=str, + help='Create a superuser with email EMAIL and ' + 'password PW', + metavar=('EMAIL', 'PW')) + + def register_user(self, email: str, password: str): + """ + Register a new user with the given email and password from the command line. + """ + try: + self.create_user(email, password) + self.stdout.write(f"Successfully registered user: {email}") + except IntegrityError: + self.stdout.write(f"Failed to register user: {email}") + + def register_superuser(self, email: str, password: str): + """ + Register a new superuser with the given email and password from the command line. + """ + try: + self.create_superuser(email, password) + self.stdout.write(f"Successfully registered superuser: {email}") + except IntegrityError: + self.stdout.write(f"Failed to register superuser: {email}") + + def create_all(self, amount=DEFAULT_OBJECTS_CREATED): + self.create_events(amount) + self.create_users(amount) + self.create_roles(amount) + self.create_services(amount) + self.create_user_roles(amount) + + def create_objects(self, model: Type[models.Model], + data_generator: Callable, amount: int) -> List[ + models.Model]: + """ + Generic method to create objects for a given model and keep track of them. + + Args: + model: The Django model class for which objects are to be created. + data_generator: A function that returns a dictionary of attributes for creating a model instance. + amount: The number of objects to create. + + Returns: + A list of successfully created model instances. + """ + created_objects = [] + for _ in range(amount): + try: + model_instance = self.create_from_data(data_generator, model) + created_objects.append(model_instance) + except IntegrityError: + self.print_failure_message(model) + + self.print_amount_created(created_objects, model) + return created_objects + + def create_users(self, amount: int): + def user_data(): + return { + 'email': fake.email(), + 'first_name': fake.first_name(), + 'last_name': fake.last_name() + } + created_users = self.create_objects(User, user_data, amount) + self.created_users.extend(created_users) + + def create_events(self, amount: int, user=None): + def event_data(): + return { + 'type': random.randint(1, EXISTING_EVENTS), + 'message': fake.paragraph(nb_sentences=3), + 'user': user + } + self.create_objects(Event, event_data, amount) + + def create_superusers(self, amount=0): + """Superusers can only be created from the specific command""" + created_superuser_amount = 0 + for value in range(0, amount): + email = fake.email() + try: + User.objects.create_superuser(email) + created_superuser_amount += 1 + except IntegrityError: + self.stdout.write("Couldn't create superuser " + email) + self.stdout.write(f"Created {created_superuser_amount} users") + + def create_services(self, amount: int): + def service_data(): + domain = fake.text(max_nb_chars=200) + description = fake.text(max_nb_chars=250) + return { + 'domain': domain, + 'description': description, + } + + created_services = self.create_objects(Service, service_data, amount) + self.associate_random_roles(created_services) + self.created_services.extend(created_services) + + def create_roles(self, amount: int): + def role_data(): + name = fake.job() + description = fake.text(max_nb_chars=250) + return {'name': name, 'description': description} + + created_roles = self.create_objects(Rol, role_data, amount) + self.created_roles.extend(created_roles) + + def create_user_roles(self, amount: int, user_id=None, service_id=None): + def user_role_data(): + user = self.get_or_create_user(user_id) + service = self.get_or_create_service(service_id) + return {"user": user, "service": service} + + self.create_objects(UserRol, user_role_data, amount) + + def create_specific_user_role(self, user, service): + self.create_user_roles(1, user, service) + + def create_user(self, email: str, password: str): + User.objects.create_user(email, password) + + def create_superuser(self, email: str, password: str): + User.objects.create_superuser(email, password) + + def print_failure_message(self, model): + self.stdout.write(f"Couldn't create {model.__name__} object.") + + def print_amount_created(self, created_objects, model): + self.stdout.write(f"Created {len(created_objects)} " + f"{model.__name__} objects") + + def create_from_data(self, data_generator, model): + model_instance = model.objects.create(**data_generator()) + return model_instance + + def associate_random_roles(self, created_services): + for service in created_services: + self.associate_service_to_random_roles(service) + + def associate_service_to_random_roles(self, service): + associated_roles = [self.get_or_create_role() for _ in range( + random.randint(0, 2))] + service.rol.set(associated_roles) + + def get_or_create_user(self, user_id): + if user_id is not None: + user = User.objects.get(user_id=user_id) + elif len(self.created_users) != 0: + user = random.choice(self.created_users) + else: + self.create_users(1) + user = self.created_users[0] + + return user + + def get_or_create_service(self, service_id): + if service_id is not None: + service = Service.objects.get(service_id=service_id) + elif len(self.created_services) != 0: + service = random.choice(self.created_services) + else: + self.create_services(1) + service = self.created_services[0] + + return service + + def get_or_create_role(self): + if len(self.created_roles) != 0: + role = random.choice(self.created_roles) + else: + self.create_roles(1) + role = self.created_roles[0] + + return role \ No newline at end of file diff --git a/idhub/management/commands/initial_datas.py b/idhub/management/commands/initial_datas.py index 04c2df7..e889cb7 100644 --- a/idhub/management/commands/initial_datas.py +++ b/idhub/management/commands/initial_datas.py @@ -95,16 +95,18 @@ class Command(BaseCommand): assert title except Exception: title = '' - return - name = '' - try: - for x in dname: - if settings.LANGUAGE_CODE in x['lang']: - name = x.get('value', '') - except Exception: - return + _name = '' - Schemas.objects.create(file_schema=file_name, data=data, type=title) + _name = json.dumps(ldata.get('name', '')) + _description = json.dumps(ldata.get('description', '')) + + Schemas.objects.create( + file_schema=file_name, + data=data, + type=title, + _name=_name, + _description=_description + ) def open_file(self, file_name): data = '' diff --git a/idhub/migrations/0001_initial.py b/idhub/migrations/0001_initial.py index 0356302..4fabd27 100644 --- a/idhub/migrations/0001_initial.py +++ b/idhub/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2024-01-20 14:29 +# Generated by Django 4.2.5 on 2024-01-22 15:44 from django.conf import settings from django.db import migrations, models @@ -102,6 +102,14 @@ class Migration(migrations.Migration): ('file_schema', models.CharField(max_length=250)), ('data', models.TextField()), ('created_at', models.DateTimeField(auto_now=True)), + ('_name', models.TextField(db_column='name', null=True)), + ( + '_description', + models.CharField( + db_column='description', max_length=250, null=True + ), + ), + ('template_description', models.TextField(null=True)), ], ), migrations.CreateModel( @@ -315,6 +323,7 @@ class Migration(migrations.Migration): (28, 'Organisational DID deleted by admin'), (29, 'User deactivated'), (30, 'User activated'), + (31, 'User send Verificable Presentation'), ], verbose_name='Event', ), diff --git a/idhub/models.py b/idhub/models.py index 53a8819..96d8559 100644 --- a/idhub/models.py +++ b/idhub/models.py @@ -473,6 +473,9 @@ class Schemas(models.Model): file_schema = models.CharField(max_length=250) data = models.TextField() created_at = models.DateTimeField(auto_now=True) + _name = models.TextField(null=True, db_column='name') + _description = models.CharField(max_length=250, null=True, db_column='description') + template_description = models.TextField(null=True) @property def get_schema(self): @@ -480,22 +483,62 @@ class Schemas(models.Model): return {} return json.loads(self.data) + def _update_and_get_field(self, field_attr, schema_key, is_json=False): + field_value = getattr(self, field_attr) + if not field_value: + field_value = self.get_schema.get(schema_key, [] if is_json else '') + self._update_model_field(field_attr, field_value) + try: + if is_json: + return json.loads(field_value) + except json.decoder.JSONDecodeError: + return 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, request=None): - names = {} - for name in self.get_schema.get('name', []): - lang = name.get('lang') - if 'ca' in lang: - lang = 'ca' - names[lang]= name.get('value') + names = self._update_and_get_field('_name', 'name', + is_json=True) + language_code = self._get_language_code(request) + name = self._get_name_by_language(names, language_code) - if request and request.LANGUAGE_CODE in names.keys(): - return names[request.LANGUAGE_CODE] + return name - return names[settings.LANGUAGE_CODE] - + def _get_language_code(self, request=None): + language_code = settings.LANGUAGE_CODE + if request: + language_code = request.LANGUAGE_CODE + if self._is_catalan_code(language_code): + language_code = 'ca' + + return language_code + + def _get_name_by_language(self, names, lang_code): + for name in names: + if name.get('lang') == lang_code: + return name.get('value') + + return None + + def _is_catalan_code(self, language_code): + return language_code == 'ca_ES' + + @name.setter + def name(self, value): + self._name = json.dumps(value) + + @property 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): """ @@ -559,6 +602,9 @@ class VerificableCredential(models.Model): def type(self): return self.schema.type + def get_description(self): + return self.schema._description or '' + def description(self): for des in json.loads(self.render("")).get('description', []): if settings.LANGUAGE_CODE in des.get('lang'): @@ -652,7 +698,6 @@ class VerificableCredential(models.Model): d_minimum = self.filter_dict(d_ordered) return ujson.dumps(d_minimum) - def get_issued_on(self): if self.issued_on: return self.issued_on.strftime("%m/%d/%Y") @@ -732,7 +777,7 @@ class Service(models.Model): def get_roles(self): if self.rol.exists(): - return ", ".join([x.name for x in self.rol.all()]) + return ", ".join([x.name for x in self.rol.order_by("name")]) return _("None") def __str__(self): diff --git a/idhub/templates/idhub/admin/credentials.html b/idhub/templates/idhub/admin/credentials.html index caee5fc..4862f82 100644 --- a/idhub/templates/idhub/admin/credentials.html +++ b/idhub/templates/idhub/admin/credentials.html @@ -1,39 +1,11 @@ {% extends "idhub/base_admin.html" %} {% load i18n %} +{% load render_table from django_tables2 %} {% block content %}

{{ subtitle }}

-
-
-
- - - - - - - - - - - - - {% for f in credentials.all %} - - - - - - - - - {% endfor %} - -
{{ f.get_type }}{{ f.description }}{{ f.get_issued_on }}{{ f.get_status }}{{ f.user.email }}
-
-
-
+{% render_table table %} {% endblock %} diff --git a/idhub/templates/idhub/admin/dids.html b/idhub/templates/idhub/admin/dids.html index c5cb896..4ae6213 100644 --- a/idhub/templates/idhub/admin/dids.html +++ b/idhub/templates/idhub/admin/dids.html @@ -1,5 +1,6 @@ {% extends "idhub/base_admin.html" %} {% load i18n %} +{% load render_table from django_tables2 %} {% block content %}

@@ -8,6 +9,8 @@

+ {% render_table table %} + {% comment %}
@@ -31,6 +34,7 @@ {% endfor %}
+ {% endcomment %} diff --git a/idhub/templates/idhub/admin/import.html b/idhub/templates/idhub/admin/import.html index c0e3f64..87c0cc3 100644 --- a/idhub/templates/idhub/admin/import.html +++ b/idhub/templates/idhub/admin/import.html @@ -1,36 +1,14 @@ {% extends "idhub/base_admin.html" %} {% load i18n %} +{% load render_table from django_tables2 %} {% block content %}

{{ subtitle }}

-
-
-
- - - - - - - - - - {% for f in dates.all %} - - - - - - {% endfor %} - -
{{ f.created_at }}{{ f.file_name }}{% if f.success %}{% else %}{% endif %}
- -
-
+{% render_table table %} + {% endblock %} diff --git a/idhub/templates/idhub/admin/people.html b/idhub/templates/idhub/admin/people.html index 25b7c61..4862f82 100644 --- a/idhub/templates/idhub/admin/people.html +++ b/idhub/templates/idhub/admin/people.html @@ -1,39 +1,11 @@ {% extends "idhub/base_admin.html" %} {% load i18n %} +{% load render_table from django_tables2 %} {% block content %}

{{ subtitle }}

-
- - - - - - - - - - - - - {% for user in users %} - - - - - - - - - {% endfor %} - -
{{ user.last_name|default:'' }}{{ user.first_name|default:'' }}{{ user.email }} - {{ user.get_memberships }} - - {{ user.get_roles }} -
-
+{% render_table table %} {% endblock %} diff --git a/idhub/templates/idhub/admin/roles.html b/idhub/templates/idhub/admin/roles.html index d3d6593..0bc90aa 100644 --- a/idhub/templates/idhub/admin/roles.html +++ b/idhub/templates/idhub/admin/roles.html @@ -1,5 +1,6 @@ {% extends "idhub/base_admin.html" %} {% load i18n %} +{% load render_table from django_tables2 %} {% block content %}

@@ -8,31 +9,10 @@

-
- - - - - - - - - - - {% for rol in roles.all %} - - - - - - - {% endfor %} - -
{{ rol.name }}{{ rol.description|default:""}}
+ {% render_table table %} -
{% endblock %} diff --git a/idhub/templates/idhub/admin/schemas.html b/idhub/templates/idhub/admin/schemas.html index 6afcf41..543c89b 100644 --- a/idhub/templates/idhub/admin/schemas.html +++ b/idhub/templates/idhub/admin/schemas.html @@ -1,47 +1,19 @@ {% extends "idhub/base_admin.html" %} {% load i18n %} +{% load render_table from django_tables2 %} {% block content %}

{{ subtitle }}

-
-
-
- - - - - - - - - - - - - {% for schema in schemas.all %} - - - - - - - - - {% endfor %} - -
{{ schema.created_at }}{{ schema.file_schema }}{{ schema.name }}{{ schema.description }}
- -
-
+{% render_table table %} + -{% for schema in schemas.all %} -