diff --git a/recruitment/migrations/0001_initial.py b/recruitment/migrations/0001_initial.py index 0798d3d..b6ba948 100644 --- a/recruitment/migrations/0001_initial.py +++ b/recruitment/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0 on 2025-12-10 11:08 +# Generated by Django 5.2.7 on 2025-12-11 11:55 import django.contrib.auth.models import django.contrib.auth.validators @@ -9,6 +9,7 @@ import django_ckeditor_5.fields import django_countries.fields import django_extensions.db.fields import recruitment.validators +import secured_fields.fields from django.conf import settings from django.db import migrations, models @@ -73,8 +74,6 @@ class Migration(migrations.Migration): ('start_time', models.DateTimeField(db_index=True, verbose_name='Start Time')), ('duration', models.PositiveIntegerField(verbose_name='Duration (minutes)')), ('status', models.CharField(choices=[('waiting', 'Waiting'), ('started', 'Started'), ('ended', 'Ended'), ('cancelled', 'Cancelled')], db_index=True, default='waiting', max_length=20)), - ('cancelled_at', models.DateTimeField(blank=True, null=True, verbose_name='Cancelled At')), - ('cancelled_reason', models.TextField(blank=True, null=True, verbose_name='Cancellation Reason')), ('meeting_id', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='External Meeting ID')), ('password', models.CharField(blank=True, max_length=20, null=True)), ('zoom_gateway_response', models.JSONField(blank=True, null=True)), @@ -101,7 +100,7 @@ class Migration(migrations.Migration): ('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')), ('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Participant Name')), ('email', models.EmailField(max_length=254, verbose_name='Email')), - ('phone', models.CharField(blank=True, max_length=12, null=True, verbose_name='Phone Number')), + ('phone', secured_fields.fields.EncryptedCharField(blank=True, max_length=12, null=True, verbose_name='Phone Number')), ('designation', models.CharField(blank=True, max_length=100, null=True, verbose_name='Designation')), ], options={ @@ -162,13 +161,13 @@ class Migration(migrations.Migration): ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('first_name', secured_fields.fields.EncryptedCharField(blank=True, max_length=150, verbose_name='first name')), ('user_type', models.CharField(choices=[('staff', 'Staff'), ('agency', 'Agency'), ('candidate', 'Candidate')], default='staff', max_length=20, verbose_name='User Type')), - ('phone', models.CharField(blank=True, null=True, verbose_name='Phone')), + ('phone', secured_fields.fields.EncryptedCharField(blank=True, null=True, verbose_name='Phone')), ('profile_image', models.ImageField(blank=True, null=True, upload_to='profile_pic/', validators=[recruitment.validators.validate_image_size], verbose_name='Profile Image')), ('designation', models.CharField(blank=True, max_length=100, null=True, verbose_name='Designation')), ('email', models.EmailField(error_messages={'unique': 'A user with this email already exists.'}, max_length=254, unique=True)), @@ -261,7 +260,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=200, unique=True, verbose_name='Agency Name')), ('contact_person', models.CharField(blank=True, max_length=150, verbose_name='Contact Person')), ('email', models.EmailField(max_length=254, unique=True)), - ('phone', models.CharField(blank=True, max_length=20, null=True)), + ('phone', secured_fields.fields.EncryptedCharField(blank=True, max_length=20, null=True)), ('website', models.URLField(blank=True)), ('notes', models.TextField(blank=True, help_text='Internal notes about the agency')), ('country', django_countries.fields.CountryField(blank=True, max_length=2, null=True)), @@ -499,15 +498,15 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')), ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated at')), ('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')), - ('first_name', models.CharField(max_length=255, verbose_name='First Name')), + ('first_name', secured_fields.fields.EncryptedCharField(max_length=255, verbose_name='First Name')), ('last_name', models.CharField(max_length=255, verbose_name='Last Name')), ('middle_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Middle Name')), ('email', models.EmailField(db_index=True, max_length=254, unique=True, verbose_name='Email')), - ('phone', models.CharField(blank=True, null=True, verbose_name='Phone')), + ('phone', secured_fields.fields.EncryptedCharField(blank=True, null=True, verbose_name='Phone')), ('date_of_birth', models.DateField(blank=True, null=True, verbose_name='Date of Birth')), ('gender', models.CharField(blank=True, choices=[('M', 'Male'), ('F', 'Female')], max_length=1, null=True, verbose_name='Gender')), ('gpa', models.DecimalField(decimal_places=2, help_text='GPA must be between 0 and 4.', max_digits=3, verbose_name='GPA')), - ('national_id', models.CharField(help_text='Enter the national id or iqama number')), + ('national_id', secured_fields.fields.EncryptedCharField(help_text='Enter the national id or iqama number')), ('nationality', django_countries.fields.CountryField(blank=True, max_length=2, null=True, verbose_name='Nationality')), ('address', models.TextField(blank=True, null=True, verbose_name='Address')), ('profile_image', models.ImageField(blank=True, null=True, upload_to='profile_pic/', validators=[recruitment.validators.validate_image_size], verbose_name='Profile Image')), @@ -532,6 +531,8 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')), ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated at')), ('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')), + ('cancelled_at', models.DateTimeField(blank=True, null=True, verbose_name='Cancelled At')), + ('cancelled_reason', models.TextField(blank=True, null=True, verbose_name='Cancellation Reason')), ('interview_date', models.DateField(db_index=True, verbose_name='Interview Date')), ('interview_time', models.TimeField(verbose_name='Interview Time')), ('interview_type', models.CharField(choices=[('Remote', 'Remote (e.g., Zoom, Google Meet)'), ('Onsite', 'In-Person (Physical Location)')], default='Remote', max_length=20)), diff --git a/recruitment/migrations/0002_alter_customuser_first_name_alter_customuser_phone_and_more.py b/recruitment/migrations/0002_alter_customuser_first_name_alter_customuser_phone_and_more.py deleted file mode 100644 index 9ca803b..0000000 --- a/recruitment/migrations/0002_alter_customuser_first_name_alter_customuser_phone_and_more.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 5.2.7 on 2025-12-10 12:50 - -import secured_fields.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('recruitment', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='customuser', - name='first_name', - field=secured_fields.fields.EncryptedCharField(blank=True, max_length=150, verbose_name='first name'), - ), - migrations.AlterField( - model_name='customuser', - name='phone', - field=secured_fields.fields.EncryptedCharField(blank=True, null=True, verbose_name='Phone'), - ), - migrations.AlterField( - model_name='hiringagency', - name='phone', - field=secured_fields.fields.EncryptedCharField(blank=True, max_length=20, null=True), - ), - migrations.AlterField( - model_name='participants', - name='phone', - field=secured_fields.fields.EncryptedCharField(blank=True, max_length=12, null=True, verbose_name='Phone Number'), - ), - migrations.AlterField( - model_name='person', - name='first_name', - field=secured_fields.fields.EncryptedCharField(max_length=255, verbose_name='First Name'), - ), - migrations.AlterField( - model_name='person', - name='phone', - field=secured_fields.fields.EncryptedCharField(blank=True, null=True, verbose_name='Phone'), - ), - ] diff --git a/recruitment/migrations/0003_alter_person_national_id.py b/recruitment/migrations/0003_alter_person_national_id.py deleted file mode 100644 index 140cee3..0000000 --- a/recruitment/migrations/0003_alter_person_national_id.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.2.7 on 2025-12-10 13:04 - -import secured_fields.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('recruitment', '0002_alter_customuser_first_name_alter_customuser_phone_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='person', - name='national_id', - field=secured_fields.fields.EncryptedCharField(help_text='Enter the national id or iqama number'), - ), - ] diff --git a/recruitment/migrations/0004_remove_interview_cancelled_at_and_more.py b/recruitment/migrations/0004_remove_interview_cancelled_at_and_more.py deleted file mode 100644 index 599d096..0000000 --- a/recruitment/migrations/0004_remove_interview_cancelled_at_and_more.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 5.2.7 on 2025-12-10 13:33 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('recruitment', '0003_alter_person_national_id'), - ] - - operations = [ - migrations.RemoveField( - model_name='interview', - name='cancelled_at', - ), - migrations.RemoveField( - model_name='interview', - name='cancelled_reason', - ), - migrations.AddField( - model_name='scheduledinterview', - name='cancelled_at', - field=models.DateTimeField(blank=True, null=True, verbose_name='Cancelled At'), - ), - migrations.AddField( - model_name='scheduledinterview', - name='cancelled_reason', - field=models.TextField(blank=True, null=True, verbose_name='Cancellation Reason'), - ), - ] diff --git a/recruitment/urls.py b/recruitment/urls.py index 5c2750c..1798112 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -218,7 +218,8 @@ urlpatterns = [ # SYSTEM & ADMINISTRATIVE # ======================================================================== # Settings & Configuration - path("settings/", views.admin_settings, name="admin_settings"), + path("settings/",views.settings,name="settings"), + path("settings/staff", views.admin_settings, name="admin_settings"), path("settings/list/", views.settings_list, name="settings_list"), path("settings/create/", views.settings_create, name="settings_create"), path("settings//", views.settings_detail, name="settings_detail"), diff --git a/recruitment/utils.py b/recruitment/utils.py index 95e9d68..e560a3d 100644 --- a/recruitment/utils.py +++ b/recruitment/utils.py @@ -8,7 +8,7 @@ from django.utils import timezone from .models import ScheduledInterview from django.template.loader import render_to_string from django.core.mail import send_mail - +import random import os import json import logging diff --git a/recruitment/views.py b/recruitment/views.py index bdf3c1b..52f6f26 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -2305,6 +2305,10 @@ def regenerate_agency_password(request, slug): new_password=generate_random_password() agency.generated_password=new_password agency.save() + if agency.user is None: + messages.error(request, _("Error: The user account associated with this agency could not be found.")) + # Redirect the staff user back to the agency detail page or list + return redirect('agency_detail', slug=agency.slug) # Or wherever appropriate user=agency.user user.set_password(new_password) user.save() diff --git a/templates/base.html b/templates/base.html index 93461e1..287daa6 100644 --- a/templates/base.html +++ b/templates/base.html @@ -229,7 +229,7 @@ {% if request.user.is_superuser %}
  • - + {% trans "Settings" %}
  • diff --git a/templates/user/settings.html b/templates/user/settings.html index b744724..7ef4468 100644 --- a/templates/user/settings.html +++ b/templates/user/settings.html @@ -93,7 +93,7 @@