diff --git a/NorahUniversity/__pycache__/settings.cpython-313.pyc b/NorahUniversity/__pycache__/settings.cpython-313.pyc deleted file mode 100644 index 0a0fe0f..0000000 Binary files a/NorahUniversity/__pycache__/settings.cpython-313.pyc and /dev/null differ diff --git a/NorahUniversity/settings.py b/NorahUniversity/settings.py index 0607f44..ed4604f 100644 --- a/NorahUniversity/settings.py +++ b/NorahUniversity/settings.py @@ -9,6 +9,7 @@ https://docs.djangoproject.com/en/5.2/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.2/ref/settings/ """ + import os from pathlib import Path from django.templatetags.static import static @@ -22,7 +23,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-_!ew&)1&r--3h17knd27^x8(xu(&-f4q3%x543lv5vx2!784s*' +SECRET_KEY = "django-insecure-_!ew&)1&r--3h17knd27^x8(xu(&-f4q3%x543lv5vx2!784s*" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -32,104 +33,102 @@ ALLOWED_HOSTS = ["*"] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.humanize', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'rest_framework', - 'recruitment.apps.RecruitmentConfig', - 'corsheaders', - 'django.contrib.sites', - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - 'allauth.socialaccount.providers.linkedin_oauth2', - 'channels', - 'django_filters', - 'crispy_forms', + "django.contrib.admin", + "django.contrib.humanize", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "rest_framework", + "recruitment.apps.RecruitmentConfig", + "corsheaders", + "django.contrib.sites", + "allauth", + "allauth.account", + "allauth.socialaccount", + "allauth.socialaccount.providers.linkedin_oauth2", + "channels", + "django_filters", + "crispy_forms", # 'django_summernote', # 'ckeditor', - 'django_ckeditor_5', - 'crispy_bootstrap5', - 'django_extensions', - 'template_partials', - 'django_countries', - 'django_celery_results', - 'django_q', - 'widget_tweaks', - 'easyaudit' + "django_ckeditor_5", + "crispy_bootstrap5", + "django_extensions", + "template_partials", + "django_countries", + "django_celery_results", + "django_q", + "widget_tweaks", + "easyaudit", ] - SITE_ID = 1 LOGIN_REDIRECT_URL = '/' -ACCOUNT_LOGOUT_REDIRECT_URL = '/' +ACCOUNT_LOGOUT_REDIRECT_URL = "/" -ACCOUNT_SIGNUP_REDIRECT_URL = '/' +ACCOUNT_SIGNUP_REDIRECT_URL = "/" -LOGIN_URL = '/accounts/login/' - +LOGIN_URL = "/accounts/login/" AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', - 'allauth.account.auth_backends.AuthenticationBackend', + "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", ] MIDDLEWARE = [ - 'corsheaders.middleware.CorsMiddleware', - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'allauth.account.middleware.AccountMiddleware', - 'easyaudit.middleware.easyaudit.EasyAuditMiddleware', + "corsheaders.middleware.CorsMiddleware", + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.locale.LocaleMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "allauth.account.middleware.AccountMiddleware", + "easyaudit.middleware.easyaudit.EasyAuditMiddleware", ] -ROOT_URLCONF = 'NorahUniversity.urls' +ROOT_URLCONF = "NorahUniversity.urls" CORS_ALLOW_ALL_ORIGINS = True -ASGI_APPLICATION = 'hospital_recruitment.asgi.application' +ASGI_APPLICATION = "hospital_recruitment.asgi.application" CHANNEL_LAYERS = { - 'default': { - 'BACKEND': 'channels_redis.core.RedisChannelLayer', - 'CONFIG': { - 'hosts': [('127.0.0.1', 6379)], + "default": { + "BACKEND": "channels_redis.core.RedisChannelLayer", + "CONFIG": { + "hosts": [("127.0.0.1", 6379)], }, }, } TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [os.path.join(BASE_DIR, "templates")], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'NorahUniversity.wsgi.application' +WSGI_APPLICATION = "NorahUniversity.wsgi.application" # Database # https://docs.djangoproject.com/en/5.2/ref/settings/#databases @@ -157,6 +156,23 @@ DATABASES = { +# AUTH_PASSWORD_VALIDATORS = [ +# { +# 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', +# }, +# { +# 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', +# }, +# { +# 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', +# }, +# { +# 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', +# }, +# ] + +# settings.py + AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', @@ -173,21 +189,20 @@ AUTH_PASSWORD_VALIDATORS = [ ] -ACCOUNT_LOGIN_METHODS = ['email'] -ACCOUNT_SIGNUP_FIELDS = ['email*', 'password1*', 'password2*'] +ACCOUNT_LOGIN_METHODS = ["email"] +ACCOUNT_SIGNUP_FIELDS = ["email*", "password1*", "password2*"] ACCOUNT_UNIQUE_EMAIL = True ACCOUNT_EMAIL_VERIFICATION = 'none' ACCOUNT_USER_MODEL_USERNAME_FIELD = None - - +ACCOUNT_EMAIL_VERIFICATION = "mandatory" ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True -ACCOUNT_FORMS = {'signup': 'recruitment.forms.StaffSignupForm'} +ACCOUNT_FORMS = {"signup": "recruitment.forms.StaffSignupForm"} -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # Crispy Forms Configuration CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" @@ -195,29 +210,29 @@ CRISPY_TEMPLATE_PACK = "bootstrap5" # Bootstrap 5 Configuration CRISPY_BS5 = { - 'include_placeholder_text': True, - 'use_css_helpers': True, + "include_placeholder_text": True, + "use_css_helpers": True, } ACCOUNT_RATE_LIMITS = { - 'send_email_confirmation': None, # Disables the limit + "send_email_confirmation": None, # Disables the limit } # Internationalization # https://docs.djangoproject.com/en/5.2/topics/i18n/ LANGUAGES = [ - ('en', 'English'), - ('ar', 'Arabic'), + ("en", "English"), + ("ar", "Arabic"), ] -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" LOCALE_PATHS = [ - BASE_DIR / 'locale', + BASE_DIR / "locale", ] -TIME_ZONE = 'Asia/Riyadh' +TIME_ZONE = "Asia/Riyadh" USE_I18N = True @@ -226,36 +241,35 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.2/howto/static-files/ -STATIC_URL = '/static/' -MEDIA_URL = '/media/' -STATICFILES_DIRS = [ - BASE_DIR / 'static' -] -STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') -MEDIA_ROOT = os.path.join(BASE_DIR, 'static/media') +STATIC_URL = "/static/" +MEDIA_URL = "/media/" +STATICFILES_DIRS = [BASE_DIR / "static"] +STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") +MEDIA_ROOT = os.path.join(BASE_DIR, "media") # Default primary key field type # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # LinkedIn OAuth Config SOCIALACCOUNT_PROVIDERS = { - 'linkedin_oauth2': { - 'SCOPE': [ - 'r_liteprofile', 'r_emailaddress', 'w_member_social', - 'rw_organization_admin', 'w_organization_social' + "linkedin_oauth2": { + "SCOPE": [ + "r_liteprofile", + "r_emailaddress", + "w_member_social", + "rw_organization_admin", + "w_organization_social", ], - 'PROFILE_FIELDS': [ - 'id', 'first-name', 'last-name', 'email-address' - ] + "PROFILE_FIELDS": ["id", "first-name", "last-name", "email-address"], } } -ZOOM_ACCOUNT_ID = 'HoGikHXsQB2GNDC5Rvyw9A' -ZOOM_CLIENT_ID = 'brC39920R8C8azfudUaQgA' -ZOOM_CLIENT_SECRET = 'rvfhjlbID4ychXPOvZ2lYsoAC0B0Ny2L' -SECRET_TOKEN = '6KdTGyF0SSCSL_V4Xa34aw' +ZOOM_ACCOUNT_ID = "HoGikHXsQB2GNDC5Rvyw9A" +ZOOM_CLIENT_ID = "brC39920R8C8azfudUaQgA" +ZOOM_CLIENT_SECRET = "rvfhjlbID4ychXPOvZ2lYsoAC0B0Ny2L" +SECRET_TOKEN = "6KdTGyF0SSCSL_V4Xa34aw" ZOOM_WEBHOOK_API_KEY = "2GNDC5Rvyw9AHoGikHXsQB" # Maximum file upload size (in bytes) @@ -264,145 +278,199 @@ FILE_UPLOAD_MAX_MEMORY_SIZE = 10485760 # 10MB CORS_ALLOW_CREDENTIALS = True -CELERY_BROKER_URL = 'redis://localhost:6379/0' # Or your message broker URL -CELERY_RESULT_BACKEND = 'django-db' # If using django-celery-results -CELERY_ACCEPT_CONTENT = ['application/json'] -CELERY_TASK_SERIALIZER = 'json' -CELERY_RESULT_SERIALIZER = 'json' -CELERY_TIMEZONE = 'UTC' +CELERY_BROKER_URL = "redis://localhost:6379/0" # Or your message broker URL +CELERY_RESULT_BACKEND = "django-db" # If using django-celery-results +CELERY_ACCEPT_CONTENT = ["application/json"] +CELERY_TASK_SERIALIZER = "json" +CELERY_RESULT_SERIALIZER = "json" +CELERY_TIMEZONE = "UTC" -LINKEDIN_CLIENT_ID = '867jwsiyem1504' -LINKEDIN_CLIENT_SECRET = 'WPL_AP1.QNH5lYnfRSQpp0Qp.GO8Srw==' -LINKEDIN_REDIRECT_URI = 'http://127.0.0.1:8000/jobs/linkedin/callback/' +LINKEDIN_CLIENT_ID = "867jwsiyem1504" +LINKEDIN_CLIENT_SECRET = "WPL_AP1.QNH5lYnfRSQpp0Qp.GO8Srw==" +LINKEDIN_REDIRECT_URI = "http://127.0.0.1:8000/jobs/linkedin/callback/" Q_CLUSTER = { - 'name': 'KAAUH_CLUSTER', - 'workers': 8, - 'recycle': 500, - 'timeout': 60, - 'max_attempts': 1, - 'compress': True, - 'save_limit': 250, - 'queue_limit': 500, - 'cpu_affinity': 1, - 'label': 'Django Q2', - 'redis': { - 'host': '127.0.0.1', - 'port': 6379, - 'db': 3, }, - 'ALT_CLUSTERS': { - 'long': { - 'timeout': 3000, - 'retry': 3600, - 'max_attempts': 2, + "name": "KAAUH_CLUSTER", + "workers": 8, + "recycle": 500, + "timeout": 60, + "max_attempts": 1, + "compress": True, + "save_limit": 250, + "queue_limit": 500, + "cpu_affinity": 1, + "label": "Django Q2", + "redis": { + "host": "127.0.0.1", + "port": 6379, + "db": 3, + }, + "ALT_CLUSTERS": { + "long": { + "timeout": 3000, + "retry": 3600, + "max_attempts": 2, }, - 'short': { - 'timeout': 10, - 'max_attempts': 1, + "short": { + "timeout": 10, + "max_attempts": 1, }, - } + }, } customColorPalette = [ - { - 'color': 'hsl(4, 90%, 58%)', - 'label': 'Red' - }, - { - 'color': 'hsl(340, 82%, 52%)', - 'label': 'Pink' - }, - { - 'color': 'hsl(291, 64%, 42%)', - 'label': 'Purple' - }, - { - 'color': 'hsl(262, 52%, 47%)', - 'label': 'Deep Purple' - }, - { - 'color': 'hsl(231, 48%, 48%)', - 'label': 'Indigo' - }, - { - 'color': 'hsl(207, 90%, 54%)', - 'label': 'Blue' - }, - ] + {"color": "hsl(4, 90%, 58%)", "label": "Red"}, + {"color": "hsl(340, 82%, 52%)", "label": "Pink"}, + {"color": "hsl(291, 64%, 42%)", "label": "Purple"}, + {"color": "hsl(262, 52%, 47%)", "label": "Deep Purple"}, + {"color": "hsl(231, 48%, 48%)", "label": "Indigo"}, + {"color": "hsl(207, 90%, 54%)", "label": "Blue"}, +] # CKEDITOR_5_CUSTOM_CSS = 'path_to.css' # optional # CKEDITOR_5_FILE_STORAGE = "path_to_storage.CustomStorage" # optional CKEDITOR_5_CONFIGS = { - 'default': { - 'toolbar': { - 'items': ['heading', '|', 'bold', 'italic', 'link', - 'bulletedList', 'numberedList', 'blockQuote', 'imageUpload', ], - } - + "default": { + "toolbar": { + "items": [ + "heading", + "|", + "bold", + "italic", + "link", + "bulletedList", + "numberedList", + "blockQuote", + "imageUpload", + ], + } }, - 'extends': { - 'blockToolbar': [ - 'paragraph', 'heading1', 'heading2', 'heading3', - '|', - 'bulletedList', 'numberedList', - '|', - 'blockQuote', + "extends": { + "blockToolbar": [ + "paragraph", + "heading1", + "heading2", + "heading3", + "|", + "bulletedList", + "numberedList", + "|", + "blockQuote", ], - 'toolbar': { - 'items': ['heading', '|', 'outdent', 'indent', '|', 'bold', 'italic', 'link', 'underline', 'strikethrough', - 'code','subscript', 'superscript', 'highlight', '|', 'codeBlock', 'sourceEditing', 'insertImage', - 'bulletedList', 'numberedList', 'todoList', '|', 'blockQuote', 'imageUpload', '|', - 'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', 'mediaEmbed', 'removeFormat', - 'insertTable', - ], - 'shouldNotGroupWhenFull': 'true' + "toolbar": { + "items": [ + "heading", + "|", + "outdent", + "indent", + "|", + "bold", + "italic", + "link", + "underline", + "strikethrough", + "code", + "subscript", + "superscript", + "highlight", + "|", + "codeBlock", + "sourceEditing", + "insertImage", + "bulletedList", + "numberedList", + "todoList", + "|", + "blockQuote", + "imageUpload", + "|", + "fontSize", + "fontFamily", + "fontColor", + "fontBackgroundColor", + "mediaEmbed", + "removeFormat", + "insertTable", + ], + "shouldNotGroupWhenFull": "true", }, - 'image': { - 'toolbar': ['imageTextAlternative', '|', 'imageStyle:alignLeft', - 'imageStyle:alignRight', 'imageStyle:alignCenter', 'imageStyle:side', '|'], - 'styles': [ - 'full', - 'side', - 'alignLeft', - 'alignRight', - 'alignCenter', - ] - + "image": { + "toolbar": [ + "imageTextAlternative", + "|", + "imageStyle:alignLeft", + "imageStyle:alignRight", + "imageStyle:alignCenter", + "imageStyle:side", + "|", + ], + "styles": [ + "full", + "side", + "alignLeft", + "alignRight", + "alignCenter", + ], }, - 'table': { - 'contentToolbar': [ 'tableColumn', 'tableRow', 'mergeTableCells', - 'tableProperties', 'tableCellProperties' ], - 'tableProperties': { - 'borderColors': customColorPalette, - 'backgroundColors': customColorPalette + "table": { + "contentToolbar": [ + "tableColumn", + "tableRow", + "mergeTableCells", + "tableProperties", + "tableCellProperties", + ], + "tableProperties": { + "borderColors": customColorPalette, + "backgroundColors": customColorPalette, + }, + "tableCellProperties": { + "borderColors": customColorPalette, + "backgroundColors": customColorPalette, }, - 'tableCellProperties': { - 'borderColors': customColorPalette, - 'backgroundColors': customColorPalette - } }, - 'heading' : { - 'options': [ - { 'model': 'paragraph', 'title': 'Paragraph', 'class': 'ck-heading_paragraph' }, - { 'model': 'heading1', 'view': 'h1', 'title': 'Heading 1', 'class': 'ck-heading_heading1' }, - { 'model': 'heading2', 'view': 'h2', 'title': 'Heading 2', 'class': 'ck-heading_heading2' }, - { 'model': 'heading3', 'view': 'h3', 'title': 'Heading 3', 'class': 'ck-heading_heading3' } + "heading": { + "options": [ + { + "model": "paragraph", + "title": "Paragraph", + "class": "ck-heading_paragraph", + }, + { + "model": "heading1", + "view": "h1", + "title": "Heading 1", + "class": "ck-heading_heading1", + }, + { + "model": "heading2", + "view": "h2", + "title": "Heading 2", + "class": "ck-heading_heading2", + }, + { + "model": "heading3", + "view": "h3", + "title": "Heading 3", + "class": "ck-heading_heading3", + }, ] + }, + }, + "list": { + "properties": { + "styles": "true", + "startIndex": "true", + "reversed": "true", } }, - 'list': { - 'properties': { - 'styles': 'true', - 'startIndex': 'true', - 'reversed': 'true', - } - } } # Define a constant in settings.py to specify file upload permissions -CKEDITOR_5_FILE_UPLOAD_PERMISSION = "staff" # Possible values: "staff", "authenticated", "any" +CKEDITOR_5_FILE_UPLOAD_PERMISSION = ( + "staff" # Possible values: "staff", "authenticated", "any" @@ -411,3 +479,7 @@ from django.contrib.messages import constants as messages MESSAGE_TAGS = { messages.ERROR: 'danger', } +) + +# Custom User Model +AUTH_USER_MODEL = "recruitment.CustomUser" diff --git a/NorahUniversity/urls.py b/NorahUniversity/urls.py index 432af79..555b9ce 100644 --- a/NorahUniversity/urls.py +++ b/NorahUniversity/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ path('application//', views.application_submit_form, name='application_submit_form'), path('application//submit/', views.application_submit, name='application_submit'), path('application//apply/', views.application_detail, name='application_detail'), + path('application//signup/', views.candidate_signup, name='candidate_signup'), path('application//success/', views.application_success, name='application_success'), path('application/applicant/profile', views.applicant_profile, name='applicant_profile'), diff --git a/apply_all_translations.py b/apply_all_translations.py new file mode 100644 index 0000000..582b76f --- /dev/null +++ b/apply_all_translations.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +""" +Script to apply all batch translations to the main django.po file +""" + +import re + +def apply_all_translations(): + """ + Apply all translations to the main django.po file + """ + # All translations from batches 02-35 + translations = { + "The date and time this notification is scheduled to be sent.": "التاريخ والوقت المحدد لإرسال هذا الإشعار.", + "Send Attempts": "محاولات الإرسال", + "Failed to start the job posting process. Please try again.": "فشل في بدء عملية نشر الوظيفة. يرجى المحاولة مرة أخرى.", + "You don't have permission to view this page.": "ليس لديك إذن لعرض هذه الصفحة.", + "Account Inactive": "الحساب غير نشط", + "Princess Nourah bint Abdulrahman University": "جامعة الأميرة نورة بنت عبدالرحمن", + "Manage your personal details and security.": "إدارة تفاصيلك الشخصية والأمان.", + "Primary": "أساسي", + "Verified": "موثق", + "Unverified": "غير موثق", + "Make Primary": "جعل أساسي", + "Remove": "إزالة", + "Add Email Address": "إضافة عنوان بريد إلكتروني", + "Hello,": "مرحباً،", + "Confirm My KAAUH ATS Email": "تأكيد بريدي الإلكتروني في نظام توظيف جامعة نورة", + "Alternatively, copy and paste this link into your browser:": "بدلاً من ذلك، انسخ والصق هذا الرابط في متصفحك:", + "Password Reset Request": "طلب إعادة تعيين كلمة المرور", + "Click Here to Reset Your Password": "اضغط هنا لإعادة تعيين كلمة المرور", + "This link is only valid for a limited time.": "هذا الرابط صالح لفترة محدودة فقط.", + "Thank you,": "شكراً لك،", + "KAAUH ATS Team": "فريق نظام توظيف جامعة نورة", + "Confirm Email Address": "تأكيد عنوان البريد الإلكتروني", + "Account Verification": "التحقق من الحساب", + "Verify your email to secure your account and unlock full features.": "تحقق من بريدك الإلكتروني لتأمين حسابك وإلغاء قفل جميع الميزات.", + "Confirm Your Email Address": "تأكيد عنوان بريدك الإلكتروني", + "Verification Failed": "فشل التحقق", + "The email confirmation link is expired or invalid.": "رابط تأكيد البريد الإلكتروني منتهي الصلاحية أو غير صالح.", + "Keep me signed in": "ابق مسجلاً للدخول", + "Return to Profile": "العودة إلى الملف الشخصي", + "Enter your e-mail address to reset your password.": "أدخل عنوان بريدك الإلكتروني لإعادة تعيين كلمة المرور.", + "Remember your password?": "تتذكر كلمة المرور؟", + "Log In": "تسجيل الدخول", + "Password Reset Sent": "تم إرسال إعادة تعيين كلمة المرور", + "Return to Login": "العودة إلى تسجيل الدخول", + "Please enter your new password below.": "يرجى إدخال كلمة المرور الجديدة أدناه.", + "Logout": "تسجيل الخروج", + "Yes": "نعم", + "No": "لا", + "Success": "نجح", + "Login": "تسجيل الدخول", + "Link": "ربط", + "Clear": "مسح", + "All": "الكل", + "Comments": "التعليقات", + "Save": "حفظ", + "Notes": "ملاحظات", + "New": "جديد", + "Users": "المستخدمون", + "Filter": "تصفية", + "Home": "الرئيسية", + "Username": "اسم المستخدم", + "Modified": "تم التعديل", + "Unlink": "فك الربط", + "Group": "تجميع", + "Export": "تصدير", + "Import": "استيراد", + "None": "لا شيء", + "Add": "إضافة", + "True": "صحيح", + "False": "خطأ", + } + + main_po_file = "locale/ar/LC_MESSAGES/django.po" + + # Read the main django.po file + with open(main_po_file, 'r', encoding='utf-8') as f: + main_content = f.read() + + # Apply translations to main file + updated_content = main_content + applied_count = 0 + + for english, arabic in translations.items(): + # Pattern to find msgid followed by empty msgstr + pattern = rf'(msgid "{re.escape(english)}"\s*\nmsgstr) ""' + replacement = rf'\1 "{arabic}"' + + if re.search(pattern, updated_content): + updated_content = re.sub(pattern, replacement, updated_content) + applied_count += 1 + print(f"✓ Applied: '{english}' -> '{arabic}'") + else: + print(f"✗ Not found: '{english}'") + + # Write updated content back to main file + with open(main_po_file, 'w', encoding='utf-8') as f: + f.write(updated_content) + + print(f"\nApplied {applied_count} translations to {main_po_file}") + return applied_count + +def main(): + """Main function to apply all translations""" + print("Applying all batch translations to main django.po file...") + applied_count = apply_all_translations() + + if applied_count > 0: + print(f"\n✅ Successfully applied {applied_count} translations!") + print("Next steps:") + print("1. Run: python manage.py compilemessages") + print("2. Test the translations in the application") + else: + print("\n❌ No translations were applied.") + +if __name__ == "__main__": + main() diff --git a/apply_batch_translations.py b/apply_batch_translations.py new file mode 100644 index 0000000..6249bdd --- /dev/null +++ b/apply_batch_translations.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +""" +Script to apply completed batch translations back to the main django.po file +""" + +import re + +def apply_batch_to_main_po(batch_file_path, main_po_path): + """ + Apply translations from a completed batch file to the main django.po file + """ + # Read the completed batch file + with open(batch_file_path, 'r', encoding='utf-8') as f: + batch_content = f.read() + + # Extract translations from batch file + translations = {} + current_msgid = None + + lines = batch_content.split('\n') + i = 0 + while i < len(lines): + line = lines[i].strip() + if line.startswith('msgid: "'): + current_msgid = line[7:-1] # Extract msgid content + # Look for the Arabic translation line (next non-empty line after "Arabic Translation:") + j = i + 1 + while j < len(lines) and not lines[j].strip().startswith('msgstr: "'): + j += 1 + if j < len(lines) and lines[j].strip().startswith('msgstr: "'): + translation = lines[j].strip()[8:-1] # Extract msgstr content + if translation and current_msgid and translation != '""': # Only add non-empty translations + translations[current_msgid] = translation + print(f"Extracted: '{current_msgid}' -> '{translation}'") + current_msgid = None + i += 1 + + print(f"Found {len(translations)} translations to apply") + + # Read the main django.po file + with open(main_po_path, 'r', encoding='utf-8') as f: + main_content = f.read() + + # Apply translations to main file + updated_content = main_content + applied_count = 0 + + for english, arabic in translations.items(): + # Pattern to find msgid followed by empty msgstr + pattern = rf'(msgid "{re.escape(english)}"\s*\nmsgstr) ""' + replacement = rf'\1 "{arabic}"' + + if re.search(pattern, updated_content): + updated_content = re.sub(pattern, replacement, updated_content) + applied_count += 1 + print(f"✓ Applied: '{english}' -> '{arabic}'") + else: + print(f"✗ Not found: '{english}'") + + # Write updated content back to main file + with open(main_po_path, 'w', encoding='utf-8') as f: + f.write(updated_content) + + print(f"\nApplied {applied_count} translations to {main_po_path}") + return applied_count + +def main(): + """Main function to apply batch 01 translations""" + batch_file = "translation_batch_01_completed.txt" + main_po_file = "locale/ar/LC_MESSAGES/django.po" + + print("Applying Batch 01 translations to main django.po file...") + applied_count = apply_batch_to_main_po(batch_file, main_po_file) + + if applied_count > 0: + print(f"\n✅ Successfully applied {applied_count} translations!") + print("Next steps:") + print("1. Run: python manage.py compilemessages") + print("2. Test the translations in the application") + print("3. Continue with the next batch") + else: + print("\n❌ No translations were applied. Please check the batch file format.") + +if __name__ == "__main__": + main() diff --git a/apply_translations_direct.py b/apply_translations_direct.py new file mode 100644 index 0000000..1c40a2b --- /dev/null +++ b/apply_translations_direct.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +""" +Script to directly apply Arabic translations to the main django.po file +""" + +import re + +def apply_translations_direct(): + """ + Apply translations directly to the main django.po file + """ + # Arabic translations for batch 01 + translations = { + "Website": "الموقع الإلكتروني", + "Admin Notes": "ملاحظات المسؤول", + "Save Assignment": "حفظ التكليف", + "Assignment": "التكليف", + "Expires At": "ينتهي في", + "Access Token": "رمز الوصول", + "Subject": "الموضوع", + "Recipients": "المستلمون", + "Internal staff involved in the recruitment process for this job": "الموظفون الداخليون المشاركون في عملية التوظيف لهذه الوظيفة", + "External Participant": "مشارك خارجي", + "External participants involved in the recruitment process for this job": "المشاركون الخارجيون المشاركون في عملية التوظيف لهذه الوظيفة", + "Reason for canceling the job posting": "سبب إلغاء نشر الوظيفة", + "Name of person who cancelled this job": "اسم الشخص الذي ألغى هذه الوظيفة", + "Hired": "تم التوظيف", + "Author": "المؤلف", + "Endpoint URL for sending candidate data (for outbound sync)": "عنوان URL لنقطة النهاية لإرسال بيانات المرشح (للمزامنة الصادرة)", + "HTTP method for outbound sync requests": "طريقة HTTP لطلبات المزامنة الصادرة", + "HTTP method for connection testing": "طريقة HTTP لاختبار الاتصال", + "Custom Headers": "رؤوس مخصصة", + "JSON object with custom HTTP headers for sync requests": "كائن JSON يحتوي على رؤوس HTTP مخصصة لطلبات المزامنة", + "Supports Outbound Sync": "يدعم المزامنة الصادرة", + "Whether this source supports receiving candidate data from ATS": "ما إذا كان هذا المصدر يدعم استقبال بيانات المرشح من نظام تتبع المتقدمين", + "Expired": "منتهي الصلاحية", + "Maximum candidates agency can submit for this job": "الحد الأقصى للمرشحين الذين يمكن للوكالة تقديمهم لهذه الوظيفة" + } + + main_po_file = "locale/ar/LC_MESSAGES/django.po" + + # Read the main django.po file + with open(main_po_file, 'r', encoding='utf-8') as f: + main_content = f.read() + + # Apply translations to main file + updated_content = main_content + applied_count = 0 + + for english, arabic in translations.items(): + # Pattern to find msgid followed by empty msgstr + pattern = rf'(msgid "{re.escape(english)}"\s*\nmsgstr) ""' + replacement = rf'\1 "{arabic}"' + + if re.search(pattern, updated_content): + updated_content = re.sub(pattern, replacement, updated_content) + applied_count += 1 + print(f"✓ Applied: '{english}' -> '{arabic}'") + else: + print(f"✗ Not found: '{english}'") + + # Write updated content back to main file + with open(main_po_file, 'w', encoding='utf-8') as f: + f.write(updated_content) + + print(f"\nApplied {applied_count} translations to {main_po_file}") + return applied_count + +def main(): + """Main function to apply batch 01 translations""" + print("Applying Batch 01 translations directly to main django.po file...") + applied_count = apply_translations_direct() + + if applied_count > 0: + print(f"\n✅ Successfully applied {applied_count} translations!") + print("Next steps:") + print("1. Run: python manage.py compilemessages") + print("2. Test the translations in the application") + print("3. Continue with the next batch") + else: + print("\n❌ No translations were applied.") + +if __name__ == "__main__": + main() diff --git a/comprehensive_translation_merger.py b/comprehensive_translation_merger.py new file mode 100644 index 0000000..c131ab6 --- /dev/null +++ b/comprehensive_translation_merger.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +""" +Comprehensive Translation Merger +Merges all 35 translation batch files into the main django.po file +""" + +import os +import re +import glob + +def parse_batch_file(filename): + """Parse a batch file and extract English-Arabic translation pairs""" + translations = {} + + try: + with open(filename, 'r', encoding='utf-8') as f: + content = f.read() + + # Pattern to match the format in completed batch files: + # msgid: "English text" + # msgstr: "" + # Arabic Translation: + # msgstr: "Arabic text" + pattern = r'msgid:\s*"([^"]*?)"\s*\nmsgstr:\s*""\s*\nArabic Translation:\s*\nmsgstr:\s*"([^"]*?)"' + + matches = re.findall(pattern, content, re.MULTILINE | re.DOTALL) + + for english, arabic in matches: + english = english.strip() + arabic = arabic.strip() + + # Skip empty or invalid entries + if english and arabic and len(english) > 1 and len(arabic) > 1: + translations[english] = arabic + + except Exception as e: + print(f"Error parsing {filename}: {e}") + + return translations + +def parse_current_django_po(): + """Parse the current django.po file and extract existing translations""" + po_file = 'locale/ar/LC_MESSAGES/django.po' + + if not os.path.exists(po_file): + return {}, [] + + with open(po_file, 'r', encoding='utf-8') as f: + content = f.read() + + # Extract msgid/msgstr pairs + pattern = r'msgid\s+"([^"]*?)"\s*\nmsgstr\s+"([^"]*?)"' + matches = re.findall(pattern, content) + + existing_translations = {} + for msgid, msgstr in matches: + existing_translations[msgid] = msgstr + + # Extract the header and footer + parts = re.split(r'(msgid\s+"[^"]*?"\s*\nmsgstr\s+"[^"]*?")', content) + + return existing_translations, parts + +def create_comprehensive_translation_dict(): + """Create a comprehensive translation dictionary from all batch files""" + all_translations = {} + + # Get all batch files + batch_files = glob.glob('translation_batch_*.txt') + batch_files.sort() # Process in order + + print(f"Found {len(batch_files)} batch files") + + for batch_file in batch_files: + print(f"Processing {batch_file}...") + batch_translations = parse_batch_file(batch_file) + + for english, arabic in batch_translations.items(): + if english not in all_translations: + all_translations[english] = arabic + else: + # Keep the first translation found, but note duplicates + print(f" Duplicate found: '{english}' -> '{arabic}' (existing: '{all_translations[english]}')") + + print(f"Total unique translations: {len(all_translations)}") + return all_translations + +def update_django_po(translations): + """Update the django.po file with new translations""" + po_file = 'locale/ar/LC_MESSAGES/django.po' + + # Read current file + with open(po_file, 'r', encoding='utf-8') as f: + content = f.read() + + lines = content.split('\n') + new_lines = [] + i = 0 + updated_count = 0 + + while i < len(lines): + line = lines[i] + + if line.startswith('msgid '): + # Extract the msgid content + msgid_match = re.match(r'msgid\s+"([^"]*)"', line) + if msgid_match: + msgid = msgid_match.group(1) + + # Look for the corresponding msgstr + if i + 1 < len(lines) and lines[i + 1].startswith('msgstr '): + msgstr_match = re.match(r'msgstr\s+"([^"]*)"', lines[i + 1]) + current_msgstr = msgstr_match.group(1) if msgstr_match else "" + + # Check if we have a translation for this msgid + if msgid in translations and (not current_msgstr or current_msgstr == ""): + # Update the translation + new_translation = translations[msgid] + new_lines.append(line) # Keep msgid line + new_lines.append(f'msgstr "{new_translation}"') # Update msgstr + updated_count += 1 + print(f" Updated: '{msgid}' -> '{new_translation}'") + else: + # Keep existing translation + new_lines.append(line) + new_lines.append(lines[i + 1]) + + i += 2 # Skip both msgid and msgstr lines + continue + + new_lines.append(line) + i += 1 + + # Write updated content + new_content = '\n'.join(new_lines) + + # Create backup + backup_file = po_file + '.backup' + with open(backup_file, 'w', encoding='utf-8') as f: + f.write(content) + print(f"Created backup: {backup_file}") + + # Write updated file + with open(po_file, 'w', encoding='utf-8') as f: + f.write(new_content) + + print(f"Updated {updated_count} translations in {po_file}") + return updated_count + +def add_missing_translations(translations): + """Add completely missing translations to django.po""" + po_file = 'locale/ar/LC_MESSAGES/django.po' + + with open(po_file, 'r', encoding='utf-8') as f: + content = f.read() + + existing_translations, _ = parse_current_django_po() + + # Find translations that don't exist in the .po file at all + missing_translations = {} + for english, arabic in translations.items(): + if english not in existing_translations: + missing_translations[english] = arabic + + if missing_translations: + print(f"Found {len(missing_translations)} completely missing translations") + + # Add missing translations to the end of the file + with open(po_file, 'a', encoding='utf-8') as f: + f.write('\n\n# Auto-added missing translations\n') + for english, arabic in missing_translations.items(): + f.write(f'\nmsgid "{english}"\n') + f.write(f'msgstr "{arabic}"\n') + + print(f"Added {len(missing_translations)} missing translations") + else: + print("No missing translations found") + + return len(missing_translations) + +def main(): + """Main function to merge all translations""" + print("🚀 Starting Comprehensive Translation Merger") + print("=" * 50) + + # Step 1: Create comprehensive translation dictionary + print("\n📚 Step 1: Building comprehensive translation dictionary...") + translations = create_comprehensive_translation_dict() + + # Step 2: Update existing translations in django.po + print("\n🔄 Step 2: Updating existing translations in django.po...") + updated_count = update_django_po(translations) + + # Step 3: Add completely missing translations + print("\n➕ Step 3: Adding missing translations...") + added_count = add_missing_translations(translations) + + # Step 4: Summary + print("\n📊 Summary:") + print(f" Total translations available: {len(translations)}") + print(f" Updated existing translations: {updated_count}") + print(f" Added missing translations: {added_count}") + print(f" Total translations processed: {updated_count + added_count}") + + print("\n✅ Translation merge completed!") + print("\n📝 Next steps:") + print(" 1. Run: python manage.py compilemessages") + print(" 2. Test Arabic translations in the browser") + print(" 3. Verify language switching functionality") + +if __name__ == "__main__": + main() diff --git a/empty_translations_summary.txt b/empty_translations_summary.txt new file mode 100644 index 0000000..44714a5 --- /dev/null +++ b/empty_translations_summary.txt @@ -0,0 +1,48 @@ +EMPTY TRANSLATIONS SUMMARY REPORT +================================================== + +Total empty translations: 843 + +UI Elements (Buttons, Links): 20 +Form Fields & Inputs: 55 +Messages (Error/Success/Warning): 27 +Navigation & Pages: 7 +Other: 734 + +SAMPLE ENTRIES: +------------------------------ + +UI Elements (showing first 5): + Line 1491: "Click Here to Reset Your Password" + Line 2685: "Email will be sent to all selected recipients" + Line 2743: "Click here to join meeting" + Line 2813: "Candidates to Schedule (Hold Ctrl/Cmd to select multiple)" + Line 4057: "Select the agency job assignment" + +Form Fields (showing first 5): + Line 1658: "Enter your e-mail address to reset your password." + Line 1712: "Please enter your new password below." + Line 2077: "Form:" + Line 2099: "Field Property" + Line 2133: "Field Required" + +Messages (showing first 5): + Line 1214: "Notification Message" + Line 2569: "Success" + Line 2776: "An unknown error occurred." + Line 2780: "An error occurred while processing your request." + Line 2872: "Your application has been submitted successfully" + +Navigation (showing first 5): + Line 1295: "You don't have permission to view this page." + Line 2232: "Page" + Line 6253: "Admin Settings Dashboard" + Line 6716: "That page number is not an integer" + Line 6720: "That page number is less than 1" + +Other (showing first 5): + Line 7: "" + Line 1041: "Number of candidates submitted so far" + Line 1052: "Deadline for agency to submit candidates" + Line 1068: "Original deadline before extensions" + Line 1078: "Agency Job Assignment" diff --git a/find_empty_translations.py b/find_empty_translations.py new file mode 100644 index 0000000..ca0ea4d --- /dev/null +++ b/find_empty_translations.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +""" +Script to find empty msgstr entries in django.po file and organize them into batches +for systematic Arabic translation work. +""" + +import re +import os +from typing import List, Tuple + +def find_empty_translations(po_file_path: str) -> List[Tuple[int, str, str]]: + """ + Find all entries with empty msgstr in the django.po file. + + Returns: + List of tuples: (line_number, msgid, context_before) + """ + empty_translations = [] + + with open(po_file_path, 'r', encoding='utf-8') as file: + lines = file.readlines() + + i = 0 + while i < len(lines): + line = lines[i].strip() + + # Look for msgid + if line.startswith('msgid '): + msgid = line[7:-1] # Remove 'msgid "' and ending '"' + + # Check next few lines for msgstr + j = i + 1 + msgstr_found = False + msgstr_empty = False + + while j < len(lines) and j < i + 5: # Look ahead max 5 lines + next_line = lines[j].strip() + + if next_line.startswith('msgstr '): + msgstr_found = True + if next_line == 'msgstr ""': + msgstr_empty = True + break + elif next_line.startswith('msgid ') or next_line.startswith('#'): + # Found next entry or comment, no msgstr for current msgid + break + j += 1 + + if msgstr_found and msgstr_empty: + # Get context (previous 2-3 lines) + context_start = max(0, i - 3) + context = ''.join(lines[context_start:i]) + empty_translations.append((i + 1, msgid, context)) + + i = j # Skip to after msgstr + else: + i += 1 + + return empty_translations + +def create_batch_files(empty_translations: List[Tuple[int, str, str]], batch_size: int = 25): + """ + Create batch files with empty translations for systematic work. + """ + total_batches = (len(empty_translations) + batch_size - 1) // batch_size + + print(f"Found {len(empty_translations)} empty translations") + print(f"Creating {total_batches} batches of ~{batch_size} translations each") + + for batch_num in range(total_batches): + start_idx = batch_num * batch_size + end_idx = min(start_idx + batch_size, len(empty_translations)) + batch_translations = empty_translations[start_idx:end_idx] + + batch_filename = f"translation_batch_{batch_num + 1:02d}.txt" + + with open(batch_filename, 'w', encoding='utf-8') as batch_file: + batch_file.write(f"=== TRANSLATION BATCH {batch_num + 1:02d} ===\n") + batch_file.write(f"Translations {start_idx + 1}-{end_idx} of {len(empty_translations)}\n") + batch_file.write("=" * 60 + "\n\n") + + for line_num, msgid, context in batch_translations: + batch_file.write(f"Line {line_num}:\n") + batch_file.write(f"msgid: \"{msgid}\"\n") + batch_file.write(f"msgstr: \"\"\n") + batch_file.write(f"\nArabic Translation: \n") + batch_file.write(f"msgstr: \"\"\n") + batch_file.write("-" * 40 + "\n\n") + + print(f"Created {batch_filename} with {len(batch_translations)} translations") + +def create_summary_report(empty_translations: List[Tuple[int, str, str]]): + """ + Create a summary report of all empty translations. + """ + with open("empty_translations_summary.txt", 'w', encoding='utf-8') as report: + report.write("EMPTY TRANSLATIONS SUMMARY REPORT\n") + report.write("=" * 50 + "\n\n") + report.write(f"Total empty translations: {len(empty_translations)}\n\n") + + # Group by type/pattern for better organization + ui_elements = [] + form_fields = [] + messages = [] + navigation = [] + other = [] + + for line_num, msgid, context in empty_translations: + msgid_lower = msgid.lower() + + if any(word in msgid_lower for word in ['button', 'btn', 'click', 'select']): + ui_elements.append((line_num, msgid)) + elif any(word in msgid_lower for word in ['field', 'form', 'input', 'enter']): + form_fields.append((line_num, msgid)) + elif any(word in msgid_lower for word in ['error', 'success', 'warning', 'message']): + messages.append((line_num, msgid)) + elif any(word in msgid_lower for word in ['menu', 'nav', 'page', 'dashboard']): + navigation.append((line_num, msgid)) + else: + other.append((line_num, msgid)) + + report.write(f"UI Elements (Buttons, Links): {len(ui_elements)}\n") + report.write(f"Form Fields & Inputs: {len(form_fields)}\n") + report.write(f"Messages (Error/Success/Warning): {len(messages)}\n") + report.write(f"Navigation & Pages: {len(navigation)}\n") + report.write(f"Other: {len(other)}\n\n") + + report.write("SAMPLE ENTRIES:\n") + report.write("-" * 30 + "\n") + + for category, name, sample_count in [ + (ui_elements, "UI Elements", 5), + (form_fields, "Form Fields", 5), + (messages, "Messages", 5), + (navigation, "Navigation", 5), + (other, "Other", 5) + ]: + if category: + report.write(f"\n{name} (showing first {min(len(category), sample_count)}):\n") + for line_num, msgid in category[:sample_count]: + report.write(f" Line {line_num}: \"{msgid}\"\n") + +def main(): + """Main function to process the django.po file.""" + po_file_path = "locale/ar/LC_MESSAGES/django.po" + + if not os.path.exists(po_file_path): + print(f"Error: {po_file_path} not found!") + return + + print("Scanning for empty translations...") + empty_translations = find_empty_translations(po_file_path) + + if not empty_translations: + print("No empty translations found! All msgstr entries have translations.") + return + + # Create batch files + create_batch_files(empty_translations, batch_size=25) + + # Create summary report + create_summary_report(empty_translations) + + print(f"\nProcess completed!") + print(f"Check the generated batch files: translation_batch_*.txt") + print(f"Summary report: empty_translations_summary.txt") + print(f"\nNext steps:") + print(f"1. Work through each batch file systematically") + print(f"2. Add Arabic translations to each empty msgstr") + print(f"3. Update the main django.po file with completed translations") + +if __name__ == "__main__": + main() diff --git a/fix_po_duplicates.py b/fix_po_duplicates.py new file mode 100644 index 0000000..c51f1d2 --- /dev/null +++ b/fix_po_duplicates.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +""" +Fix duplicate message definitions in .po files +""" + +import re +import os + +def fix_po_duplicates(po_file): + """Remove duplicate message definitions from a .po file""" + + print(f"Processing {po_file}...") + + with open(po_file, 'r', encoding='utf-8') as f: + content = f.read() + + # Split into entries + entries = [] + current_entry = [] + lines = content.split('\n') + + i = 0 + while i < len(lines): + line = lines[i] + + if line.startswith('msgid '): + # Start of new entry + if current_entry: + entries.append('\n'.join(current_entry)) + current_entry = [line] + elif line.startswith('msgstr ') and current_entry: + # End of entry + current_entry.append(line) + entries.append('\n'.join(current_entry)) + current_entry = [] + else: + if current_entry: + current_entry.append(line) + + i += 1 + + # Add the last entry if it exists + if current_entry: + entries.append('\n'.join(current_entry)) + + # Remove duplicates, keeping the first occurrence + seen_msgids = set() + unique_entries = [] + + for entry in entries: + # Extract msgid + msgid_match = re.search(r'msgid\s+"([^"]*)"', entry) + if msgid_match: + msgid = msgid_match.group(1) + if msgid not in seen_msgids: + seen_msgids.add(msgid) + unique_entries.append(entry) + else: + print(f" Removing duplicate: {msgid}") + else: + # Header or other entry, keep it + unique_entries.append(entry) + + # Rebuild content + new_content = '\n\n'.join(unique_entries) + + # Write back to file + with open(po_file, 'w', encoding='utf-8') as f: + f.write(new_content) + + print(f" Fixed {po_file} - removed {len(entries) - len(unique_entries)} duplicates") + +def main(): + """Fix duplicates in both English and Arabic .po files""" + + po_files = [ + 'locale/ar/LC_MESSAGES/django.po', + 'locale/en/LC_MESSAGES/django.po' + ] + + for po_file in po_files: + if os.path.exists(po_file): + fix_po_duplicates(po_file) + else: + print(f"File not found: {po_file}") + + print("\n✅ Duplicate fixing completed!") + print("Now run: python manage.py compilemessages") + +if __name__ == "__main__": + main() diff --git a/locale/ar/LC_MESSAGES/django.po b/locale/ar/LC_MESSAGES/django.po index a97763b..9f5332b 100644 --- a/locale/ar/LC_MESSAGES/django.po +++ b/locale/ar/LC_MESSAGES/django.po @@ -8,11 +8,15 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" +<<<<<<< HEAD "POT-Creation-Date: 2025-11-03 13:54+0300\n" +======= +"POT-Creation-Date: 2025-11-03 12:14+0300\n" +>>>>>>> update1 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" -"Language: \n" +"Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -42,10 +46,17 @@ msgstr "اسم العائلة" #: templates/recruitment/agency_list.html:187 #: templates/recruitment/agency_portal_assignment_detail.html:560 msgid "Phone" +<<<<<<< HEAD msgstr "الهاتف" #: recruitment/forms.py:243 recruitment/models.py:426 #: recruitment/models.py:1683 recruitment/models.py:1757 +======= +msgstr "رقم الهاتف" + +#: recruitment/forms.py:243 recruitment/models.py:426 +#: recruitment/models.py:1664 recruitment/models.py:1738 +>>>>>>> update1 #: templates/jobs/job_candidates_list.html:227 #: templates/meetings/meeting_details.html:305 #: templates/participants/participants_list.html:213 @@ -57,16 +68,21 @@ msgstr "الهاتف" #: templates/recruitment/candidate_detail.html:337 #: templates/recruitment/candidate_list.html:270 #: templates/recruitment/notification_list.html:50 +<<<<<<< HEAD #: templates/user/admin_settings.html:176 +======= +#: templates/user/admin_settings.html:175 +>>>>>>> update1 msgid "Email" msgstr "البريد الإلكتروني" #: recruitment/forms.py:244 recruitment/forms.py:1067 recruitment/models.py:429 msgid "Resume" -msgstr "" +msgstr "السيرة الذاتية" #: recruitment/forms.py:245 msgid "Hiring Type" +<<<<<<< HEAD msgstr "" #: recruitment/forms.py:246 recruitment/models.py:180 recruitment/models.py:501 @@ -102,26 +118,69 @@ msgid "Enter email" msgstr "أدخل الملاحظات" #: recruitment/forms.py:281 +======= +msgstr "نوع التوظيف" + +#: recruitment/forms.py:246 recruitment/models.py:180 recruitment/models.py:505 +#: recruitment/models.py:1291 +msgid "Hiring Agency" +msgstr "وكالة التوظيف" + +#: recruitment/forms.py:249 +#: templates/recruitment/agency_portal_submit_candidate.html:194 +msgid "Enter first name" +msgstr "أدخل الاسم الأول" + +#: recruitment/forms.py:250 +#: templates/recruitment/agency_portal_submit_candidate.html:205 +msgid "Enter last name" +msgstr "أدخل اسم العائلة" + +#: recruitment/forms.py:251 +#: templates/recruitment/agency_portal_submit_candidate.html:237 +msgid "Enter phone number" +msgstr "أدخل رقم الهاتف" + +#: recruitment/forms.py:252 +msgid "Enter email" +msgstr "أدخل البريد الإلكتروني" + +#: recruitment/forms.py:281 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/export.html:56 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_form.html:21 +>>>>>>> update1 msgid "Submit" msgstr "إرسال" #: recruitment/forms.py:292 +<<<<<<< HEAD #, fuzzy #| msgid "Next Action Date" msgid "New Application Stage" msgstr "تاريخ الإجراء التالي" #: recruitment/forms.py:303 recruitment/models.py:736 +======= +msgid "New Application Stage" +msgstr "مرحلة تقديم جديدة" + +#: recruitment/forms.py:303 recruitment/models.py:717 +>>>>>>> update1 #: templates/includes/meeting_form.html:10 #: templates/meetings/create_meeting.html:162 #: templates/meetings/list_meetings.html:306 #: templates/meetings/update_meeting.html:215 #: templates/recruitment/candidate_interview_view.html:263 msgid "Topic" -msgstr "" +msgstr "الموضوع" +<<<<<<< HEAD #: recruitment/forms.py:304 recruitment/models.py:740 #: recruitment/models.py:1587 recruitment/models.py:1606 +======= +#: recruitment/forms.py:304 recruitment/models.py:721 +#: recruitment/models.py:1568 recruitment/models.py:1587 +>>>>>>> update1 #: templates/interviews/schedule_interviews.html:169 #: templates/interviews/schedule_interviews.html:201 #: templates/meetings/create_meeting.html:166 @@ -133,50 +192,81 @@ msgstr "" msgid "Start Time" msgstr "وقت البدء" +<<<<<<< HEAD #: recruitment/forms.py:305 recruitment/models.py:742 +======= +#: recruitment/forms.py:305 recruitment/models.py:723 +>>>>>>> update1 #: templates/meetings/list_meetings.html:266 #: templates/meetings/list_meetings.html:311 #: templates/meetings/meeting_details.html:265 #: templates/recruitment/candidate_interview_view.html:264 +<<<<<<< HEAD +======= +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1868 +>>>>>>> update1 msgid "Duration" msgstr "المدة" #: recruitment/forms.py:308 msgid "Enter meeting topic" -msgstr "" +msgstr "أدخل موضوع الاجتماع" #: recruitment/forms.py:310 msgid "60" -msgstr "" +msgstr "60" #: recruitment/forms.py:324 templates/meetings/create_meeting.html:180 #: templates/meetings/list_meetings.html:179 +<<<<<<< HEAD #, fuzzy #| msgid "Sales Meeting" msgid "Create Meeting" msgstr "اجتماع مبيعات" #: recruitment/forms.py:332 recruitment/models.py:711 +======= +msgid "Create Meeting" +msgstr "إنشاء اجتماع" + +#: recruitment/forms.py:332 recruitment/models.py:692 +>>>>>>> update1 #: templates/recruitment/training_list.html:204 #: templates/recruitment/training_update.html:144 msgid "Title" msgstr "العنوان" +<<<<<<< HEAD #: recruitment/forms.py:333 recruitment/models.py:712 recruitment/models.py:806 +======= +#: recruitment/forms.py:333 recruitment/models.py:693 recruitment/models.py:787 +>>>>>>> update1 #: templates/recruitment/training_update.html:158 #, fuzzy #| msgid "Contact" msgid "Content" +<<<<<<< HEAD msgstr "جهة الاتصال" #: recruitment/forms.py:334 recruitment/models.py:713 +======= +msgstr "المحتوى" + +#: recruitment/forms.py:334 recruitment/models.py:694 +>>>>>>> update1 #: templates/recruitment/training_update.html:150 msgid "Video Link" -msgstr "" +msgstr "رابط الفيديو" +<<<<<<< HEAD #: recruitment/forms.py:335 recruitment/models.py:715 +======= +#: recruitment/forms.py:335 recruitment/models.py:696 +>>>>>>> update1 #: templates/recruitment/training_update.html:166 +#: venv/lib/python3.13/site-packages/django/db/models/fields/files.py:244 msgid "File" +<<<<<<< HEAD msgstr "الملف" #: recruitment/forms.py:338 @@ -205,6 +295,30 @@ msgstr "تاريخ الإنشاء" #: recruitment/forms.py:516 recruitment/models.py:422 #: recruitment/models.py:1334 templates/forms/form_templates_list.html:270 +======= +msgstr "ملف" + +#: recruitment/forms.py:338 +#, fuzzy +#| msgid "Enter material content" +msgid "Enter material title" +msgstr "أدخل عنوان المادة" + +#: recruitment/forms.py:339 +msgid "Enter material content" +msgstr "أدخل محتوى المادة" + +#: recruitment/forms.py:340 +msgid "https://www.youtube.com/watch?v=..." +msgstr "https://www.youtube.com/watch?v=..." + +#: recruitment/forms.py:359 +msgid "Create Material" +msgstr "إنشاء مادة" + +#: recruitment/forms.py:516 recruitment/models.py:422 +#: recruitment/models.py:1315 templates/forms/form_templates_list.html:270 +>>>>>>> update1 #: templates/meetings/list_meetings.html:256 #: templates/meetings/list_meetings.html:308 #: templates/meetings/reschedule_meeting.html:11 @@ -214,6 +328,7 @@ msgstr "تاريخ الإنشاء" #: templates/recruitment/candidate_list.html:273 #: templates/recruitment/schedule_meeting_form.html:18 msgid "Job" +<<<<<<< HEAD msgstr "" #: recruitment/forms.py:517 templates/forms/form_templates_list.html:269 @@ -11035,10 +11150,8904 @@ msgstr "تاريخ الإبطال" #~ msgid "Car Transfer Approve" #~ msgstr "الموافقة على نقل السيارة" +======= +msgstr "الوظيفة" + +#: recruitment/forms.py:517 templates/forms/form_templates_list.html:269 +msgid "Template Name" +msgstr "اسم القالب" + +#: recruitment/forms.py:518 recruitment/models.py:1101 +#: templates/recruitment/agency_detail.html:377 +msgid "Description" +msgstr "الوصف" + +#: recruitment/forms.py:519 recruitment/models.py:1135 +#: recruitment/models.py:1300 templates/jobs/job_list.html:230 +#: templates/recruitment/agency_access_link_detail.html:31 +#: templates/recruitment/agency_access_link_form.html:89 +#: templates/recruitment/agency_assignment_list.html:87 +#: templates/recruitment/agency_detail.html:456 +#: templates/recruitment/agency_portal_dashboard.html:148 +#: templates/user/admin_settings.html:192 +msgid "Active" +msgstr "نشط" + +#: recruitment/forms.py:524 +msgid "Enter template name" +msgstr "أدخل اسم القالب" + +#: recruitment/forms.py:530 +msgid "Enter template description (optional)" +msgstr "أدخل وصف القالب (اختياري)" + +#: recruitment/forms.py:549 templates/forms/form_templates_list.html:384 +msgid "Create Template" +msgstr "إنشاء قالب" + +#: recruitment/forms.py:620 +msgid "Enter your comment or note" +msgstr "أدخل تعليقك أو ملاحظتك" + +#: recruitment/forms.py:625 templates/meetings/meeting_details.html:422 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:23 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:59 +msgid "Comment" +msgstr "تعليق" + +#: recruitment/forms.py:637 +msgid "Add Comment" +msgstr "إضافة تعليق" + +#: recruitment/forms.py:768 recruitment/models.py:1276 +#: templates/recruitment/agency_confirm_delete.html:218 +#: templates/recruitment/agency_list.html:184 +msgid "Agency Name" +msgstr "اسم الوكالة" + +#: recruitment/forms.py:769 recruitment/models.py:1278 +#: templates/recruitment/agency_confirm_delete.html:229 +#: templates/recruitment/agency_list.html:185 +msgid "Contact Person" +msgstr "شخص الاتصال" + +#: recruitment/forms.py:770 recruitment/forms.py:1065 +#: templates/participants/participants_detail.html:172 +#: templates/recruitment/agency_portal_submit_candidate.html:219 +#: templates/user/profile.html:139 +#, fuzzy +#| msgid "IP Address" +msgid "Email Address" +msgstr "عنوان البريد الإلكتروني" + +#: recruitment/forms.py:771 recruitment/forms.py:1066 recruitment/models.py:32 +#: recruitment/models.py:1739 templates/meetings/meeting_details.html:306 +#: templates/participants/participants_detail.html:178 +#: templates/recruitment/agency_portal_submit_candidate.html:230 +msgid "Phone Number" +msgstr "رقم الهاتف" + +#: recruitment/forms.py:772 templates/recruitment/agency_detail.html:261 +#: templates/recruitment/agency_detail.html:313 +#: templates/recruitment/agency_list.html:189 +msgid "Website" +msgstr "الموقع الإلكتروني" + +#: recruitment/forms.py:773 templates/jobs/create_job.html:302 +#: templates/jobs/edit_job.html:302 +#: templates/recruitment/agency_detail.html:363 +#: templates/recruitment/agency_list.html:188 +msgid "Country" +msgstr "الدولة" + +#: recruitment/forms.py:774 recruitment/models.py:428 +#: templates/recruitment/agency_detail.html:339 +#: templates/recruitment/agency_portal_assignment_detail.html:568 +#, fuzzy +#| msgid "IP Address" +msgid "Address" +msgstr "العنوان" + +#: recruitment/forms.py:775 +#, fuzzy +#| msgid "Internal Job ID:" +msgid "Internal Notes" +msgstr "ملاحظات داخلية" + +#: recruitment/forms.py:799 +#, fuzzy +#| msgid "Submitted by Agency" +msgid "Save Agency" +msgstr "حفظ الوكالة" + +#: recruitment/forms.py:888 recruitment/models.py:495 +#: recruitment/models.py:1309 +#: templates/recruitment/agency_access_link_detail.html:46 +#: templates/recruitment/agency_assignment_detail.html:124 +#: templates/recruitment/agency_assignment_list.html:112 +msgid "Agency" +msgstr "وكالة" + +#: recruitment/forms.py:889 +msgid "Job Posting" +msgstr "إعلان الوظيفة" + +#: recruitment/forms.py:890 recruitment/models.py:1320 +#: templates/recruitment/agency_portal_assignment_detail.html:174 +msgid "Maximum Candidates" +msgstr "الحد الأقصى للمرشحين" + +#: recruitment/forms.py:891 recruitment/models.py:1332 +msgid "Deadline Date" +msgstr "الموعد النهائي" + +#: recruitment/forms.py:892 recruitment/forms.py:987 recruitment/models.py:1337 +#: recruitment/models.py:1505 +#, fuzzy +#| msgid "Active" +msgid "Is Active" +msgstr "هل هو نشط" + +#: recruitment/forms.py:893 recruitment/models.py:750 +#: recruitment/models.py:1342 recruitment/models.py:1691 +#: templates/includes/candidate_modal_body.html:70 +#: templates/includes/easy_logs.html:209 +#: templates/meetings/list_meetings.html:312 +#: templates/recruitment/agency_access_link_form.html:84 +#: templates/recruitment/agency_assignment_detail.html:136 +#: templates/recruitment/agency_assignment_list.html:84 +#: templates/recruitment/agency_assignment_list.html:116 +#: templates/recruitment/agency_portal_assignment_detail.html:148 +#: templates/recruitment/candidate_detail.html:557 +#: templates/recruitment/candidate_hired_view.html:236 +#: templates/recruitment/notification_detail.html:155 +#: templates/recruitment/notification_list.html:37 +#: templates/recruitment/partials/_candidate_table.html:12 +#: templates/user/admin_settings.html:176 +msgid "Status" +msgstr "الحالة" + +#: recruitment/forms.py:894 recruitment/models.py:1360 +#: templates/recruitment/agency_assignment_detail.html:160 +msgid "Admin Notes" +msgstr "ملاحظات المسؤول" + +#: recruitment/forms.py:928 +msgid "Save Assignment" +msgstr "حفظ التكليف" + +#: recruitment/forms.py:985 recruitment/models.py:1470 +#: templates/recruitment/agency_access_link_detail.html:37 +#: templates/recruitment/agency_access_link_form.html:37 +msgid "Assignment" +msgstr "التكليف" + +#: recruitment/forms.py:986 recruitment/models.py:1489 +#: templates/recruitment/agency_access_link_detail.html:56 +#: templates/recruitment/agency_access_link_form.html:52 +msgid "Expires At" +msgstr "ينتهي في" + +#: recruitment/forms.py:1011 +#: templates/recruitment/agency_access_link_form.html:4 +#: templates/recruitment/agency_access_link_form.html:12 +#: templates/recruitment/agency_access_link_form.html:130 +#, fuzzy +#| msgid "Create Meeting" +msgid "Create Access Link" +msgstr "إنشاء رابط وصول" + +#: recruitment/forms.py:1091 +#: templates/recruitment/agency_portal_dashboard.html:198 +#: templates/recruitment/agency_portal_submit_candidate.html:4 +#: templates/recruitment/agency_portal_submit_candidate.html:104 +#: templates/recruitment/agency_portal_submit_candidate.html:337 +#: templates/recruitment/agency_portal_submit_candidate.html:561 +#, fuzzy +#| msgid "View Candidate" +msgid "Submit Candidate" +msgstr "تقديم المرشح" + +#: recruitment/forms.py:1155 +#: templates/recruitment/agency_access_link_detail.html:98 +#: templates/recruitment/agency_assignment_detail.html:187 +#: templates/recruitment/agency_portal_login.html:145 +msgid "Access Token" +msgstr "رمز الوصول" + +#: recruitment/forms.py:1164 recruitment/models.py:733 +#: templates/account/login.html:164 templates/meetings/list_meetings.html:266 +#: templates/recruitment/agency_access_link_detail.html:109 +#: templates/recruitment/agency_assignment_detail.html:198 +#: templates/recruitment/agency_portal_login.html:167 +msgid "Password" +msgstr "كلمة المرور" + +#: recruitment/forms.py:1253 +#, fuzzy +#| msgid "Participant Video" +msgid "Select Participants" +msgstr "اختر المشاركين" + +#: recruitment/forms.py:1259 +#, fuzzy +#| msgid "Select country" +msgid "Select Users" +msgstr "اختر المستخدمين" + +#: recruitment/forms.py:1276 templates/includes/email_compose_form.html:20 +#: templates/recruitment/agency_portal_assignment_detail.html:488 +msgid "Subject" +msgstr "الموضوع" + +#: recruitment/forms.py:1287 templates/includes/email_compose_form.html:56 +#: templates/recruitment/agency_portal_assignment_detail.html:503 +#, fuzzy +#| msgid "Error Message" +msgid "Message" +msgstr "الرسالة" + +#: recruitment/forms.py:1295 templates/includes/email_compose_form.html:35 +msgid "Recipients" +msgstr "المستلمون" + +#: recruitment/forms.py:1303 +#, fuzzy +#| msgid "Candidate Information" +msgid "Include candidate information" +msgstr "تضمين معلومات المرشح" + +#: recruitment/forms.py:1312 +#, fuzzy +#| msgid "Meeting Details" +msgid "Include meeting details" +msgstr "تضمين تفاصيل الاجتماع" + +#: recruitment/forms.py:1376 +#, fuzzy +#| msgid "Please select a stage." +msgid "Please select at least one recipient." +msgstr "الرجاء اختيار مستلم واحد على الأقل." + +#: recruitment/models.py:20 +msgid "Created at" +msgstr "تم الإنشاء في" + +#: recruitment/models.py:21 +msgid "Updated at" +msgstr "تم التحديث في" + +#: recruitment/models.py:23 +msgid "Slug" +msgstr "الرابط المختصر" + +#: recruitment/models.py:43 +msgid "Full-time" +msgstr "دوام كامل" + +#: recruitment/models.py:44 +msgid "Part-time" +msgstr "دوام جزئي" + +#: recruitment/models.py:45 +msgid "Contract" +msgstr "عقد عمل" + +#: recruitment/models.py:46 +msgid "Internship" +msgstr "داخلي" + +#: recruitment/models.py:47 +msgid "Faculty" +msgstr "كلية" + +#: recruitment/models.py:48 +msgid "Temporary" +msgstr "موقت" + +#: recruitment/models.py:52 +msgid "On-site" +msgstr "في الموقع" + +#: recruitment/models.py:53 +msgid "Remote" +msgstr "عن بعد" + +#: recruitment/models.py:54 +msgid "Hybrid" +msgstr "مزيج" + +#: recruitment/models.py:60 +#, fuzzy +#| msgid "Internal Information" +msgid "Internal Participant" +msgstr "مشارك داخلي" + +#: recruitment/models.py:61 +msgid "Internal staff involved in the recruitment process for this job" +msgstr "الموظفون الداخليون المشاركون في عملية التوظيف لهذه الوظيفة" + +#: recruitment/models.py:66 +msgid "External Participant" +msgstr "مشارك خارجي" + +#: recruitment/models.py:67 +msgid "External participants involved in the recruitment process for this job" +msgstr "المشاركون الخارجيون المشاركون في عملية التوظيف لهذه الوظيفة" + +#: recruitment/models.py:182 +msgid "External agency responsible for sourcing candidates for this role" +msgstr "وكالة خارجية مسؤولة عن توفير المرشحين لهذا المنصب" + +#: recruitment/models.py:187 +msgid "Reason for canceling the job posting" +msgstr "سبب إلغاء نشر الوظيفة" + +#: recruitment/models.py:188 +#, fuzzy +#| msgid "Cancelled" +msgid "Cancel Reason" +msgstr "سبب الإلغاء" + +#: recruitment/models.py:193 +msgid "Name of person who cancelled this job" +msgstr "اسم الشخص الذي ألغى هذه الوظيفة" + +#: recruitment/models.py:194 +#, fuzzy +#| msgid "Cancelled" +msgid "Cancelled By" +msgstr "تم الإلغاء بواسطة" + +#: recruitment/models.py:392 recruitment/models.py:437 +#: templates/jobs/job_candidates_list.html:211 +#: templates/recruitment/candidate_list.html:231 +msgid "Applied" +msgstr "تم التقديم" + +#: recruitment/models.py:393 templates/jobs/job_candidates_list.html:212 +#: templates/jobs/job_list.html:283 +#: templates/jobs/partials/applicant_tracking.html:128 +#: templates/recruitment/candidate_detail.html:410 +#: templates/recruitment/candidate_list.html:232 +msgid "Exam" +msgstr "الاختبار" + +#: recruitment/models.py:394 templates/jobs/job_candidates_list.html:213 +#: templates/jobs/job_list.html:284 +#: templates/jobs/partials/applicant_tracking.html:144 +#: templates/recruitment/candidate_detail.html:424 +#: templates/recruitment/candidate_list.html:233 +msgid "Interview" +msgstr "المقابلة" + +#: recruitment/models.py:395 templates/jobs/job_candidates_list.html:214 +#: templates/jobs/job_list.html:285 +#: templates/jobs/partials/applicant_tracking.html:160 +#: templates/recruitment/candidate_detail.html:439 +#: templates/recruitment/candidate_list.html:234 +#: templates/recruitment/candidate_offer_view.html:248 +msgid "Offer" +msgstr "العرض" + +#: recruitment/models.py:396 +#: templates/jobs/partials/applicant_tracking.html:176 +#: templates/recruitment/agency_detail.html:462 +#: templates/recruitment/candidate_hired_view.html:273 +msgid "Hired" +msgstr "تم التوظيف" + +#: recruitment/models.py:399 +#: templates/includes/candidate_update_exam_form.html:5 +#: templates/includes/candidate_update_interview_form.html:5 +msgid "Passed" +msgstr "نجح" + +#: recruitment/models.py:400 recruitment/models.py:1671 +#: templates/includes/candidate_update_exam_form.html:8 +#: templates/includes/candidate_update_interview_form.html:8 +#: templates/includes/easy_logs.html:267 +msgid "Failed" +msgstr "رسب" + +#: recruitment/models.py:403 +#: templates/includes/candidate_update_offer_form.html:5 +msgid "Accepted" +msgstr "قبل" + +#: recruitment/models.py:404 +#: templates/includes/candidate_update_offer_form.html:8 +#: templates/recruitment/agency_detail.html:468 +msgid "Rejected" +msgstr "رفض" + +#: recruitment/models.py:407 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant" +msgstr "مقدم الطلب" + +#: recruitment/models.py:408 recruitment/models.py:509 +#: templates/meetings/list_meetings.html:248 +#: templates/meetings/list_meetings.html:307 +msgid "Candidate" +msgstr "المرشح" + +#: recruitment/models.py:431 +#, fuzzy +#| msgid "Resume" +msgid "Resume Parsed" +msgstr "السيرة الذاتية المحللة" + +#: recruitment/models.py:434 +#, fuzzy +#| msgid "Create Candidate" +msgid "Potential Candidate" +msgstr "مرشح محتمل" + +#: recruitment/models.py:436 +msgid "Parsed Summary" +msgstr "ملخص محلل" + +#: recruitment/models.py:442 templates/jobs/job_candidates_list.html:229 +#: templates/recruitment/agency_assignment_detail.html:241 +#: templates/recruitment/agency_portal_assignment_detail.html:243 +#: templates/recruitment/candidate_list.html:275 +#: templates/recruitment/partials/_candidate_table.html:13 +msgid "Stage" +msgstr "المرحلة" + +#: recruitment/models.py:450 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant Status" +msgstr "حالة مقدم الطلب" + +#: recruitment/models.py:452 templates/recruitment/candidate_exam_view.html:253 +msgid "Exam Date" +msgstr "تاريخ الاختبار" + +#: recruitment/models.py:458 +msgid "Exam Status" +msgstr "حالة الاختبار" + +#: recruitment/models.py:461 recruitment/models.py:1634 +msgid "Interview Date" +msgstr "تاريخ المقابلة" + +#: recruitment/models.py:468 +msgid "Interview Status" +msgstr "حالة المقابلة" + +#: recruitment/models.py:470 +msgid "Offer Date" +msgstr "تاريخ العرض" + +#: recruitment/models.py:476 +msgid "Offer Status" +msgstr "حالة العرض" + +#: recruitment/models.py:478 +#: templates/recruitment/candidate_hired_view.html:235 +#, fuzzy +#| msgid "Applied Date" +msgid "Hired Date" +msgstr "تاريخ التوظيف" + +#: recruitment/models.py:479 +msgid "Join Date" +msgstr "تاريخ الانضمام" + +#: recruitment/models.py:491 templates/recruitment/candidate_list.html:276 +msgid "Hiring Source" +msgstr "مصدر التوظيف" + +#: recruitment/models.py:493 +msgid "Public" +msgstr "عام" + +#: recruitment/models.py:494 +msgid "Internal" +msgstr "داخلي" + +#: recruitment/models.py:510 +#: templates/recruitment/agency_assignment_list.html:114 +#: templates/recruitment/agency_portal_dashboard.html:173 +msgid "Candidates" +msgstr "المرشحون" + +#: recruitment/models.py:699 +msgid "Created by" +msgstr "أنشأ بواسطة" + +#: recruitment/models.py:703 +msgid "Training Material" +msgstr "مادة تدريبية" + +#: recruitment/models.py:704 templates/recruitment/training_list.html:4 +#: templates/recruitment/training_list.html:128 +msgid "Training Materials" +msgstr "المواد التدريبية" + +#: recruitment/models.py:712 templates/meetings/list_meetings.html:203 +msgid "Waiting" +msgstr "في الانتظار" + +#: recruitment/models.py:713 templates/meetings/list_meetings.html:204 +msgid "Started" +msgstr "بدأ" + +#: recruitment/models.py:714 templates/meetings/list_meetings.html:205 +msgid "Ended" +msgstr "انتهى" + +#: recruitment/models.py:715 recruitment/models.py:1303 +#: recruitment/models.py:1641 +#: templates/recruitment/agency_assignment_list.html:90 +#: templates/recruitment/agency_portal_dashboard.html:152 +msgid "Cancelled" +msgstr "ملغى" + +#: recruitment/models.py:719 templates/meetings/meeting_details.html:266 +msgid "Meeting ID" +msgstr "معرف الاجتماع" + +#: recruitment/models.py:725 +msgid "Timezone" +msgstr "المنطقة الزمنية" + +#: recruitment/models.py:727 templates/meetings/meeting_details.html:274 +msgid "Join URL" +msgstr "رابط الانضمام" + +#: recruitment/models.py:730 +msgid "Participant Video" +msgstr "فيديو المشارك" + +#: recruitment/models.py:736 +msgid "Join Before Host" +msgstr "الانضمام قبل المضيف" + +#: recruitment/models.py:739 +msgid "Mute Upon Entry" +msgstr "كتم الصوت عند الدخول" + +#: recruitment/models.py:741 +msgid "Waiting Room" +msgstr "غرفة الانتظار" + +#: recruitment/models.py:744 +msgid "Zoom Gateway Response" +msgstr "استجابة بوابة زوم" + +#: recruitment/models.py:778 +#, fuzzy +#| msgid "Meetings" +msgid "Meeting" +msgstr "الاجتماع" + +#: recruitment/models.py:784 +msgid "Author" +msgstr "المؤلف" + +#: recruitment/models.py:793 +#, fuzzy +#| msgid "Meeting Details" +msgid "Meeting Comment" +msgstr "تعليق الاجتماع" + +#: recruitment/models.py:794 +#, fuzzy +#| msgid "Meeting Details" +msgid "Meeting Comments" +msgstr "تعليقات الاجتماع" + +#: recruitment/models.py:1093 +msgid "Source Name" +msgstr "اسم المصدر" + +#: recruitment/models.py:1094 recruitment/models.py:1097 +msgid "e.g., ATS, ERP " +msgstr "مثلاً، ATS, ERP" + +#: recruitment/models.py:1097 templates/meetings/meeting_details.html:307 +msgid "Source Type" +msgstr "نوع المصدر" + +#: recruitment/models.py:1102 +msgid "A description of the source" +msgstr "وصف المصدر" + +#: recruitment/models.py:1107 recruitment/models.py:1247 +#: templates/includes/easy_logs.html:210 +msgid "IP Address" +msgstr "عنوان IP" + +#: recruitment/models.py:1108 +msgid "The IP address of the source" +msgstr "عنوان IP الخاص بالمصدر" + +#: recruitment/models.py:1117 +msgid "API Key" +msgstr "مفتاح واجهة برمجة التطبيقات" + +#: recruitment/models.py:1118 +msgid "API key for authentication (will be encrypted)" +msgstr "مفتاح واجهة برمجة التطبيقات للمصادقة (سيتم تشفيره)" + +#: recruitment/models.py:1124 +msgid "API Secret" +msgstr "سر واجهة برمجة التطبيقات" + +#: recruitment/models.py:1125 +msgid "API secret for authentication (will be encrypted)" +msgstr "سر واجهة برمجة التطبيقات للمصادقة (سيتم تشفيره)" + +#: recruitment/models.py:1130 +msgid "Trusted IP Addresses" +msgstr "عناوين IP الموثوقة" + +#: recruitment/models.py:1131 +msgid "Comma-separated list of trusted IP addresses" +msgstr "قائمة عناوين IP الموثوقة مفصولة بفواصل" + +#: recruitment/models.py:1136 +msgid "Whether this source is active for integration" +msgstr "ما إذا كان هذا المصدر نشطاً للتكامل" + +#: recruitment/models.py:1141 +msgid "Integration Version" +msgstr "إصدار التكامل" + +#: recruitment/models.py:1142 +msgid "Version of the integration protocol" +msgstr "إصدار بروتوكول التكامل" + +#: recruitment/models.py:1147 +msgid "Last Sync At" +msgstr "آخر مزامنة في" + +#: recruitment/models.py:1148 +msgid "Timestamp of the last successful synchronization" +msgstr "الطابع الزمني لآخر مزامنة ناجحة" + +#: recruitment/models.py:1160 +msgid "Sync Status" +msgstr "حالة المزامنة" + +#: recruitment/models.py:1167 +#, fuzzy +#| msgid "Endpoint" +msgid "Sync Endpoint" +msgstr "نقطة نهاية المزامنة" + +#: recruitment/models.py:1168 +msgid "Endpoint URL for sending candidate data (for outbound sync)" +msgstr "عنوان URL لنقطة النهاية لإرسال بيانات المرشح (للمزامنة الصادرة)" + +#: recruitment/models.py:1178 +#, fuzzy +#| msgid "HTTP Method" +msgid "Sync Method" +msgstr "طريقة المزامنة" + +#: recruitment/models.py:1179 +msgid "HTTP method for outbound sync requests" +msgstr "طريقة HTTP لطلبات المزامنة الصادرة" + +#: recruitment/models.py:1189 +#, fuzzy +#| msgid "HTTP Method" +msgid "Test Method" +msgstr "طريقة الاختبار" + +#: recruitment/models.py:1190 +msgid "HTTP method for connection testing" +msgstr "طريقة HTTP لاختبار الاتصال" + +#: recruitment/models.py:1195 +msgid "Custom Headers" +msgstr "رؤوس مخصصة" + +#: recruitment/models.py:1196 +msgid "JSON object with custom HTTP headers for sync requests" +msgstr "كائن JSON يحتوي على رؤوس HTTP مخصصة لطلبات المزامنة" + +#: recruitment/models.py:1200 +msgid "Supports Outbound Sync" +msgstr "يدعم المزامنة الصادرة" + +#: recruitment/models.py:1201 +msgid "Whether this source supports receiving candidate data from ATS" +msgstr "" +"ما إذا كان هذا المصدر يدعم استقبال بيانات المرشح من نظام تتبع المتقدمين" + +#: recruitment/models.py:1208 recruitment/models.py:1230 +#: templates/jobs/job_list.html:269 +msgid "Source" +msgstr "المصدر" + +#: recruitment/models.py:1209 +msgid "Sources" +msgstr "المصادر" + +#: recruitment/models.py:1219 +msgid "Request" +msgstr "الطلب" + +#: recruitment/models.py:1220 +msgid "Response" +msgstr "الاستجابة" + +#: recruitment/models.py:1221 +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:98 +msgid "Error" +msgstr "خطأ" + +#: recruitment/models.py:1222 +msgid "Sync" +msgstr "مزامنة" + +#: recruitment/models.py:1223 templates/jobs/job_list.html:395 +msgid "Create Job" +msgstr "إنشاء وظيفة" + +#: recruitment/models.py:1224 +msgid "Update Job" +msgstr "تحديث وظيفة" + +#: recruitment/models.py:1233 templates/includes/easy_logs.html:199 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:25 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:49 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:25 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:49 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:18 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:34 +msgid "Action" +msgstr "الإجراء" + +#: recruitment/models.py:1235 +msgid "Endpoint" +msgstr "نقطة النهاية" + +#: recruitment/models.py:1236 +msgid "HTTP Method" +msgstr "طريقة HTTP" + +#: recruitment/models.py:1238 +msgid "Request Data" +msgstr "بيانات الطلب" + +#: recruitment/models.py:1241 +msgid "Response Data" +msgstr "بيانات الاستجابة" + +#: recruitment/models.py:1244 +msgid "Status Code" +msgstr "رمز الحالة" + +#: recruitment/models.py:1246 +msgid "Error Message" +msgstr "رسالة الخطأ" + +#: recruitment/models.py:1249 +msgid "User Agent" +msgstr "وكيل المستخدم" + +#: recruitment/models.py:1252 +msgid "Processing Time (seconds)" +msgstr "وقت المعالجة (ثواني)" + +#: recruitment/models.py:1260 +msgid "Integration Log" +msgstr "سجل التكامل" + +#: recruitment/models.py:1261 +msgid "Integration Logs" +msgstr "سجلات التكامل" + +#: recruitment/models.py:1283 +msgid "Internal notes about the agency" +msgstr "ملاحظات داخلية حول الوكالة" + +#: recruitment/models.py:1284 +msgid "Select country" +msgstr "اختر الدولة" + +#: recruitment/models.py:1292 templates/recruitment/agency_list.html:4 +#: templates/recruitment/agency_list.html:131 +msgid "Hiring Agencies" +msgstr "وكالات التوظيف" + +#: recruitment/models.py:1301 recruitment/models.py:1642 +#: templates/recruitment/agency_assignment_list.html:89 +#: templates/recruitment/agency_portal_dashboard.html:150 +msgid "Completed" +msgstr "مكتمل" + +#: recruitment/models.py:1302 +#: templates/recruitment/agency_access_link_detail.html:60 +#: templates/recruitment/agency_assignment_detail.html:151 +#: templates/recruitment/agency_assignment_list.html:88 +#: templates/recruitment/agency_assignment_list.html:148 +#: templates/recruitment/agency_portal_assignment_detail.html:165 +#: templates/recruitment/agency_portal_dashboard.html:154 +msgid "Expired" +msgstr "منتهي الصلاحية" + +#: recruitment/models.py:1321 +msgid "Maximum candidates agency can submit for this job" +msgstr "الحد الأقصى للمرشحين الذين يمكن للوكالة تقديمهم لهذه الوظيفة" + +#: recruitment/models.py:1325 +#: templates/recruitment/agency_access_link_detail.html:71 +#, fuzzy +#| msgid "Candidates" +msgid "Candidates Submitted" +msgstr "المرشحون المقدمون" + +#: recruitment/models.py:1326 +msgid "Number of candidates submitted so far" +msgstr "عدد المرشحين المقدمين حتى الآن" + +#: recruitment/models.py:1330 +#: templates/recruitment/agency_portal_assignment_detail.html:410 +#, fuzzy +#| msgid "Applied Date" +msgid "Assigned Date" +msgstr "تاريخ التعيين" + +#: recruitment/models.py:1333 +msgid "Deadline for agency to submit candidates" +msgstr "الموعد النهائي للوكالة لتقديم المرشحين" + +#: recruitment/models.py:1348 +#, fuzzy +#| msgid "Deadline:" +msgid "Deadline Extended" +msgstr "تمديد الموعد النهائي" + +#: recruitment/models.py:1353 +#, fuzzy +#| msgid "Application Deadline" +msgid "Original Deadline" +msgstr "الموعد النهائي الأصلي" + +#: recruitment/models.py:1354 +msgid "Original deadline before extensions" +msgstr "الموعد النهائي الأصلي قبل التمديدات" + +#: recruitment/models.py:1361 +#, fuzzy +#| msgid "Internal notes about the agency" +msgid "Internal notes about this assignment" +msgstr "ملاحظات داخلية حول هذا التكليف" + +#: recruitment/models.py:1365 +msgid "Agency Job Assignment" +msgstr "تكليف وكالة بالوظيفة" + +#: recruitment/models.py:1366 +msgid "Agency Job Assignments" +msgstr "تكليفات وكالة بالوظائف" + +#: recruitment/models.py:1405 +msgid "Deadline date must be in the future" +msgstr "يجب أن يكون تاريخ الموعد النهائي في المستقبل" + +#: recruitment/models.py:1408 +msgid "Maximum candidates must be greater than 0" +msgstr "يجب أن يكون الحد الأقصى للمرشحين أكبر من 0" + +#: recruitment/models.py:1411 +msgid "Candidates submitted cannot exceed maximum candidates" +msgstr "لا يمكن أن يتجاوز عدد المرشحين المقدمين الحد الأقصى للمرشحين" + +#: recruitment/models.py:1478 +msgid "Unique Token" +msgstr "رمز مميز فريد" + +#: recruitment/models.py:1482 +#, fuzzy +#| msgid "Password" +msgid "Access Password" +msgstr "كلمة مرور الوصول" + +#: recruitment/models.py:1483 +#, fuzzy +#| msgid "Posted successfully!" +msgid "Password for agency access" +msgstr "تم تغيير كلمة المرور بنجاح" + +#: recruitment/models.py:1487 templates/participants/participants_list.html:217 +#: templates/recruitment/agency_access_link_detail.html:51 +msgid "Created At" +msgstr "تم الإنشاء في" + +#: recruitment/models.py:1490 +msgid "When this access link expires" +msgstr "عندما ينتهي رابط الوصول هذا" + +#: recruitment/models.py:1495 +#: templates/recruitment/agency_access_link_detail.html:141 +msgid "Last Accessed" +msgstr "آخر وصول" + +#: recruitment/models.py:1501 +msgid "Access Count" +msgstr "عدد مرات الوصول" + +#: recruitment/models.py:1509 +msgid "Agency Access Link" +msgstr "رابط وصول الوكالة" + +#: recruitment/models.py:1510 +msgid "Agency Access Links" +msgstr "روابط وصول الوكالة" + +#: recruitment/models.py:1524 +msgid "Expiration date must be in the future" +msgstr "يجب أن يكون تاريخ الانتهاء في المستقبل" + +#: recruitment/models.py:1569 recruitment/models.py:1588 +#: templates/interviews/schedule_interviews.html:176 +#: templates/interviews/schedule_interviews.html:205 +msgid "End Time" +msgstr "وقت الانتهاء" + +#: recruitment/models.py:1582 templates/interviews/schedule_interviews.html:146 +msgid "Start Date" +msgstr "تاريخ البدء" + +#: recruitment/models.py:1583 templates/interviews/schedule_interviews.html:153 +msgid "End Date" +msgstr "تاريخ الانتهاء" + +#: recruitment/models.py:1585 templates/interviews/schedule_interviews.html:160 +msgid "Working Days" +msgstr "أيام العمل" + +#: recruitment/models.py:1590 +msgid "Break Start Time" +msgstr "وقت بدء الاستراحة" + +#: recruitment/models.py:1591 +msgid "Break End Time" +msgstr "وقت انتهاء الاستراحة" + +#: recruitment/models.py:1594 +msgid "Interview Duration (minutes)" +msgstr "مدة المقابلة (دقائق)" + +#: recruitment/models.py:1597 +msgid "Buffer Time (minutes)" +msgstr "وقت المخزن المؤقت (دقائق)" + +#: recruitment/models.py:1635 +msgid "Interview Time" +msgstr "وقت المقابلة" + +#: recruitment/models.py:1639 +msgid "Scheduled" +msgstr "مجدول" + +#: recruitment/models.py:1640 +msgid "Confirmed" +msgstr "مؤكد" + +#: recruitment/models.py:1665 templates/recruitment/notification_list.html:49 +msgid "In-App" +msgstr "في التطبيق" + +#: recruitment/models.py:1668 +msgid "Pending" +msgstr "قيد الانتظار" + +#: recruitment/models.py:1669 templates/recruitment/notification_list.html:42 +msgid "Sent" +msgstr "تم الإرسال" + +#: recruitment/models.py:1670 templates/recruitment/notification_list.html:41 +msgid "Read" +msgstr "مقروء" + +#: recruitment/models.py:1672 +msgid "Retrying" +msgstr "إعادة المحاولة" + +#: recruitment/models.py:1678 +msgid "Recipient" +msgstr "المستلم" + +#: recruitment/models.py:1680 +msgid "Notification Message" +msgstr "رسالة الإشعار" + +#: recruitment/models.py:1685 +msgid "Notification Type" +msgstr "نوع الإشعار" + +#: recruitment/models.py:1699 templates/recruitment/notification_detail.html:62 +#, fuzzy +#| msgid "Create Meeting" +msgid "Related Meeting" +msgstr "الاجتماع المرتبط" + +#: recruitment/models.py:1702 +#, fuzzy +#| msgid "Scheduled" +msgid "Scheduled Send Time" +msgstr "وقت الإرسال المجدول" + +#: recruitment/models.py:1703 +msgid "The date and time this notification is scheduled to be sent." +msgstr "التاريخ والوقت المحدد لإرسال هذا الإشعار." + +#: recruitment/models.py:1707 +msgid "Send Attempts" +msgstr "محاولات الإرسال" + +#: recruitment/models.py:1708 +#, fuzzy +#| msgid "Error Message" +msgid "Last Error Message" +msgstr "آخر رسالة خطأ" + +#: recruitment/models.py:1712 +#, fuzzy +#| msgid "Location" +msgid "Notification" +msgstr "الإشعار" + +#: recruitment/models.py:1713 templates/recruitment/notification_list.html:4 +#: templates/recruitment/notification_list.html:12 +#, fuzzy +#| msgid "Location" +msgid "Notifications" +msgstr "الإشعارات" + +#: recruitment/models.py:1737 +#, fuzzy +#| msgid "Participant Video" +msgid "Participant Name" +msgstr "اسم المشارك" + +#: recruitment/models.py:1741 +#: templates/participants/participants_detail.html:184 +#: templates/participants/participants_list.html:216 +msgid "Designation" +msgstr "التصميم" + +#: recruitment/views.py:614 +msgid "Failed to start the job posting process. Please try again." +msgstr "فشل في بدء عملية نشر الوظيفة. يرجى المحاولة مرة أخرى." + +#: recruitment/views.py:2181 templates/includes/easy_logs.html:176 +#, fuzzy +#| msgid "User Agent" +msgid "User Authentication" +msgstr "مصادقة المستخدم" + +#: recruitment/views.py:2184 templates/includes/easy_logs.html:182 +#, fuzzy +#| msgid "Request" +msgid "HTTP Requests" +msgstr "طلبات HTTP" + +#: recruitment/views.py:2187 templates/includes/easy_logs.html:170 +msgid "Model Changes (CRUD)" +msgstr "تغييرات النموذج (CRUD)" + +#: recruitment/views_frontend.py:275 +msgid "You don't have permission to view this page." +msgstr "ليس لديك إذن لعرض هذه الصفحة." + +#: templates/account/account_inactive.html:7 +#: templates/account/account_inactive.html:131 +msgid "Account Inactive" +msgstr "الحساب غير نشط" + +#: templates/account/account_inactive.html:114 +#: templates/account/password_reset_done.html:136 +#: templates/account/password_reset_from_key.html:55 +#: templates/account/password_reset_from_key_done.html:52 +msgid "جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية" +msgstr "جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية" + +#: templates/account/account_inactive.html:115 +#: templates/account/password_reset_done.html:137 +#: templates/account/password_reset_from_key.html:56 +#: templates/account/password_reset_from_key_done.html:53 +msgid "ومستشفى الملك عبدالله بن عبدالعزيز التخصصي" +msgstr "ومستشفى الملك عبدالله بن عبدالعزيز التخصصي" + +#: templates/account/account_inactive.html:116 +#: templates/account/password_reset_done.html:138 +#: templates/account/password_reset_from_key.html:57 +#: templates/account/password_reset_from_key_done.html:54 +msgid "Princess Nourah bint Abdulrahman University" +msgstr "جامعة الأميرة نورة بنت عبدالرحمن" + +#: templates/account/account_inactive.html:117 +#: templates/account/password_reset_done.html:139 +#: templates/account/password_reset_from_key.html:58 +#: templates/account/password_reset_from_key_done.html:55 +#, fuzzy +#| msgid "King Abdullah Academic University Hospital" +msgid "King Abdullah bin Abdulaziz University Hospital" +msgstr "مستشفى الملك عبدالله بن عبدالعزيز الجامعي" + +#: templates/account/account_inactive.html:135 +msgid "" +"Access denied. This account has been marked as inactive by an administrator." +msgstr "تم رفض الوصول. تم وضع علامة على هذا الحساب بأنه غير نشط من قبل مسؤول." + +#: templates/account/account_inactive.html:138 +msgid "" +"If you believe this is an error, please contact the system administrator for " +"assistance." +msgstr "" +"إذا كنت تعتقد أن هذا خطأ، يرجى الاتصال بمسؤول النظام للحصول على المساعدة." + +#: templates/account/account_inactive.html:143 +#: templates/account/password_reset_from_key.html:109 +#, fuzzy +#| msgid "Re-post to LinkedIn" +msgid "Return to Sign In" +msgstr "العودة إلى تسجيل الدخول" + +#: templates/account/email.html:6 templates/account/email.html:33 +#: templates/account/email.html:52 templates/account/logout.html:29 +#, fuzzy +#| msgid "IP Address" +msgid "Email Addresses" +msgstr "عناوين البريد الإلكتروني" + +#: templates/account/email.html:13 templates/account/logout.html:12 +#: templates/user/profile.html:111 +#, fuzzy +#| msgid "Settings" +msgid "Account Settings" +msgstr "إعدادات الحساب" + +#: templates/account/email.html:14 templates/account/logout.html:13 +#: templates/user/profile.html:112 +msgid "Manage your personal details and security." +msgstr "إدارة تفاصيلك الشخصية والأمان." + +#: templates/account/email.html:28 templates/account/logout.html:26 +#: templates/recruitment/agency_portal_submit_candidate.html:182 +#: templates/user/profile.html:123 +#, fuzzy +#| msgid "Internal Information" +msgid "Personal Information" +msgstr "المعلومات الشخصية" + +#: templates/account/email.html:36 templates/account/logout.html:32 +#: templates/account/password_change.html:4 +#: templates/account/password_change.html:15 +#: templates/account/password_change.html:34 +#: templates/account/password_reset_from_key.html:90 +#: templates/user/admin_settings.html:223 templates/user/profile.html:161 +#: templates/user/staff_password_create.html:4 +#: templates/user/staff_password_create.html:16 +#: templates/user/staff_password_create.html:35 +#, fuzzy +#| msgid "Password" +msgid "Change Password" +msgstr "تغيير كلمة المرور" + +#: templates/account/email.html:40 templates/account/logout.html:5 +#: templates/account/logout.html:37 templates/account/logout.html:66 +#: templates/base.html:213 +msgid "Sign Out" +msgstr "تسجيل الخروج" + +#: templates/account/email.html:53 +msgid "" +"These email addresses are linked to your account. You can set the primary " +"address, resend verification, or remove an address." +msgstr "" +"عناوين البريد الإلكتروني هذه مرتبطة بحسابك. يمكنك تعيين العنوان الأساسي أو " +"إعادة إرسال التحقق أو إزالة عنوان." + +#: templates/account/email.html:73 +msgid "Primary" +msgstr "أساسي" + +#: templates/account/email.html:76 +msgid "Verified" +msgstr "موثق" + +#: templates/account/email.html:78 +msgid "Unverified" +msgstr "غير موثق" + +#: templates/account/email.html:89 +msgid "Make Primary" +msgstr "جعله أساسيًا" + +#: templates/account/email.html:98 +#, fuzzy +#| msgid "Required Qualifications" +msgid "Re-send Verification" +msgstr "إعادة إرسال التحقق" + +#: templates/account/email.html:107 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_delete.html:4 +msgid "Remove" +msgstr "إزالة" + +#: templates/account/email.html:114 +#, fuzzy +#| msgid "No candidates found." +msgid "No email addresses found." +msgstr "لم يتم العثور على عناوين بريد إلكتروني." + +#: templates/account/email.html:121 +msgid "Add Email Address" +msgstr "إضافة عنوان بريد إلكتروني" + +#: templates/account/email.html:136 +#, fuzzy +#| msgid "Email" +msgid "Add Email" +msgstr "إضافة بريد إلكتروني" + +#: templates/account/email/email_confirmation_message.html:5 +#: templates/account/email/email_confirmation_message.txt:4 +#: templates/account/email/password_reset_key_message.html:14 +#: templates/account/email/password_reset_key_message.txt:7 +msgid "Hello," +msgstr "مرحباً،" + +#: templates/account/email/email_confirmation_message.html:9 +#: templates/account/email/email_confirmation_message.txt:6 +msgid "" +"To verify the ownership of your email address, please click the confirmation " +"link below:" +msgstr "" +"للتحقق من ملكية عنوان بريدك الإلكتروني، يرجى النقر على رابط التأكيد أدناه:" + +#: templates/account/email/email_confirmation_message.html:15 +#: templates/account/email/email_confirmation_message.txt:9 +msgid "Confirm My KAAUH ATS Email" +msgstr "تأكيد بريدي الإلكتروني في نظام توظيف مستشفى جامعة نورة" + +#: templates/account/email/email_confirmation_message.html:20 +#: templates/account/email/email_confirmation_message.txt:13 +msgid "" +"If you did not request this verification, you can safely ignore this email." +msgstr "إذا لم تطلب هذا التحقق، يمكنك تجاهل هذا البريد الإلكتروني بأمان." + +#: templates/account/email/email_confirmation_message.html:24 +#: templates/account/email/email_confirmation_message.txt:15 +msgid "Alternatively, copy and paste this link into your browser:" +msgstr "بدلاً من ذلك، انسخ والصق هذا الرابط في متصفحك:" + +#: templates/account/email/password_reset_key_message.html:10 +#: templates/account/email/password_reset_key_message.txt:5 +msgid "Password Reset Request" +msgstr "طلب إعادة تعيين كلمة المرور" + +#: templates/account/email/password_reset_key_message.html:16 +#: templates/account/email/password_reset_key_message.txt:9 +msgid "" +"You are receiving this email because you or someone else has requested a " +"password reset for your account at" +msgstr "" +"تتلقى هذا البريد الإلكتروني لأنك أو شخص آخر طلب إعادة تعيين كلمة المرور " +"لحسابك في" + +#: templates/account/email/password_reset_key_message.html:21 +#: templates/account/email/password_reset_key_message.txt:12 +msgid "Click Here to Reset Your Password" +msgstr "اضغط هنا لإعادة تعيين كلمة المرور الخاصة بك" + +#: templates/account/email/password_reset_key_message.html:25 +#: templates/account/email/password_reset_key_message.txt:16 +msgid "This link is only valid for a limited time." +msgstr "هذا الرابط صالح لفترة محدودة فقط." + +#: templates/account/email/password_reset_key_message.html:27 +#: templates/account/email/password_reset_key_message.txt:18 +msgid "" +"If you did not request a password reset, please ignore this email. Your " +"password will remain unchanged." +msgstr "" +"إذا لم تطلب إعادة تعيين كلمة المرور، يرجى تجاهل هذا البريد الإلكتروني. ستبقى " +"كلمة المرور الخاصة بك دون تغيير." + +#: templates/account/email/password_reset_key_message.html:30 +#: templates/account/email/password_reset_key_message.txt:20 +msgid "Thank you," +msgstr "شكراً لك،" + +#: templates/account/email/password_reset_key_message.html:31 +#: templates/account/email/password_reset_key_message.txt:21 +msgid "KAAUH ATS Team" +msgstr "فريق نظام توظيف مستشفى جامعة نورة" + +#: templates/account/email/password_reset_key_message.html:36 +#: templates/account/email/password_reset_key_message.txt:24 +msgid "" +"If the button above does not work, copy and paste the following link into " +"your browser:" +msgstr "إذا لم يعمل الزر أعلاه، انسخ والصق الرابط التالي في متصفحك:" + +#: templates/account/email_confirm.html:5 +msgid "Confirm Email Address" +msgstr "تأكيد عنوان البريد الإلكتروني" + +#: templates/account/email_confirm.html:13 +msgid "Account Verification" +msgstr "التحقق من الحساب" + +#: templates/account/email_confirm.html:14 +msgid "Verify your email to secure your account and unlock full features." +msgstr "تحقق من بريدك الإلكتروني لتأمين حسابك وفتح الميزات الكاملة." + +#: templates/account/email_confirm.html:32 +msgid "Confirm Your Email Address" +msgstr "تأكيد عنوان بريدك الإلكتروني" + +#: templates/account/email_confirm.html:35 +#, python-format +msgid "" +"Please confirm that **%(email)s** is the correct email address for your " +"account." +msgstr "" +"الرجاء التأكد من أن **%(email)s** هو عنوان البريد الإلكتروني الصحيح لحسابك." + +#: templates/account/email_confirm.html:44 +#, fuzzy +#| msgid "Confirm Delete" +msgid "Confirm & Activate" +msgstr "تأكيد وتفعيل" + +#: templates/account/email_confirm.html:53 +msgid "Verification Failed" +msgstr "فشل التحقق" + +#: templates/account/email_confirm.html:56 +msgid "The email confirmation link is expired or invalid." +msgstr "رابط تأكيد البريد الإلكتروني منتهي الصلاحية أو غير صالح." + +#: templates/account/email_confirm.html:59 +msgid "" +"If you recently requested a link, please ensure you use the newest one. You " +"can request a new verification email from your account settings." +msgstr "" +"إذا طلبت رابطًا مؤخرًا، يرجى التأكد من استخدام أحدث رابط. يمكنك طلب بريد " +"إلكتروني جديد للتحقق من إعدادات حسابك." + +#: templates/account/email_confirm.html:63 +#, fuzzy +#| msgid "Settings" +msgid "Go to Settings" +msgstr "انتقال إلى الإعدادات" + +#: templates/account/login.html:151 templates/account/login.html:178 +#, fuzzy +#| msgid "Sign out" +msgid "Sign In" +msgstr "تسجيل الدخول" + +#: templates/account/login.html:158 +#, fuzzy +#| msgid "Email" +msgid "Email *" +msgstr "البريد الإلكتروني *" + +#: templates/account/login.html:159 +#, fuzzy +#| msgid "Enter email" +msgid "Enter your email" +msgstr "أدخل بريدك الإلكتروني" + +#: templates/account/login.html:163 +#, fuzzy +#| msgid "Password" +msgid "Password *" +msgstr "كلمة المرور *" + +#: templates/account/login.html:167 templates/account/password_reset.html:150 +#, fuzzy +#| msgid "Password" +msgid "Forgot Password?" +msgstr "هل نسيت كلمة المرور؟" + +#: templates/account/login.html:174 +msgid "Keep me signed in" +msgstr "اجعلني مسجلاً للدخول" + +#: templates/account/logout.html:50 +#, fuzzy +#| msgid "Sign Out" +msgid "Confirm Sign Out" +msgstr "تأكيد تسجيل الخروج" + +#: templates/account/logout.html:52 +#, fuzzy +#| msgid "Are you sure you want to sign out?" +msgid "Are you sure you want to sign out of your account?" +msgstr "هل أنت متأكد أنك تريد تسجيل الخروج من حسابك؟" + +#: templates/account/logout.html:71 +#: templates/forms/form_templates_list.html:382 +#: templates/includes/email_compose_form.html:110 +#: templates/includes/meeting_form.html:40 +#: templates/interviews/schedule_interviews.html:221 +#: templates/jobs/create_job.html:327 templates/jobs/edit_job.html:327 +#: templates/jobs/job_detail.html:536 +#: templates/meetings/create_meeting.html:184 +#: templates/meetings/delete_meeting_form.html:8 +#: templates/meetings/meeting_details.html:399 +#: templates/meetings/set_candidate_form.html:6 +#: templates/meetings/update_meeting.html:237 +#: templates/participants/participants_detail.html:252 +#: templates/participants/participants_list.html:334 +#: templates/recruitment/agency_access_link_form.html:127 +#: templates/recruitment/agency_assignment_detail.html:430 +#: templates/recruitment/agency_assignment_form.html:213 +#: templates/recruitment/agency_confirm_delete.html:357 +#: templates/recruitment/agency_form.html:337 +#: templates/recruitment/agency_portal_assignment_detail.html:510 +#: templates/recruitment/agency_portal_assignment_detail.html:575 +#: templates/recruitment/agency_portal_assignment_detail.html:609 +#: templates/recruitment/agency_portal_submit_candidate.html:333 +#: templates/recruitment/notification_confirm_all_read.html:53 +#: templates/recruitment/notification_confirm_delete.html:33 +#: templates/recruitment/schedule_meeting_form.html:89 +msgid "Cancel" +msgstr "إلغاء" + +#: templates/account/password_change.html:19 +#: templates/user/staff_password_create.html:20 +msgid "" +"Please enter your current password and a new password to secure your account." +msgstr "الرجاء إدخال كلمة المرور الحالية وكلمة مرور جديدة لتأمين حسابك." + +#: templates/account/password_change.html:41 +msgid "Return to Profile" +msgstr "العودة إلى الملف الشخصي" + +#: templates/account/password_reset.html:154 +msgid "Enter your e-mail address to reset your password." +msgstr "أدخل عنوان بريدك الإلكتروني لإعادة تعيين كلمة المرور الخاصة بك." + +#: templates/account/password_reset.html:162 +#, fuzzy +#| msgid "IP Address" +msgid "E-mail Address" +msgstr "عنوان البريد الإلكتروني" + +#: templates/account/password_reset.html:179 +#, fuzzy +#| msgid "Password" +msgid "Reset My Password" +msgstr "إعادة تعيين كلمة المرور الخاصة بي" + +#: templates/account/password_reset.html:185 +msgid "Remember your password?" +msgstr "هل تتذكر كلمة المرور الخاصة بك؟" + +#: templates/account/password_reset.html:186 +msgid "Log In" +msgstr "تسجيل الدخول" + +#: templates/account/password_reset_done.html:7 +#: templates/account/password_reset_done.html:151 +msgid "Password Reset Sent" +msgstr "تم إرسال إعادة تعيين كلمة المرور" + +#: templates/account/password_reset_done.html:160 +#, fuzzy +#| msgid "" +#| "\n" +#| "                        We've **sent an email** to the address you " +#| "provided with instructions on how to reset your password.\n" +#| "                        " +msgid "" +"\n" +" We've **sent an email** to the address you provided " +"with instructions on how to reset your password.\n" +" " +msgstr "" +"\n" +"لقد **أرسلنا بريدًا إلكترونيًا** إلى العنوان الذي قدمته مع تعليمات حول كيفية " +"إعادة تعيين كلمة المرور الخاصة بك." + +#: templates/account/password_reset_done.html:168 +msgid "" +"Please check your inbox (and spam folder). The link in the email is " +"temporary and will expire soon for security reasons." +msgstr "" +"يرجى التحقق من صندوق الوارد الخاص بك (ومجلد الرسائل غير المرغوب فيها). " +"الرابط الموجود في البريد الإلكتروني مؤقت وسينتهي قريبًا لأسباب أمنية." + +#: templates/account/password_reset_done.html:175 +msgid "Return to Login" +msgstr "العودة إلى تسجيل الدخول" + +#: templates/account/password_reset_from_key.html:7 +#: templates/account/password_reset_from_key.html:70 +#, fuzzy +#| msgid "Password" +msgid "Set New Password" +msgstr "تعيين كلمة مرور جديدة" + +#: templates/account/password_reset_from_key.html:76 +msgid "Please enter your new password below." +msgstr "يرجى إدخال كلمة المرور الجديدة الخاصة بك أدناه." + +#: templates/account/password_reset_from_key.html:79 +msgid "You can then log in." +msgstr "يمكنك بعد ذلك تسجيل الدخول." + +#: templates/account/password_reset_from_key.html:96 +msgid "Password Reset Failed" +msgstr "فشل إعادة تعيين كلمة المرور" + +#: templates/account/password_reset_from_key.html:98 +msgid "The password reset link is invalid or has expired." +msgstr "رابط إعادة تعيين كلمة المرور غير صالح أو منتهي الصلاحية." + +#: templates/account/password_reset_from_key.html:102 +msgid "Request New Reset Link" +msgstr "طلب رابط إعادة تعيين جديد" + +#: templates/account/password_reset_from_key_done.html:7 +#, fuzzy +#| msgid "Password" +msgid "Password Changed" +msgstr "تم تغيير كلمة المرور" + +#: templates/account/password_reset_from_key_done.html:69 +#, fuzzy +#| msgid "Posted successfully!" +msgid "Password Changed Successfully" +msgstr "تم تغيير كلمة المرور بنجاح" + +#: templates/account/password_reset_from_key_done.html:72 +msgid "" +"Your password has been set. You can now use your new password to sign in." +msgstr "" +"تم تعيين كلمة المرور الخاصة بك. يمكنك الآن استخدام كلمة المرور الجديدة " +"لتسجيل الدخول." + +#: templates/account/password_reset_from_key_done.html:77 +#: templates/account/verification_sent.html:182 +#, fuzzy +#| msgid "Post to LinkedIn" +msgid "Go to Sign In" +msgstr "انتقال إلى تسجيل الدخول" + +#: templates/account/verification_sent.html:153 +msgid "Verify Your Email Address" +msgstr "تحقق من عنوان بريدك الإلكتروني" + +#: templates/account/verification_sent.html:159 +#, fuzzy +#| msgid "" +#| "\n" +#| "                        We have sent an email to your email id for " +#| "verification. Follow the link provided to finalize the signup process.\n" +#| "                       " +msgid "" +"\n" +" We have sent an email to your email id for " +"verification. Follow the link provided to finalize the signup process.\n" +" " +msgstr "" +"\n" +"لقد أرسلنا بريدًا إلكترونيًا إلى معرف بريدك الإلكتروني للتحقق. اتبع الرابط " +"المقدم لإنهاء عملية التسجيل." + +#: templates/account/verification_sent.html:165 +msgid "" +"If you do not see the verification email in your main inbox, please check " +"your spam folder." +msgstr "" +"إذا لم ترَ رسالة التحقق الإلكترونية في صندوق الوارد الرئيسي، يرجى التحقق من " +"مجلد الرسائل غير المرغوب فيها." + +#: templates/account/verification_sent.html:169 +msgid "" +"Please contact us if you do not receive the verification email within a few " +"minutes." +msgstr "" +"يرجى الاتصال بنا إذا لم تستلم رسالة التحقق الإلكترونية في غضون بضع دقائق." + +#: templates/account/verification_sent.html:176 +msgid "Change or Resend Email" +msgstr "تغيير أو إعادة إرسال البريد الإلكتروني" + +#: templates/admin/sync_dashboard.html:4 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/app_index.html:5 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/base_site.html:3 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/index.html:5 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:15 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/layouts/base.html:7 +msgid "Django site admin" +msgstr "إدارة موقع Django" + +#: templates/agency_base.html:9 +#, fuzzy +#| msgid "King Abdullah Academic University Hospital" +msgid "King Abdullah Academic University Hospital - Agency Portal" +msgstr "مستشفى الملك عبدالله الجامعي - بوابة الوكالة" + +#: templates/agency_base.html:10 +msgid "KAAUH Agency Portal" +msgstr "بوابة وكالة مستشفى الملك عبدالله الجامعي" + +#: templates/agency_base.html:31 templates/base.html:38 +msgid "Saudi Vision 2030" +msgstr "رؤية السعودية 2030" + +#: templates/agency_base.html:59 templates/base.html:59 templates/base.html:63 +msgid "kaauh logo green bg" +msgstr "شعار مستشفى الملك عبدالله الجامعي بخلفية خضراء" + +#: templates/agency_base.html:60 templates/agency_base.html:137 +#: templates/recruitment/agency_portal_login.html:126 +#, fuzzy +#| msgid "Agency Name" +msgid "Agency Portal" +msgstr "بوابة الوكالة" + +#: templates/agency_base.html:64 templates/base.html:68 +msgid "Toggle navigation" +msgstr "تبديل التنقل" + +#: templates/agency_base.html:75 templates/base.html:77 +#: templates/forms/partials/candidate_facing_base.html:267 +msgid "Toggle language menu" +msgstr "تبديل قائمة اللغة" + +#: templates/agency_base.html:103 templates/includes/easy_logs.html:261 +msgid "Logout" +msgstr "تسجيل الخروج" + +#: templates/agency_base.html:119 templates/base.html:302 +#: templates/jobs/job_detail.html:520 +#: templates/recruitment/candidate_exam_view.html:355 +#: templates/recruitment/candidate_hired_view.html:344 +#: templates/recruitment/candidate_interview_view.html:508 +#: templates/recruitment/candidate_screening_view.html:495 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/submit_line.html:19 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:52 +msgid "Close" +msgstr "إغلاق" + +#: templates/agency_base.html:133 templates/base.html:315 +msgid "King Abdullah Academic University Hospital (KAAUH)." +msgstr "مستشفى الملك عبدالله الجامعي (KAAUH)." + +#: templates/agency_base.html:134 templates/base.html:316 +msgid "All rights reserved." +msgstr "جميع الحقوق محفوظة." + +#: templates/agency_base.html:175 +#, fuzzy +#| msgid "Are you sure you want to sign out?" +msgid "Are you sure you want to logout?" +msgstr "هل أنت متأكد من رغبتك في تسجيل الخروج؟" + +#: templates/base.html:9 +msgid "King Abdullah Academic University Hospital - Applicant Tracking System" +msgstr "مستشفى الملك عبدالله الجامعي - نظام تتبع المتقدمين" + +#: templates/base.html:10 +msgid "University ATS" +msgstr "نظام تتبع المتقدمين الجامعي" + +#: templates/base.html:134 +msgid "Toggle user menu" +msgstr "تبديل قائمة المستخدم" + +#: templates/base.html:141 templates/base.html:143 templates/base.html:159 +msgid "Your account" +msgstr "حسابك" + +#: templates/base.html:175 +msgid "My Profile" +msgstr "ملفي الشخصي" + +#: templates/base.html:179 +msgid "Settings" +msgstr "الإعدادات" + +#: templates/base.html:180 +msgid "Activity Log" +msgstr "سجل النشاط" + +#: templates/base.html:192 +msgid "Connect LinkedIn" +msgstr "ربط LinkedIn" + +#: templates/base.html:198 +msgid "LinkedIn Connected" +msgstr "LinkedIn متصل" + +#: templates/base.html:210 +msgid "Sign out" +msgstr "تسجيل الخروج" + +#: templates/base.html:232 templates/jobs/job_candidates_list.html:123 +#: templates/recruitment/partials/ai_overview_breadcromb.html:49 +msgid "Jobs" +msgstr "الوظائف" + +#: templates/base.html:240 templates/jobs/job_candidates_list.html:125 +#: templates/jobs/job_candidates_list.html:207 +#: templates/jobs/job_detail.html:291 templates/jobs/job_list.html:354 +#: templates/recruitment/partials/ai_overview_breadcromb.html:71 +msgid "Applicants" +msgstr "المتقدمون" + +#: templates/base.html:248 +msgid "Agencies" +msgstr "الوكالات" + +#: templates/base.html:258 +msgid "Meetings" +msgstr "الاجتماعات" + +#: templates/base.html:270 +#: templates/recruitment/candidate_interview_view.html:475 +#: templates/recruitment/candidate_interview_view.html:495 +msgid "Participants" +msgstr "المشاركون" + +#: templates/base.html:320 +#, fuzzy +#| msgid "Created by" +msgid "Powered by" +msgstr "بدعم من" + +#: templates/base.html:361 +msgid "Are you sure you want to sign out?" +msgstr "هل أنت متأكد من رغبتك في تسجيل الخروج؟" + +#: templates/forms/application_detail.html:7 +#: templates/forms/application_detail.html:61 +msgid "Job Overview" +msgstr "نظرة عامة على الوظيفة" + +#: templates/forms/application_detail.html:39 +msgid "Ready to Apply?" +msgstr "جاهز للتقديم؟" + +#: templates/forms/application_detail.html:42 +msgid "Review the job details, then apply below." +msgstr "راجع تفاصيل الوظيفة، ثم قدم طلبك أدناه." + +#: templates/forms/application_detail.html:46 +#: templates/forms/application_detail.html:106 +msgid "Apply for this Position" +msgstr "التقدم لهذه الوظيفة" + +#: templates/forms/application_detail.html:66 +#: templates/jobs/job_detail.html:240 +msgid "Salary:" +msgstr "الراتب:" + +#: templates/forms/application_detail.html:73 +#: templates/jobs/job_detail.html:177 +#: templates/recruitment/agency_portal_submit_candidate.html:142 +msgid "Deadline:" +msgstr "الموعد النهائي:" + +#: templates/forms/application_detail.html:77 +msgid "EXPIRED" +msgstr "منتهي الصلاحية" + +#: templates/forms/application_detail.html:80 +msgid "Not specified" +msgstr "غير محدد" + +#: templates/forms/application_detail.html:84 +#: templates/jobs/job_candidates_list.html:149 +#: templates/jobs/job_detail.html:231 +msgid "Job Type:" +msgstr "نوع الوظيفة:" + +#: templates/forms/application_detail.html:85 +#: templates/jobs/job_candidates_list.html:146 +#: templates/jobs/job_detail.html:237 +msgid "Location:" +msgstr "الموقع:" + +#: templates/forms/application_detail.html:86 +#: templates/jobs/job_candidates_list.html:143 +#: templates/jobs/job_detail.html:225 +#: templates/recruitment/agency_portal_submit_candidate.html:139 +msgid "Department:" +msgstr "القسم:" + +#: templates/forms/application_detail.html:87 +msgid "JOB ID:" +msgstr "معرف الوظيفة:" + +#: templates/forms/application_detail.html:88 +#: templates/jobs/job_candidates_list.html:152 +#: templates/jobs/job_detail.html:234 +msgid "Workplace:" +msgstr "مكان العمل:" + +#: templates/forms/application_detail.html:91 +#: templates/jobs/create_job.html:189 templates/jobs/edit_job.html:189 +#: templates/jobs/job_detail.html:256 +msgid "Job Description" +msgstr "وصف الوظيفة" + +#: templates/forms/application_detail.html:92 +#, fuzzy +#| msgid "Required Qualifications" +msgid "Qualifications" +msgstr "المؤهلات" + +#: templates/forms/application_detail.html:93 +#: templates/jobs/create_job.html:221 templates/jobs/edit_job.html:221 +#: templates/jobs/job_detail.html:268 +msgid "Benefits" +msgstr "المزايا" + +#: templates/forms/application_detail.html:94 +#: templates/jobs/create_job.html:229 templates/jobs/edit_job.html:229 +#: templates/jobs/job_detail.html:274 +msgid "Application Instructions" +msgstr "تعليمات التقديم" + +#: templates/forms/application_submit_form.html:489 +#: templates/forms/partials/candidate_facing_base.html:11 +msgid "Application Form" +msgstr "نموذج التقديم" + +#: templates/forms/application_submit_form.html:504 +msgid "Review Your Application" +msgstr "مراجعة طلبك" + +#: templates/forms/application_submit_form.html:516 +msgid "Back" +msgstr "العودة" + +#: templates/forms/application_submit_form.html:520 +#: templates/includes/paginator.html:20 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/pagination_infinite.html:9 +msgid "Next" +msgstr "التالي" + +#: templates/forms/application_submit_form.html:529 +msgid "Submit Application" +msgstr "إرسال الطلب" + +#: templates/forms/form_submission_details.html:160 +#, fuzzy +#| msgid "Job Details" +msgid "Submission Details" +msgstr "تفاصيل الإرسال" + +#: templates/forms/form_submission_details.html:162 +#: templates/forms/form_template_all_submissions.html:249 +#: templates/forms/form_template_all_submissions.html:364 +#, fuzzy +#| msgid "Back to Jobs" +msgid "Back to Submissions" +msgstr "العودة إلى الإرسالات" + +#: templates/forms/form_submission_details.html:170 +msgid "Submission Metadata" +msgstr "بيانات تعريف الإرسال" + +#: templates/forms/form_submission_details.html:176 +msgid "Submission ID:" +msgstr "معرف الإرسال:" + +#: templates/forms/form_submission_details.html:180 +#: templates/recruitment/agency_portal_submit_candidate.html:151 +#, fuzzy +#| msgid "Submit" +msgid "Submitted:" +msgstr "تاريخ الإرسال:" + +#: templates/forms/form_submission_details.html:184 +msgid "Form:" +msgstr "النموذج:" + +#: templates/forms/form_submission_details.html:192 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant Name:" +msgstr "اسم المتقدم:" + +#: templates/forms/form_submission_details.html:198 +#, fuzzy +#| msgid "Email" +msgid "Email:" +msgstr "البريد الإلكتروني:" + +#: templates/forms/form_submission_details.html:208 +#, fuzzy +#| msgid "Response" +msgid "Form Responses" +msgstr "استجابات النموذج" + +#: templates/forms/form_submission_details.html:219 +msgid "Field Property" +msgstr "خاصية الحقل" + +#: templates/forms/form_submission_details.html:227 +#, fuzzy +#| msgid "Response Data" +msgid "Response Value" +msgstr "قيمة الاستجابة" + +#: templates/forms/form_submission_details.html:233 +#, fuzzy +#| msgid "Download Resume" +msgid "Download File" +msgstr "تحميل الملف" + +#: templates/forms/form_submission_details.html:234 +#, fuzzy +#| msgid "Download Resume" +msgid "Download" +msgstr "تحميل" + +#: templates/forms/form_submission_details.html:250 +#, fuzzy +#| msgid "No description provided" +msgid "Not provided" +msgstr "لم يتم تقديمه" + +#: templates/forms/form_submission_details.html:256 +#, fuzzy +#| msgid "Application Stage" +msgid "Associated Stage" +msgstr "المرحلة المرتبطة" + +#: templates/forms/form_submission_details.html:264 +msgid "Field Required" +msgstr "الحقل مطلوب" + +#: templates/forms/form_submission_details.html:268 +#: templates/recruitment/candidate_detail.html:538 +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:867 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:88 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:96 +msgid "Yes" +msgstr "نعم" + +#: templates/forms/form_submission_details.html:270 +#: templates/recruitment/candidate_detail.html:540 +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:868 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:89 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:97 +msgid "No" +msgstr "لا" + +#: templates/forms/form_submission_details.html:281 +msgid "No response fields were found for this submission." +msgstr "لم يتم العثور على حقول استجابة لهذا الإرسال." + +#: templates/forms/form_submission_details.html:282 +msgid "" +"This may occur if the form template was modified or responses were cleared." +msgstr "قد يحدث هذا إذا تم تعديل قالب النموذج أو مسح الاستجابات." + +#: templates/forms/form_template_all_submissions.html:232 +#: templates/forms/form_template_submissions_list.html:188 +#: templates/jobs/job_candidates_list.html:122 +#: templates/recruitment/agency_portal_submit_candidate.html:95 +msgid "Dashboard" +msgstr "لوحة التحكم" + +#: templates/forms/form_template_all_submissions.html:233 +#: templates/forms/form_template_submissions_list.html:189 +#: templates/forms/form_templates_list.html:152 +msgid "Form Templates" +msgstr "قوالب النماذج" + +#: templates/forms/form_template_all_submissions.html:234 +#: templates/forms/form_template_submissions_list.html:193 +#: templates/forms/form_templates_list.html:240 +#: templates/forms/form_templates_list.html:295 +#: templates/jobs/job_list.html:320 templates/jobs/job_list.html:373 +#: templates/recruitment/agency_access_link_detail.html:151 +msgid "Submissions" +msgstr "الإرسالات" + +#: templates/forms/form_template_all_submissions.html:235 +msgid "All Submissions Table" +msgstr "جدول جميع الإرسالات" + +#: templates/forms/form_template_all_submissions.html:244 +msgid "All Submissions for" +msgstr "جميع الإرسالات لـ" + +#: templates/forms/form_template_all_submissions.html:258 +#: templates/forms/form_template_submissions_list.html:227 +msgid "Submission ID" +msgstr "معرف الإرسال" + +#: templates/forms/form_template_all_submissions.html:259 +#: templates/forms/form_template_submissions_list.html:228 +#: templates/forms/form_template_submissions_list.html:265 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant Name" +msgstr "اسم المتقدم" + +#: templates/forms/form_template_all_submissions.html:260 +#: templates/forms/form_template_submissions_list.html:229 +#: templates/forms/form_template_submissions_list.html:266 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant Email" +msgstr "البريد الإلكتروني للمتقدم" + +#: templates/forms/form_template_all_submissions.html:261 +#: templates/forms/form_template_submissions_list.html:230 +#: templates/forms/form_template_submissions_list.html:267 +#, fuzzy +#| msgid "Submitted by Agency" +msgid "Submitted At" +msgstr "تاريخ الإرسال" + +#: templates/forms/form_template_all_submissions.html:315 +#: templates/forms/form_template_submissions_list.html:286 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "                                Showing %(start)s to %(end)s of %(total)s " +#| "results.\n" +#| "                            " +msgid "" +"\n" +" Showing %(start)s to %(end)s of %(total)s " +"results.\n" +" " +msgstr "" +"\n" +"                                عرض %(start)s إلى %(end)s من %(total)s نتيجة." + +#: templates/forms/form_template_all_submissions.html:336 +#: templates/forms/form_template_submissions_list.html:307 +#: templates/includes/paginator.html:15 +msgid "Page" +msgstr "صفحة" + +#: templates/forms/form_template_all_submissions.html:336 +#: templates/forms/form_template_submissions_list.html:307 +#: templates/includes/easy_logs.html:159 +msgid "of" +msgstr "من" + +#: templates/forms/form_template_all_submissions.html:359 +#: templates/forms/form_template_submissions_list.html:330 +#, fuzzy +#| msgid "No meetings found." +msgid "No Submissions Found" +msgstr "لم يتم العثور على إرسالات" + +#: templates/forms/form_template_all_submissions.html:361 +#: templates/forms/form_template_submissions_list.html:332 +msgid "There are no submissions for this form template yet." +msgstr "لا توجد إرسالات لقالب النموذج هذا بعد." + +#: templates/forms/form_template_submissions_list.html:202 +msgid "Submissions for" +msgstr "الإرسالات لـ" + +#: templates/forms/form_template_submissions_list.html:208 +#, fuzzy +#| msgid "View All Applicants" +msgid "View All in Table" +msgstr "عرض الكل في جدول" + +#: templates/forms/form_template_submissions_list.html:211 +#: templates/forms/form_template_submissions_list.html:335 +msgid "Back to Templates" +msgstr "العودة إلى القوالب" + +#: templates/forms/form_template_submissions_list.html:231 +#: templates/forms/form_templates_list.html:275 +#: templates/jobs/job_candidates_list.html:231 templates/jobs/job_list.html:272 +#: templates/meetings/list_meetings.html:313 +#: templates/participants/participants_list.html:218 +#: templates/recruitment/agency_access_link_detail.html:168 +#: templates/recruitment/agency_assignment_detail.html:243 +#: templates/recruitment/agency_assignment_detail.html:339 +#: templates/recruitment/agency_assignment_list.html:117 +#: templates/recruitment/agency_list.html:191 +#: templates/recruitment/agency_portal_assignment_detail.html:245 +#: templates/recruitment/candidate_exam_view.html:255 +#: templates/recruitment/candidate_hired_view.html:237 +#: templates/recruitment/candidate_interview_view.html:269 +#: templates/recruitment/candidate_list.html:278 +#: templates/recruitment/candidate_offer_view.html:249 +#: templates/recruitment/candidate_screening_view.html:382 +#: templates/recruitment/notification_detail.html:126 +#: templates/recruitment/partials/_candidate_table.html:14 +#: templates/recruitment/training_list.html:207 +#: templates/user/admin_settings.html:179 +msgid "Actions" +msgstr "الإجراءات" + +#: templates/forms/form_template_submissions_list.html:243 +#: templates/forms/form_template_submissions_list.html:272 +#: templates/recruitment/agency_assignment_detail.html:268 +#: templates/recruitment/agency_assignment_list.html:160 +#: templates/recruitment/agency_portal_dashboard.html:209 +#, fuzzy +#| msgid "Core Details" +msgid "View Details" +msgstr "عرض التفاصيل" + +#: templates/forms/form_template_submissions_list.html:260 +#, fuzzy +#| msgid "Submit" +msgid "Submission" +msgstr "الإرسال" + +#: templates/forms/form_templates_list.html:155 +#, fuzzy +#| msgid "Create Template" +msgid "Create New Template" +msgstr "إنشاء قالب جديد" + +#: templates/forms/form_templates_list.html:165 +#, fuzzy +#| msgid "Template Name" +msgid "Search by Template Name" +msgstr "البحث باسم القالب" + +#: templates/forms/form_templates_list.html:169 +msgid "Search templates by name..." +msgstr "البحث عن القوالب بالاسم..." + +#: templates/forms/form_templates_list.html:177 +#: templates/includes/search_form.html:16 +#: templates/recruitment/agency_assignment_list.html:77 +#: templates/recruitment/agency_assignment_list.html:98 +#: templates/recruitment/agency_list.html:167 +msgid "Search" +msgstr "بحث" + +#: templates/forms/form_templates_list.html:183 +#, fuzzy +#| msgid "Search" +msgid "Clear Search" +msgstr "مسح البحث" + +#: templates/forms/form_templates_list.html:213 +#: templates/forms/form_templates_list.html:271 +msgid "Stages" +msgstr "المراحل" + +#: templates/forms/form_templates_list.html:217 +#: templates/forms/form_templates_list.html:272 +msgid "Fields" +msgstr "الحقول" + +#: templates/forms/form_templates_list.html:226 +msgid "No description provided" +msgstr "لم يتم تقديم وصف" + +#: templates/forms/form_templates_list.html:234 +#: templates/forms/form_templates_list.html:289 +#: templates/jobs/job_list.html:314 +msgid "Preview" +msgstr "معاينة" + +#: templates/forms/form_templates_list.html:237 +#: templates/forms/form_templates_list.html:292 +#: templates/jobs/job_candidates_list.html:257 +#: templates/jobs/job_candidates_list.html:353 templates/jobs/job_list.html:306 +#: templates/jobs/job_list.html:317 +#: templates/participants/participants_list.html:236 +#: templates/participants/participants_list.html:280 +#: templates/recruitment/agency_assignment_list.html:164 +#: templates/recruitment/agency_list.html:251 +#: templates/recruitment/agency_list.html:323 +#: templates/recruitment/candidate_list.html:329 +#: templates/recruitment/candidate_list.html:377 +#: templates/recruitment/training_list.html:181 +#: templates/recruitment/training_list.html:222 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:51 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:51 +msgid "Edit" +msgstr "تعديل" + +#: templates/forms/form_templates_list.html:243 +#: templates/forms/form_templates_list.html:298 +#: templates/jobs/job_candidates_list.html:260 +#: templates/jobs/job_candidates_list.html:356 +#: templates/meetings/list_meetings.html:284 +#: templates/meetings/list_meetings.html:372 +#: templates/participants/participants_detail.html:142 +#: templates/participants/participants_detail.html:255 +#: templates/participants/participants_list.html:239 +#: templates/participants/participants_list.html:282 +#: templates/participants/participants_list.html:337 +#: templates/recruitment/candidate_list.html:332 +#: templates/recruitment/candidate_list.html:379 +#: templates/recruitment/notification_detail.html:141 +#: templates/recruitment/training_list.html:183 +#: templates/recruitment/training_list.html:225 +#: templates/recruitment/training_update.html:184 +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:499 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_preview.html:26 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:60 +msgid "Delete" +msgstr "حذف" + +#: templates/forms/form_templates_list.html:254 +#: templates/recruitment/agency_detail.html:518 +#: templates/recruitment/agency_form.html:393 +#: templates/recruitment/notification_confirm_delete.html:23 +msgid "Created:" +msgstr "تم الإنشاء:" + +#: templates/forms/form_templates_list.html:255 +msgid "ago" +msgstr "منذ" + +#: templates/forms/form_templates_list.html:273 +#: templates/recruitment/agency_confirm_delete.html:264 +#: templates/recruitment/agency_list.html:190 +#: templates/recruitment/agency_list.html:328 +#: templates/recruitment/notification_detail.html:169 +#: templates/recruitment/training_list.html:206 +msgid "Created" +msgstr "تم الإنشاء" + +#: templates/forms/form_templates_list.html:274 +#: templates/participants/participants_detail.html:218 +#, fuzzy +#| msgid "Last Updated:" +msgid "Last Updated" +msgstr "آخر تحديث" + +#: templates/forms/form_templates_list.html:346 +msgid "No Form Templates Found" +msgstr "لم يتم العثور على قوالب نماذج" + +#: templates/forms/form_templates_list.html:349 +#, python-format +msgid "No templates match your search \"%(query)s\"." +msgstr "لا توجد قوالب تطابق بحثك \"%(query)s\"." + +#: templates/forms/form_templates_list.html:351 +msgid "You haven't created any form templates yet." +msgstr "لم تقم بإنشاء أي قوالب نماذج بعد." + +#: templates/forms/form_templates_list.html:355 +msgid "Create Your First Template" +msgstr "إنشاء أول قالب لك" + +#: templates/forms/form_templates_list.html:370 +#: templates/jobs/job_detail.html:347 +#, fuzzy +#| msgid "Create New Form" +msgid "Create New Form Template" +msgstr "إنشاء قالب نموذج جديد" + +#: templates/forms/partials/candidate_facing_base.html:244 +#: templates/jobs/application_success.html:126 +msgid "KAAUH IMAGE" +msgstr "صورة مستشفى الملك عبدالله الجامعي" + +#: templates/forms/partials/candidate_facing_base.html:261 +#: templates/jobs/application_success.html:141 +msgid "Careers" +msgstr "الوظائف" + +#: templates/includes/candidate_exam_status_form.html:6 +#: templates/meetings/list_meetings.html:369 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_preview.html:28 +msgid "Update" +msgstr "تحديث" + +#: templates/includes/candidate_modal_body.html:2 +#: templates/recruitment/candidate_exam_view.html:252 +#: templates/recruitment/candidate_screening_view.html:370 +#: templates/recruitment/partials/_candidate_table.html:11 +msgid "AI Score" +msgstr "نقاط الذكاء الاصطناعي" + +#: templates/includes/candidate_modal_body.html:8 +#, fuzzy +#| msgid "Job Title" +msgid "Job Fit" +msgstr "مدى ملاءمة الوظيفة" + +#: templates/includes/candidate_modal_body.html:15 +#: templates/recruitment/candidate_detail.html:511 +msgid "Top Keywords" +msgstr "أهم الكلمات المفتاحية" + +#: templates/includes/candidate_modal_body.html:29 +msgid "Experience" +msgstr "الخبرة" + +#: templates/includes/candidate_modal_body.html:31 +msgid "years" +msgstr "سنوات" + +#: templates/includes/candidate_modal_body.html:32 +msgid "Recent Role:" +msgstr "الدور الأخير:" + +#: templates/includes/candidate_modal_body.html:37 +msgid "Skills" +msgstr "المهارات" + +#: templates/includes/candidate_modal_body.html:39 +msgid "Soft Skills:" +msgstr "المهارات الشخصية:" + +#: templates/includes/candidate_modal_body.html:40 +msgid "Industry Match:" +msgstr "التطابق مع الصناعة:" + +#: templates/includes/candidate_modal_body.html:49 +#: templates/recruitment/candidate_detail.html:505 +msgid "Recommendation" +msgstr "التوصية" + +#: templates/includes/candidate_modal_body.html:54 +#: templates/recruitment/candidate_detail.html:496 +msgid "Strengths" +msgstr "نقاط القوة" + +#: templates/includes/candidate_modal_body.html:59 +#: templates/recruitment/candidate_detail.html:499 +msgid "Weaknesses" +msgstr "نقاط الضعف" + +#: templates/includes/candidate_modal_body.html:64 +#: templates/recruitment/candidate_detail.html:551 +msgid "Criteria Assessment" +msgstr "تقييم المعايير" + +#: templates/includes/candidate_modal_body.html:69 +#: templates/recruitment/candidate_detail.html:556 +#, fuzzy +#| msgid "Create Material" +msgid "Criteria" +msgstr "المعايير" + +#: templates/includes/candidate_modal_body.html:79 +#: templates/includes/candidate_modal_body.html:100 +#: templates/recruitment/candidate_detail.html:566 +msgid "Met" +msgstr "تم استيفاؤها" + +#: templates/includes/candidate_modal_body.html:81 +#: templates/includes/candidate_modal_body.html:102 +#: templates/recruitment/candidate_detail.html:568 +msgid "Not Met" +msgstr "لم يتم استيفاؤها" + +#: templates/includes/candidate_modal_body.html:97 +#, fuzzy +#| msgid "Description & Requirements" +msgid "Minimum Requirements" +msgstr "الحد الأدنى من المتطلبات" + +#: templates/includes/candidate_modal_body.html:108 +#: templates/recruitment/candidate_screening_view.html:275 +msgid "Screening Rating" +msgstr "تقييم الفرز" + +#: templates/includes/candidate_modal_body.html:116 +#: templates/recruitment/candidate_detail.html:583 +msgid "Language Fluency" +msgstr "طلاقة اللغة" + +#: templates/includes/copy_to_clipboard.html:5 +#: templates/includes/easy_logs.html:269 +#: templates/recruitment/agency_assignment_detail.html:453 +msgid "Success" +msgstr "نجاح" + +#: templates/includes/copy_to_clipboard.html:9 +#, python-format +msgid "Copied \"%(text)s\" to clipboard!" +msgstr "تم نسخ \"%(text)s\" إلى الحافظة!" + +#: templates/includes/easy_logs.html:5 +#, fuzzy +#| msgid "Dashboard" +msgid "Audit Dashboard" +msgstr "لوحة تحكم التدقيق" + +#: templates/includes/easy_logs.html:153 +msgid "System Audit Logs" +msgstr "سجلات تدقيق النظام" + +#: templates/includes/easy_logs.html:157 +msgid "Viewing Logs" +msgstr "عرض السجلات" + +#: templates/includes/easy_logs.html:159 +msgid "Displaying" +msgstr "يتم عرض" + +#: templates/includes/easy_logs.html:160 +msgid "total records." +msgstr "إجمالي السجلات." + +#: templates/includes/easy_logs.html:197 templates/includes/easy_logs.html:206 +#: templates/includes/easy_logs.html:214 +#, fuzzy +#| msgid "Start Time" +msgid "Date/Time" +msgstr "التاريخ/الوقت" + +#: templates/includes/easy_logs.html:198 templates/includes/easy_logs.html:207 +#: templates/includes/easy_logs.html:215 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html:17 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:33 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:15 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:33 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:14 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:30 +msgid "User" +msgstr "المستخدم" + +#: templates/includes/easy_logs.html:200 +msgid "Model" +msgstr "النموذج" + +#: templates/includes/easy_logs.html:201 +msgid "Object PK" +msgstr "المفتاح الأساسي للكائن" + +#: templates/includes/easy_logs.html:202 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:35 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:81 +#, fuzzy +#| msgid "Change Stage" +msgid "Changes" +msgstr "التغييرات" + +#: templates/includes/easy_logs.html:208 +#: templates/recruitment/notification_detail.html:161 +#: templates/recruitment/notification_list.html:46 +#, fuzzy +#| msgid "Job Type" +msgid "Type" +msgstr "النوع" + +#: templates/includes/easy_logs.html:216 +#, fuzzy +#| msgid "HTTP Method" +msgid "Method" +msgstr "الطريقة" + +#: templates/includes/easy_logs.html:217 +msgid "Path" +msgstr "المسار" + +#: templates/includes/easy_logs.html:234 +msgid "CREATE" +msgstr "إنشاء" + +#: templates/includes/easy_logs.html:235 +msgid "UPDATE" +msgstr "تحديث" + +#: templates/includes/easy_logs.html:236 +msgid "DELETE" +msgstr "حذف" + +#: templates/includes/easy_logs.html:260 +msgid "Login" +msgstr "تسجيل الدخول" + +#: templates/includes/easy_logs.html:262 +#, fuzzy +#| msgid "Failed" +msgid "Failed Login" +msgstr "فشل تسجيل الدخول" + +#: templates/includes/easy_logs.html:288 +msgid "No logs found for this section or the database is empty." +msgstr "لم يتم العثور على سجلات لهذا القسم أو قاعدة البيانات فارغة." + +#: templates/includes/email_compose_form.html:10 +#: templates/recruitment/candidate_interview_view.html:523 +#, fuzzy +#| msgid "Host Email" +msgid "Compose Email" +msgstr "إنشاء بريد إلكتروني" + +#: templates/includes/email_compose_form.html:103 +msgid "Email will be sent to all selected recipients" +msgstr "سيتم إرسال البريد الإلكتروني إلى جميع المستلمين المحددين" + +#: templates/includes/email_compose_form.html:116 +#: templates/includes/email_compose_form.html:236 +#: templates/includes/email_compose_form.html:267 +#: templates/recruitment/agency_detail.html:501 +#, fuzzy +#| msgid "Email" +msgid "Send Email" +msgstr "إرسال بريد إلكتروني" + +#: templates/includes/email_compose_form.html:131 +#: templates/recruitment/agency_portal_submit_candidate.html:381 +#: templates/recruitment/candidate_detail.html:599 +msgid "Loading..." +msgstr "جاري التحميل..." + +#: templates/includes/email_compose_form.html:134 +msgid "Sending email..." +msgstr "جاري إرسال البريد الإلكتروني..." + +#: templates/includes/email_compose_form.html:208 +msgid "Sending..." +msgstr "جاري الإرسال..." + +#: templates/includes/meeting_form.html:15 +#, fuzzy +#| msgid "Start Time" +msgid "Start Time and Date" +msgstr "وقت وتاريخ البدء" + +#: templates/includes/meeting_form.html:20 +#: templates/meetings/create_meeting.html:170 +#: templates/meetings/reschedule_meeting.html:51 +#: templates/meetings/schedule_meeting_form.html:62 +#: templates/meetings/update_meeting.html:223 +#: templates/recruitment/schedule_meeting_form.html:72 +msgid "Duration (minutes)" +msgstr "المدة (بالدقائق)" + +#: templates/includes/meeting_form.html:25 +msgid "Meeting Details (will appear after scheduling):" +msgstr "تفاصيل الاجتماع (ستظهر بعد الجدولة):" + +#: templates/includes/meeting_form.html:26 +#, fuzzy +#| msgid "Join URL" +msgid "Join URL:" +msgstr "رابط الانضمام:" + +#: templates/includes/meeting_form.html:27 +#, fuzzy +#| msgid "Meeting ID" +msgid "Meeting ID:" +msgstr "معرف الاجتماع:" + +#: templates/includes/meeting_form.html:32 +msgid "Click here to join meeting" +msgstr "انقر هنا للانضمام إلى الاجتماع" + +#: templates/includes/meeting_form.html:42 +#, fuzzy +#| msgid "Delete Meeting" +msgid "Reschedule Meeting" +msgstr "إعادة جدولة الاجتماع" + +#: templates/includes/meeting_form.html:42 +#: templates/includes/meeting_form.html:71 +#: templates/meetings/schedule_meeting_form.html:84 +#: templates/recruitment/schedule_meeting_form.html:4 +#: templates/recruitment/schedule_meeting_form.html:86 +#, fuzzy +#| msgid "Delete Meeting" +msgid "Schedule Meeting" +msgstr "جدولة الاجتماع" + +#: templates/includes/meeting_form.html:67 +#: templates/meetings/schedule_meeting_form.html:9 +#: templates/recruitment/candidate_interview_view.html:407 +#: templates/recruitment/schedule_meeting_form.html:15 +#, fuzzy +#| msgid "Interview" +msgid "Schedule Interview" +msgstr "جدولة مقابلة" + +#: templates/includes/meeting_form.html:83 +msgid "Processing..." +msgstr "جاري المعالجة..." + +#: templates/includes/meeting_form.html:129 +msgid "An unknown error occurred." +msgstr "حدث خطأ غير معروف." + +#: templates/includes/meeting_form.html:137 +msgid "An error occurred while processing your request." +msgstr "حدث خطأ أثناء معالجة طلبك." + +#: templates/includes/paginator.html:7 +msgid "First" +msgstr "الصفحة الأولى" + +#: templates/includes/paginator.html:10 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/pagination_infinite.html:5 +msgid "Previous" +msgstr "السابق" + +#: templates/includes/paginator.html:23 +msgid "Last" +msgstr "الصفحة الأخيرة" + +#: templates/includes/search_form.html:14 +msgid "Search..." +msgstr "بحث..." + +#: templates/interviews/schedule_interviews.html:110 +msgid "Bulk Interview Scheduling" +msgstr "الجدولة الجماعية للمقابلات" + +#: templates/interviews/schedule_interviews.html:113 +msgid "Configure time slots for:" +msgstr "تكوين الفترات الزمنية لـ:" + +#: templates/interviews/schedule_interviews.html:117 +#: templates/recruitment/candidate_exam_view.html:187 +#: templates/recruitment/candidate_hired_view.html:211 +#: templates/recruitment/candidate_interview_view.html:190 +#: templates/recruitment/candidate_offer_view.html:189 +#: templates/recruitment/candidate_screening_view.html:238 +#, fuzzy +#| msgid "Back to Jobs" +msgid "Back to Job" +msgstr "العودة إلى الوظيفة" + +#: templates/interviews/schedule_interviews.html:127 +#, fuzzy +#| msgid "Delete Candidate" +msgid "Select Candidates" +msgstr "تحديد المرشحين" + +#: templates/interviews/schedule_interviews.html:131 +msgid "Candidates to Schedule (Hold Ctrl/Cmd to select multiple)" +msgstr "المرشحون للجدولة (اضغط على Ctrl/Cmd لتحديد أكثر من واحد)" + +#: templates/interviews/schedule_interviews.html:141 +#, fuzzy +#| msgid "Schedule" +msgid "Schedule Details" +msgstr "تفاصيل الجدول" + +#: templates/interviews/schedule_interviews.html:183 +#, fuzzy +#| msgid "Duration (minutes)" +msgid "Duration (min)" +msgstr "المدة (دقيقة)" + +#: templates/interviews/schedule_interviews.html:190 +#, fuzzy +#| msgid "Buffer Time (minutes)" +msgid "Buffer (min)" +msgstr "الفاصل (دقيقة)" + +#: templates/interviews/schedule_interviews.html:197 +#, fuzzy +#| msgid "Break End Time" +msgid "Daily Break Times" +msgstr "أوقات الاستراحة اليومية" + +#: templates/interviews/schedule_interviews.html:218 +#, fuzzy +#| msgid "Schedule" +msgid "Preview Schedule" +msgstr "معاينة الجدول" + +#: templates/jobs/application_success.html:7 +#, fuzzy +#| msgid "Application Stage" +msgid "Application Submitted - Thank You" +msgstr "تم إرسال الطلب - شكرًا لك" + +#: templates/jobs/application_success.html:135 +#: templates/recruitment/dashboard.html:263 +msgid "Applications" +msgstr "الطلبات" + +#: templates/jobs/application_success.html:138 +msgid "Profile" +msgstr "الملف الشخصي" + +#: templates/jobs/application_success.html:150 +#, fuzzy +#| msgid "Application Information" +msgid "Application Confirmation" +msgstr "تأكيد الطلب" + +#: templates/jobs/application_success.html:168 +msgid "Thank You!" +msgstr "شكرًا لك!" + +#: templates/jobs/application_success.html:169 +msgid "Your application has been submitted successfully" +msgstr "تم إرسال طلبك بنجاح" + +#: templates/jobs/application_success.html:183 +msgid "" +"We appreciate your interest in joining our team. Our hiring team will review " +"your application and contact you if there's a potential match for this " +"position." +msgstr "" +"نقدر اهتمامك بالانضمام إلى فريقنا. سيقوم فريق التوظيف بمراجعة طلبك والاتصال " +"بك إذا كان هناك تطابق محتمل لهذا المنصب." + +#: templates/jobs/application_success.html:188 +msgid "Return to Job Listings" +msgstr "العودة إلى قوائم الوظائف" + +#: templates/jobs/career.html:224 templates/jobs/career.html:237 +msgid "Job ID#" +msgstr "معرف الوظيفة #" + +#: templates/jobs/career.html:225 templates/jobs/career.html:239 +#: templates/jobs/create_job.html:122 templates/jobs/edit_job.html:122 +#: templates/meetings/meeting_details.html:251 +#: templates/recruitment/agency_portal_assignment_detail.html:143 +msgid "Job Title" +msgstr "المسمى الوظيفي" + +#: templates/jobs/career.html:226 templates/jobs/career.html:241 +#, fuzzy +#| msgid "Hiring Agency" +msgid "Hiring" +msgstr "التوظيف" + +#: templates/jobs/career.html:227 templates/jobs/career.html:244 +#, fuzzy +#| msgid "Join Date" +msgid "Posting Date" +msgstr "تاريخ النشر" + +#: templates/jobs/career.html:228 templates/jobs/career.html:247 +#, fuzzy +#| msgid "Applied for:" +msgid "Apply Before" +msgstr "قدم قبل" + +#: templates/jobs/career.html:229 templates/jobs/career.html:249 +#: templates/recruitment/candidate_interview_view.html:266 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:27 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:98 +msgid "Link" +msgstr "الرابط" + +#: templates/jobs/career.html:253 +#: templates/participants/participants_list.html:186 +#: templates/recruitment/candidate_list.html:243 +msgid "Apply" +msgstr "تقديم" + +#: templates/jobs/create_job.html:105 templates/jobs/edit_job.html:105 +#, fuzzy +#| msgid "Create New Job Posting" +msgid "Edit Job Posting" +msgstr "تعديل إعلان الوظيفة" + +#: templates/jobs/create_job.html:105 templates/jobs/edit_job.html:105 +msgid "Create New Job Posting" +msgstr "إنشاء إعلان وظيفة جديد" + +#: templates/jobs/create_job.html:116 templates/jobs/edit_job.html:116 +msgid "Core Position Details" +msgstr "التفاصيل الأساسية للوظيفة" + +#: templates/jobs/create_job.html:129 templates/jobs/edit_job.html:129 +#: templates/meetings/meeting_details.html:254 +msgid "Job Type" +msgstr "نوع الوظيفة" + +#: templates/jobs/create_job.html:137 templates/jobs/edit_job.html:137 +msgid "Workplace Type" +msgstr "نوع مكان العمل" + +#: templates/jobs/create_job.html:144 templates/jobs/edit_job.html:144 +msgid "Application Deadline" +msgstr "الموعد النهائي للتقديم" + +#: templates/jobs/create_job.html:151 templates/jobs/edit_job.html:151 +msgid "Department" +msgstr "القسم" + +#: templates/jobs/create_job.html:158 templates/jobs/edit_job.html:158 +#: templates/recruitment/partials/stats_cards.html:37 +msgid "Open Positions" +msgstr "المناصب المفتوحة" + +#: templates/jobs/create_job.html:165 templates/jobs/edit_job.html:165 +msgid "Max Applications" +msgstr "الحد الأقصى للمتقدمين" + +#: templates/jobs/create_job.html:183 templates/jobs/edit_job.html:183 +msgid "Job Content" +msgstr "بيانات الوظيفة" + +#: templates/jobs/create_job.html:197 templates/jobs/edit_job.html:197 +msgid "Qualifications and Requirements" +msgstr "المؤهلات والمتطلبات" + +#: templates/jobs/create_job.html:211 templates/jobs/edit_job.html:211 +msgid "Benefits & Application Instructions" +msgstr "المزايا وتعليمات التقديم" + +#: templates/jobs/create_job.html:243 templates/jobs/edit_job.html:243 +msgid "Internal & Promotion" +msgstr "المعلومات الداخلية والترويج" + +#: templates/jobs/create_job.html:249 templates/jobs/edit_job.html:249 +msgid "Position Number" +msgstr "رقم المنصب" + +#: templates/jobs/create_job.html:256 templates/jobs/edit_job.html:256 +msgid "Reports To" +msgstr "يقدم تقاريره إلى" + +#: templates/jobs/create_job.html:266 templates/jobs/edit_job.html:266 +msgid "Hashtags (For Promotion/Search on Linkedin)" +msgstr "الهاشتاجات (للترويج/البحث على LinkedIn)" + +#: templates/jobs/create_job.html:269 templates/jobs/edit_job.html:269 +#, fuzzy +#| msgid "Comma-separated list of trusted IP addresses" +msgid "Comma-separated list of hashtags, e.g., #hiring, #professor" +msgstr "قائمة هاشتاجات مفصولة بفواصل، مثال: #توظيف، #أستاذ" + +#: templates/jobs/create_job.html:282 templates/jobs/edit_job.html:282 +msgid "Location & Salary" +msgstr "الموقع والراتب" + +#: templates/jobs/create_job.html:288 templates/jobs/edit_job.html:288 +#: templates/recruitment/agency_detail.html:351 +msgid "City" +msgstr "المدينة" + +#: templates/jobs/create_job.html:295 templates/jobs/edit_job.html:295 +msgid "State/Province" +msgstr "الولاية/المقاطعة" + +#: templates/jobs/create_job.html:312 templates/jobs/edit_job.html:312 +msgid "Salary Range" +msgstr "نطاق الراتب" + +#: templates/jobs/create_job.html:330 templates/jobs/edit_job.html:330 +msgid "Save Job" +msgstr "حفظ" + +#: templates/jobs/job_candidates_list.html:118 +#, fuzzy +#| msgid "Applicants" +msgid "Applicants for" +msgstr "المتقدمون لـ" + +#: templates/jobs/job_candidates_list.html:131 +#, fuzzy +#| msgid "New Application Stage" +msgid "Add New Applicant" +msgstr "إضافة متقدم جديد" + +#: templates/jobs/job_candidates_list.html:162 +#: templates/jobs/job_detail.html:315 +#, fuzzy +#| msgid "Applicants" +msgid "Total Applicants" +msgstr "إجمالي المتقدمين" + +#: templates/jobs/job_candidates_list.html:175 +#, fuzzy +#| msgid "Applicants" +msgid "Search Applicants" +msgstr "البحث عن المتقدمين" + +#: templates/jobs/job_candidates_list.html:179 +msgid "Search by name, email, phone, or stage..." +msgstr "البحث بالاسم أو البريد الإلكتروني أو الهاتف أو المرحلة..." + +#: templates/jobs/job_candidates_list.html:185 +msgid "Filter Results" +msgstr "تصفية النتائج" + +#: templates/jobs/job_candidates_list.html:190 +#: templates/recruitment/notification_list.html:200 +msgid "Clear Filters" +msgstr "مسح الفلاتر" + +#: templates/jobs/job_candidates_list.html:210 +#: templates/recruitment/candidate_list.html:230 +msgid "All Stages" +msgstr "جميع المراحل" + +#: templates/jobs/job_candidates_list.html:226 +#: templates/meetings/meeting_details.html:303 +#: templates/participants/participants_list.html:212 +#: templates/recruitment/agency_assignment_detail.html:239 +#: templates/recruitment/agency_portal_assignment_detail.html:241 +#: templates/recruitment/candidate_exam_view.html:250 +#: templates/recruitment/candidate_hired_view.html:232 +#: templates/recruitment/candidate_interview_view.html:261 +#: templates/recruitment/candidate_list.html:269 +#: templates/recruitment/candidate_offer_view.html:246 +#: templates/recruitment/candidate_screening_view.html:364 +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:19 +msgid "Name" +msgstr "الاسم" + +#: templates/jobs/job_candidates_list.html:230 +#: templates/jobs/job_candidates_list.html:343 +#: templates/recruitment/candidate_detail.html:355 +msgid "Applied Date" +msgstr "تاريخ التقديم" + +#: templates/jobs/job_candidates_list.html:253 templates/jobs/job_list.html:303 +#: templates/jobs/job_list.html:365 templates/meetings/list_meetings.html:272 +#: templates/meetings/list_meetings.html:366 +#: templates/participants/participants_list.html:232 +#: templates/participants/participants_list.html:276 +#: templates/recruitment/agency_list.html:246 +#: templates/recruitment/agency_list.html:319 +#: templates/recruitment/candidate_list.html:325 +#: templates/recruitment/candidate_list.html:373 +#: templates/recruitment/candidate_update.html:104 +#: templates/recruitment/training_list.html:177 +#: templates/recruitment/training_list.html:218 +#: templates/recruitment/training_update.html:119 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/edit_inline/stacked.html:65 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:38 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_title.html:25 +msgid "View" +msgstr "عرض" + +#: templates/jobs/job_candidates_list.html:277 +#, fuzzy +#| msgid "Rejected" +msgid "Selected" +msgstr "تم التحديد" + +#: templates/jobs/job_candidates_list.html:280 +#, fuzzy +#| msgid "Interview" +msgid "Mark Interview" +msgstr "تحديد مقابلة" + +#: templates/jobs/job_candidates_list.html:284 +#, fuzzy +#| msgid "Offer" +msgid "Mark Offer" +msgstr "تحديد عرض" + +#: templates/jobs/job_candidates_list.html:349 +#, fuzzy +#| msgid "Profile" +msgid "View Profile" +msgstr "عرض الملف الشخصي" + +#: templates/jobs/job_candidates_list.html:376 +#, fuzzy +#| msgid "No applicants yet" +msgid "No applicants found" +msgstr "لم يتم العثور على متقدمين" + +#: templates/jobs/job_candidates_list.html:377 +#, fuzzy +#| msgid "Candidates will appear here once they apply for this position." +msgid "There are no candidates who have applied for this position yet." +msgstr "لا يوجد مرشحون تقدموا لهذا المنصب بعد." + +#: templates/jobs/job_candidates_list.html:379 +#, fuzzy +#| msgid "Applicants" +msgid "Add First Applicant" +msgstr "إضافة أول متقدم" + +#: templates/jobs/job_detail.html:171 +msgid "JOB ID: " +msgstr "معرف الوظيفة: " + +#: templates/jobs/job_detail.html:208 +msgid "Share Public Link" +msgstr "مشاركة الرابط العام" + +#: templates/jobs/job_detail.html:212 +#: templates/meetings/meeting_details.html:270 +#: templates/meetings/meeting_details.html:475 +msgid "Copied!" +msgstr "تم النسخ!" + +#: templates/jobs/job_detail.html:220 +msgid "Administrative & Location" +msgstr "الإدارية والموقع" + +#: templates/jobs/job_detail.html:221 +#, fuzzy +#| msgid "Edit Job" +msgid "Edit JOb" +msgstr "تعديل الوظيفة" + +#: templates/jobs/job_detail.html:228 +msgid "Position No:" +msgstr "رقم المنصب:" + +#: templates/jobs/job_detail.html:243 +msgid "Created By:" +msgstr "أنشأ بواسطة:" + +#: templates/jobs/job_detail.html:246 +#, fuzzy +#| msgid "Created at" +msgid "Created At:" +msgstr "تاريخ الإنشاء:" + +#: templates/jobs/job_detail.html:249 +#, fuzzy +#| msgid "Updated at" +msgid "Updated At:" +msgstr "تاريخ التحديث:" + +#: templates/jobs/job_detail.html:262 +msgid "Required Qualifications" +msgstr "المؤهلات المطلوبة" + +#: templates/jobs/job_detail.html:296 +msgid "Tracking" +msgstr "التتبع" + +#: templates/jobs/job_detail.html:301 +#, fuzzy +#| msgid "Form Templates" +msgid "Form Template" +msgstr "قالب النموذج" + +#: templates/jobs/job_detail.html:306 +#, fuzzy +#| msgid "Connect LinkedIn" +msgid "LinkedIn" +msgstr "LinkedIn" + +#: templates/jobs/job_detail.html:319 +#, fuzzy +#| msgid "Create Template" +msgid "Create Applicant" +msgstr "إنشاء متقدم" + +#: templates/jobs/job_detail.html:322 +#, fuzzy +#| msgid "Applicants" +msgid "Manage Applicants" +msgstr "إدارة المتقدمين" + +#: templates/jobs/job_detail.html:329 +#, fuzzy +#| msgid "Application Stage" +msgid "Applicant Stages" +msgstr "مراحل المتقدمين" + +#: templates/jobs/job_detail.html:332 +msgid "" +"The applicant tracking flow is defined by the attached Form Template. View " +"the Form Template tab to manage stages and fields." +msgstr "" +"يتم تحديد سير عمل تتبع المتقدمين بواسطة قالب النموذج المرفق. عرض علامة تبويب " +"قالب النموذج لإدارة المراحل والحقول." + +#: templates/jobs/job_detail.html:339 +msgid "Form Management" +msgstr "إدارة النماذج" + +#: templates/jobs/job_detail.html:342 +msgid "Manage the custom application forms associated with this job posting." +msgstr "إدارة نماذج الطلبات المخصصة المرتبطة بإعلان الوظيفة هذا." + +#: templates/jobs/job_detail.html:352 +#, fuzzy +#| msgid "Form Templates" +msgid "View Form Template" +msgstr "عرض قالب النموذج" + +#: templates/jobs/job_detail.html:355 +msgid "" +"This job status is not active, the form will appear once the job is made " +"active" +msgstr "حالة هذه الوظيفة غير نشطة، سيظهر النموذج بمجرد تفعيل الوظيفة" + +#: templates/jobs/job_detail.html:368 +msgid "LinkedIn Integration" +msgstr "تكامل LinkedIn" + +#: templates/jobs/job_detail.html:372 +msgid "Posted successfully!" +msgstr "تم النشر بنجاح!" + +#: templates/jobs/job_detail.html:376 +msgid "View on LinkedIn" +msgstr "عرض على LinkedIn" + +#: templates/jobs/job_detail.html:380 +msgid "Posted on:" +msgstr "تم النشر في:" + +#: templates/jobs/job_detail.html:383 +msgid "This job has not been posted to LinkedIn yet." +msgstr "لم يتم نشر هذه الوظيفة على LinkedIn بعد." + +#: templates/jobs/job_detail.html:391 +msgid "Re-post to LinkedIn" +msgstr "إعادة النشر على LinkedIn" + +#: templates/jobs/job_detail.html:391 +msgid "Post to LinkedIn" +msgstr "النشر على LinkedIn" + +#: templates/jobs/job_detail.html:396 +msgid "Upload Image for Post" +msgstr "رفع صورة للمنشور" + +#: templates/jobs/job_detail.html:401 +msgid "You need to" +msgstr "أنت بحاجة إلى" + +#: templates/jobs/job_detail.html:401 +msgid "authenticate with LinkedIn" +msgstr "المصادقة مع LinkedIn" + +#: templates/jobs/job_detail.html:401 +msgid "first." +msgstr "أولاً." + +#: templates/jobs/job_detail.html:408 +#: templates/recruitment/candidate_hired_view.html:466 +msgid "Error:" +msgstr "خطأ:" + +#: templates/jobs/job_detail.html:413 +#, fuzzy +#| msgid "LinkedIn Connected" +msgid "Update LinkedIn Content" +msgstr "تحديث محتوى LinkedIn" + +#: templates/jobs/job_detail.html:426 +msgid "Candidate Categories & Scores" +msgstr "فئات المرشحين ونقاطهم" + +#: templates/jobs/job_detail.html:441 +msgid "Key Performance Indicators" +msgstr "مؤشرات الأداء الرئيسية" + +#: templates/jobs/job_detail.html:454 +msgid "Avg. AI Score" +msgstr "متوسط نقاط الذكاء الاصطناعي" + +#: templates/jobs/job_detail.html:465 +#: templates/recruitment/partials/stats_cards.html:97 +msgid "High Potential" +msgstr "إمكانات عالية" + +#: templates/jobs/job_detail.html:476 +#, fuzzy +#| msgid "Interview" +msgid "Time to Interview" +msgstr "الوقت اللازم للمقابلة" + +#: templates/jobs/job_detail.html:487 +msgid "Avg. Exam Review" +msgstr "متوسط مراجعة الاختبار" + +#: templates/jobs/job_detail.html:497 +msgid "Vacancy Fill Rate" +msgstr "معدل شغل الشواغر" + +#: templates/jobs/job_detail.html:519 +#, fuzzy +#| msgid "Edit Job" +msgid "Edit Job Status" +msgstr "تعديل حالة الوظيفة" + +#: templates/jobs/job_detail.html:525 +#, fuzzy +#| msgid "Sync Status" +msgid "Select New Status" +msgstr "اختيار حالة جديدة" + +#: templates/jobs/job_detail.html:531 +msgid "Status form not available. Please check your view." +msgstr "نموذج الحالة غير متاح. يرجى التحقق من طريقة العرض الخاصة بك." + +#: templates/jobs/job_detail.html:537 +#: templates/meetings/meeting_details.html:396 +#: templates/recruitment/agency_portal_assignment_detail.html:578 +#: templates/user/profile.html:147 +msgid "Save Changes" +msgstr "حفظ التغييرات" + +#: templates/jobs/job_list.html:204 +msgid "Job Postings" +msgstr "الوظائف" + +#: templates/jobs/job_list.html:207 +msgid "Create New Job" +msgstr "إنشاء وظيفة جديدة" + +#: templates/jobs/job_list.html:216 +msgid "Search by Title or Department" +msgstr "البحث بالاسم أو القسم" + +#: templates/jobs/job_list.html:226 templates/meetings/list_meetings.html:200 +#, fuzzy +#| msgid "Offer Status" +msgid "Filter by Status" +msgstr "التصفية حسب الحالة" + +#: templates/jobs/job_list.html:228 templates/meetings/list_meetings.html:202 +#: templates/recruitment/agency_assignment_list.html:86 +msgid "All Statuses" +msgstr "جميع الحالات" + +#: templates/jobs/job_list.html:229 +msgid "Draft" +msgstr "مسودة" + +#: templates/jobs/job_list.html:231 +msgid "Closed" +msgstr "مغلقة" + +#: templates/jobs/job_list.html:232 +msgid "Archived" +msgstr "أرشيف" + +#: templates/jobs/job_list.html:238 templates/meetings/list_meetings.html:215 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/change_list_filter_actions.html:7 +msgid "Apply Filters" +msgstr "تطبيق الفلاتر" + +#: templates/jobs/job_list.html:242 templates/meetings/list_meetings.html:219 +#: templates/participants/participants_list.html:190 +#: templates/recruitment/candidate_list.html:247 +#: templates/recruitment/notification_list.html:60 +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:527 +msgid "Clear" +msgstr "مسح" + +#: templates/jobs/job_list.html:268 +msgid "Job Title / ID" +msgstr "المسمى الوظيفي / المعرف" + +#: templates/jobs/job_list.html:270 +msgid "Max Apps" +msgstr "الحد الأقصى للطلبات" + +#: templates/jobs/job_list.html:271 +#: templates/recruitment/agency_assignment_detail.html:144 +#: templates/recruitment/agency_assignment_list.html:115 +#: templates/recruitment/agency_portal_assignment_detail.html:158 +#: templates/recruitment/agency_portal_dashboard.html:162 +msgid "Deadline" +msgstr "الموعد النهائي" + +#: templates/jobs/job_list.html:273 +msgid "Manage Forms" +msgstr "إدارة النماذج" + +#: templates/jobs/job_list.html:276 +msgid "Applicants Metrics" +msgstr "مقاييس المتقدمين" + +#: templates/jobs/job_list.html:281 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:22 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:65 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:78 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/dropdown_filters.py:22 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/mixins.py:45 +msgid "All" +msgstr "الكل" + +#: templates/jobs/job_list.html:282 +#: templates/jobs/partials/applicant_tracking.html:112 +msgid "Screened" +msgstr "تم الفرز" + +#: templates/jobs/job_list.html:355 +#, fuzzy +#| msgid "Offer Date" +msgid "Offers Made" +msgstr "العروض المقدمة" + +#: templates/jobs/job_list.html:356 +msgid "Form" +msgstr "النموذج" + +#: templates/jobs/job_list.html:359 +msgid "N/A" +msgstr "غير متوفر" + +#: templates/jobs/job_list.html:366 +#, fuzzy +#| msgid "Job Details" +msgid "Details" +msgstr "التفاصيل" + +#: templates/jobs/job_list.html:369 +msgid "Edit Job" +msgstr "تعديل الوظيفة" + +#: templates/jobs/job_list.html:392 +#, fuzzy +#| msgid "No meetings found." +msgid "No job postings found" +msgstr "لم يتم العثور على إعلانات وظيفية" + +#: templates/jobs/job_list.html:393 +msgid "Create your first job posting to get started or adjust your filters." +msgstr "أنشئ أول إعلان وظيفي للبدء أو قم بضبط الفلاتر الخاصة بك." + +#: templates/meetings/create_meeting.html:4 +msgid "Create Zoom Meeting" +msgstr "إنشاء اجتماع Zoom" + +#: templates/meetings/create_meeting.html:151 +msgid "Create New Zoom Meeting" +msgstr "إنشاء اجتماع Zoom جديد" + +#: templates/meetings/create_meeting.html:155 +#: templates/meetings/meeting_details.html:210 +msgid "Back to Meetings" +msgstr "العودة إلى الاجتماعات" + +#: templates/meetings/delete_meeting_form.html:4 +#, fuzzy +#| msgid "Are you sure you want to delete this material?" +msgid "" +"Are you sure you want to delete this meeting? This action is irreversible." +msgstr "" +"هل أنت متأكد من رغبتك في حذف هذا الاجتماع؟ هذا الإجراء لا يمكن التراجع عنه." + +#: templates/meetings/delete_meeting_form.html:7 +#: templates/meetings/meeting_details.html:222 +msgid "Delete Meeting" +msgstr "حذف الاجتماع" + +#: templates/meetings/list_meetings.html:4 +#: templates/meetings/list_meetings.html:176 +msgid "Zoom Meetings" +msgstr "اجتماعات Zoom" + +#: templates/meetings/list_meetings.html:187 +msgid "Search by Topic" +msgstr "البحث حسب الموضوع" + +#: templates/meetings/list_meetings.html:209 +#: templates/meetings/meeting_details.html:252 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidate Name" +msgstr "اسم المرشح" + +#: templates/meetings/list_meetings.html:210 +#, fuzzy +#| msgid "Search templates by name..." +msgid "Search by candidate..." +msgstr "البحث حسب المرشح..." + +#: templates/meetings/list_meetings.html:264 +#: templates/meetings/list_meetings.html:309 +#: templates/user/admin_settings.html:173 +msgid "ID" +msgstr "المعرف" + +#: templates/meetings/list_meetings.html:265 +#, fuzzy +#| msgid "Started" +msgid "Start" +msgstr "بدء" + +#: templates/meetings/list_meetings.html:277 +#: templates/meetings/list_meetings.html:362 +#, fuzzy +#| msgid "Join URL" +msgid "Join" +msgstr "انضمام" + +#: templates/meetings/list_meetings.html:423 +#, fuzzy +#| msgid "No meetings found." +msgid "No Zoom meetings found" +msgstr "لم يتم العثور على اجتماعات Zoom" + +#: templates/meetings/list_meetings.html:424 +msgid "Create your first meeting or adjust your filters." +msgstr "أنشئ أول اجتماع لك أو قم بضبط الفلاتر الخاصة بك." + +#: templates/meetings/list_meetings.html:426 +msgid "Create Your First Meeting" +msgstr "إنشاء أول اجتماع لك" + +#: templates/meetings/meeting_details.html:216 +#, fuzzy +#| msgid "Update Meeting" +msgid "Edit Meeting" +msgstr "تعديل الاجتماع" + +#: templates/meetings/meeting_details.html:221 +#, fuzzy +#| msgid "Are you sure you want to delete this material?" +msgid "Are you sure you want to delete this meeting? This action is permanent." +msgstr "هل أنت متأكد من رغبتك في حذف هذا الاجتماع؟ هذا الإجراء نهائي." + +#: templates/meetings/meeting_details.html:249 +#, fuzzy +#| msgid "Interview Date" +msgid "Interview Detail" +msgstr "تفاصيل المقابلة" + +#: templates/meetings/meeting_details.html:253 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidate Email" +msgstr "البريد الإلكتروني للمرشح" + +#: templates/meetings/meeting_details.html:262 +#, fuzzy +#| msgid "Core Details" +msgid "Connection Details" +msgstr "تفاصيل الاتصال" + +#: templates/meetings/meeting_details.html:264 +#, fuzzy +#| msgid "Start Time" +msgid "Date & Time" +msgstr "التاريخ والوقت" + +#: templates/meetings/meeting_details.html:265 +#: templates/recruitment/notification_detail.html:71 +msgid "minutes" +msgstr "دقيقة" + +#: templates/meetings/meeting_details.html:267 +msgid "Host Email" +msgstr "بريد المضيف الإلكتروني" + +#: templates/meetings/meeting_details.html:277 +#, fuzzy +#| msgid "Join URL" +msgid "Copy URL" +msgstr "نسخ الرابط" + +#: templates/meetings/meeting_details.html:298 +msgid "Assigned Participants" +msgstr "المشاركون المعينون" + +#: templates/meetings/meeting_details.html:304 +#, fuzzy +#| msgid "Toggle navigation" +msgid "Role/Designation" +msgstr "الدور/المنصب" + +#: templates/meetings/meeting_details.html:317 +msgid "External Participants" +msgstr "المشاركون الخارجيون" + +#: templates/meetings/meeting_details.html:326 +msgid "System User" +msgstr "مستخدم النظام" + +#: templates/meetings/meeting_details.html:346 +msgid "Comments" +msgstr "التعليقات" + +#: templates/meetings/meeting_details.html:369 +#: templates/meetings/meeting_details.html:391 +#, fuzzy +#| msgid "Edit Job" +msgid "Edit Comment" +msgstr "تعديل التعليق" + +#: templates/meetings/meeting_details.html:376 +#, fuzzy +#| msgid "Delete Candidate" +msgid "Delete Comment" +msgstr "حذف التعليق" + +#: templates/meetings/meeting_details.html:376 +#, fuzzy +#| msgid "Are you sure you want to delete this item?" +msgid "Are you sure you want to delete this comment?" +msgstr "هل أنت متأكد من رغبتك في حذف هذا التعليق؟" + +#: templates/meetings/meeting_details.html:407 +msgid "No comments yet. Be the first to comment!" +msgstr "لا توجد تعليقات بعد. كن أول من يعلق!" + +#: templates/meetings/meeting_details.html:414 +#, fuzzy +#| msgid "Add New Candidate" +msgid "Add a New Comment" +msgstr "إضافة تعليق جديد" + +#: templates/meetings/meeting_details.html:427 +#, fuzzy +#| msgid "Submit" +msgid "Submit Comment" +msgstr "إرسال التعليق" + +#: templates/meetings/meeting_details.html:431 +msgid "You must be logged in to add a comment." +msgstr "يجب عليك تسجيل الدخول لإضافة تعليق." + +#: templates/meetings/meeting_details.html:475 +#, fuzzy +#| msgid "Failed" +msgid "Copy Failed." +msgstr "فشل النسخ." + +#: templates/meetings/reschedule_meeting.html:9 +#: templates/meetings/schedule_meeting_form.html:7 +#: templates/recruitment/schedule_meeting_form.html:13 +#, fuzzy +#| msgid "Interview" +msgid "Update Interview" +msgstr "تحديث المقابلة" + +#: templates/meetings/reschedule_meeting.html:15 +msgid "You are updating the existing meeting schedule." +msgstr "أنت تقوم بتحديث جدول الاجتماع الحالي." + +#: templates/meetings/reschedule_meeting.html:27 +#: templates/meetings/schedule_meeting_form.html:27 +#: templates/recruitment/schedule_meeting_form.html:38 +#, fuzzy +#| msgid "Meeting ID" +msgid "Meeting Topic" +msgstr "موضوع الاجتماع" + +#: templates/meetings/reschedule_meeting.html:65 +#: templates/meetings/schedule_meeting_form.html:82 +#: templates/meetings/update_meeting.html:233 +msgid "Update Meeting" +msgstr "تحديث الاجتماع" + +#: templates/meetings/schedule_meeting_form.html:16 +msgid "Candidate has upcoming interviews. Updating existing schedule." +msgstr "لدى المرشح مقابلات قادمة. يتم تحديث الجدول الزمني الحالي." + +#: templates/meetings/schedule_meeting_form.html:38 +msgid "e.g., Technical Screening, HR Interview" +msgstr "على سبيل المثال: الفرز الفني، مقابلة الموارد البشرية" + +#: templates/meetings/set_candidate_form.html:5 +#: templates/recruitment/candidate_interview_view.html:509 +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/change_list.html:41 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html:23 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html:24 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/pagination.html:19 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:9 +msgid "Save" +msgstr "حفظ" + +#: templates/meetings/update_meeting.html:4 +#: templates/meetings/update_meeting.html:196 +msgid "Update Zoom Meeting" +msgstr "تحديث اجتماع Zoom" + +#: templates/meetings/update_meeting.html:198 +msgid "Modify the details of your scheduled meeting" +msgstr "تعديل تفاصيل اجتماعك المجدول" + +#: templates/meetings/update_meeting.html:207 +#, fuzzy +#| msgid "Back to Meetings" +msgid "Back to Details" +msgstr "العودة إلى التفاصيل" + +#: templates/participants/participants_create.html:94 +#, fuzzy +#| msgid "Create New Candidate" +msgid "Create New Participant" +msgstr "إنشاء مشارك جديد" + +#: templates/participants/participants_create.html:96 +#, fuzzy +#| msgid "Enter details to create a new candidate record." +msgid "Enter details to create a new participant record." +msgstr "أدخل التفاصيل لإنشاء سجل مشارك جديد." + +#: templates/participants/participants_create.html:99 +#: templates/participants/participants_create.html:101 +#: templates/participants/participants_detail.html:135 +#: templates/participants/participants_detail.html:136 +#: templates/recruitment/candidate_create.html:99 +#: templates/recruitment/candidate_create.html:101 +#: templates/recruitment/candidate_detail.html:631 +#: templates/recruitment/candidate_update.html:97 +#: templates/recruitment/candidate_update.html:99 +#: templates/recruitment/training_create.html:112 +#: templates/recruitment/training_create.html:114 +#: templates/recruitment/training_update.html:112 +#: templates/recruitment/training_update.html:114 +msgid "Back to List" +msgstr "العودة إلى القائمة" + +#: templates/participants/participants_create.html:112 +#, fuzzy +#| msgid "Basic Information" +msgid "Participant Information" +msgstr "معلومات المشارك" + +#: templates/participants/participants_create.html:131 +#, fuzzy +#| msgid "Participant Video" +msgid "Save Participant" +msgstr "حفظ المشارك" + +#: templates/participants/participants_detail.html:131 +#, fuzzy +#| msgid "Participant Video" +msgid "Participant Details" +msgstr "تفاصيل المشارك" + +#: templates/participants/participants_detail.html:139 +#, fuzzy +#| msgid "Participant Video" +msgid "Edit Participant" +msgstr "تعديل المشارك" + +#: templates/participants/participants_detail.html:140 +#, fuzzy +#| msgid "Profile" +msgid "Edit Profile" +msgstr "تعديل الملف الشخصي" + +#: templates/participants/participants_detail.html:161 +#, fuzzy +#| msgid "Candidate Information" +msgid "Contact & Role Information" +msgstr "معلومات الاتصال والدور" + +#: templates/participants/participants_detail.html:166 +#: templates/user/admin_settings.html:174 +#, fuzzy +#| msgid "First Name" +msgid "Full Name" +msgstr "الاسم الكامل" + +#: templates/participants/participants_detail.html:192 +#, fuzzy +#| msgid "Active Jobs" +msgid "Assigned Jobs" +msgstr "الوظائف المعينة" + +#: templates/participants/participants_detail.html:199 +msgid "This participant is not currently assigned to any job." +msgstr "هذا المشارك غير معين لأي وظيفة حاليًا." + +#: templates/participants/participants_detail.html:210 +msgid "Metadata" +msgstr "بيانات وصفية" + +#: templates/participants/participants_detail.html:213 +#, fuzzy +#| msgid "Created" +msgid "Record Created" +msgstr "تم إنشاء السجل" + +#: templates/participants/participants_detail.html:214 +#: templates/participants/participants_detail.html:219 +msgid "at" +msgstr "في" + +#: templates/participants/participants_detail.html:225 +msgid "Total Assigned Jobs" +msgstr "إجمالي الوظائف المعينة" + +#: templates/participants/participants_detail.html:240 +#: templates/participants/participants_list.html:322 +#, fuzzy +#| msgid "Confirm Delete" +msgid "Confirm Deletion" +msgstr "تأكيد الحذف" + +#: templates/participants/participants_detail.html:244 +#: templates/participants/participants_list.html:326 +#, fuzzy +#| msgid "Are you sure you want to delete this item?" +msgid "Are you sure you want to delete" +msgstr "هل أنت متأكد من رغبتك في حذف" + +#: templates/participants/participants_detail.html:248 +#: templates/participants/participants_list.html:330 +msgid "This action cannot be undone." +msgstr "لا يمكن التراجع عن هذا الإجراء." + +#: templates/participants/participants_list.html:143 +msgid "Participants List" +msgstr "قائمة المشاركين" + +#: templates/participants/participants_list.html:147 +msgid "Add New Participant" +msgstr "إضافة مشارك جديد" + +#: templates/participants/participants_list.html:156 +#: templates/recruitment/candidate_list.html:206 +msgid "Search by Name or Email" +msgstr "البحث بالاسم أو البريد الإلكتروني" + +#: templates/participants/participants_list.html:172 +msgid "Filter by Assigned Job" +msgstr "التصفية حسب الوظيفة المعينة" + +#: templates/participants/participants_list.html:174 +#: templates/recruitment/candidate_list.html:224 +#: templates/recruitment/dashboard.html:395 +msgid "All Jobs" +msgstr "جميع الوظائف" + +#: templates/participants/participants_list.html:304 +msgid "No participants found" +msgstr "لم يتم العثور على مشاركين" + +#: templates/participants/participants_list.html:305 +msgid "Create your first participant record or adjust your filters." +msgstr "أنشئ أول سجل مشارك لك أو قم بضبط الفلاتر الخاصة بك." + +#: templates/participants/participants_list.html:308 +msgid "Add Participant" +msgstr "إضافة مشارك" + +#: templates/recruitment/agency_access_link_detail.html:4 +#: templates/recruitment/agency_access_link_detail.html:12 +msgid "Access Link Details" +msgstr "تفاصيل رابط الوصول" + +#: templates/recruitment/agency_access_link_detail.html:14 +msgid "Secure access link for agency candidate submissions" +msgstr "رابط وصول آمن لتقديم المرشحين من قبل الوكالة" + +#: templates/recruitment/agency_access_link_detail.html:17 +#: templates/recruitment/agency_portal_submit_candidate.html:122 +#: templates/recruitment/agency_portal_submit_candidate.html:365 +msgid "Back to Assignment" +msgstr "العودة إلى التعيين" + +#: templates/recruitment/agency_access_link_detail.html:28 +msgid "Access Information" +msgstr "معلومات الوصول" + +#: templates/recruitment/agency_access_link_detail.html:31 +#: templates/user/admin_settings.html:194 +msgid "Inactive" +msgstr "غير نشط" + +#: templates/recruitment/agency_access_link_detail.html:66 +msgid "Max Candidates" +msgstr "الحد الأقصى للمرشحين" + +#: templates/recruitment/agency_access_link_detail.html:83 +#: templates/recruitment/agency_assignment_detail.html:172 +msgid "Access Credentials" +msgstr "وثائق الوصول" + +#: templates/recruitment/agency_access_link_detail.html:87 +#: templates/recruitment/agency_assignment_detail.html:176 +msgid "Login URL" +msgstr "رابط تسجيل الدخول" + +#: templates/recruitment/agency_access_link_detail.html:121 +#: templates/recruitment/agency_assignment_detail.html:210 +msgid "" +"Share these credentials securely with the agency. They can use this " +"information to log in and submit candidates." +msgstr "" +"شارك وثائق الاعتماد هذه بأمان مع الوكالة. يمكنهم استخدام هذه المعلومات " +"لتسجيل الدخول وتقديم المرشحين." + +#: templates/recruitment/agency_access_link_detail.html:132 +msgid "Usage Statistics" +msgstr "إحصائيات الاستخدام" + +#: templates/recruitment/agency_access_link_detail.html:137 +msgid "Total Accesses" +msgstr "إجمالي مرات الوصول" + +#: templates/recruitment/agency_access_link_detail.html:146 +msgid "Never" +msgstr "أبدًا" + +#: templates/recruitment/agency_access_link_detail.html:173 +msgid "View Assignment" +msgstr "عرض التعيين" + +#: templates/recruitment/agency_access_link_detail.html:178 +#: templates/user/admin_settings.html:232 +#, fuzzy +#| msgid "Active" +msgid "Deactivate" +msgstr "تعطيل" + +#: templates/recruitment/agency_access_link_detail.html:184 +#, fuzzy +#| msgid "Active" +msgid "Reactivate" +msgstr "إعادة التنشيط" + +#: templates/recruitment/agency_access_link_detail.html:217 +#: templates/recruitment/agency_assignment_detail.html:489 +msgid "" +"Are you sure you want to deactivate this access link? Agencies will no " +"longer be able to use it." +msgstr "" +"هل أنت متأكد من رغبتك في تعطيل رابط الوصول هذا؟ لن تتمكن الوكالات من " +"استخدامه بعد الآن." + +#: templates/recruitment/agency_access_link_detail.html:224 +#: templates/recruitment/agency_assignment_detail.html:496 +#, fuzzy +#| msgid "Are you sure you want to delete this material?" +msgid "Are you sure you want to reactivate this access link?" +msgstr "هل أنت متأكد من رغبتك في إعادة تنشيط رابط الوصول هذا؟" + +#: templates/recruitment/agency_access_link_form.html:14 +msgid "Generate a secure access link for agency to submit candidates" +msgstr "إنشاء رابط وصول آمن للوكالة لتقديم المرشحين" + +#: templates/recruitment/agency_access_link_form.html:17 +#: templates/recruitment/agency_assignment_detail.html:103 +#: templates/recruitment/agency_assignment_form.html:114 +#, fuzzy +#| msgid "Back to List" +msgid "Back to Assignments" +msgstr "العودة إلى التعيينات" + +#: templates/recruitment/agency_access_link_form.html:47 +msgid "Select the agency job assignment" +msgstr "اختر تعيين الوظيفة للوكالة" + +#: templates/recruitment/agency_access_link_form.html:62 +msgid "When will this access link expire?" +msgstr "متى سينتهي صلاحية رابط الوصول هذا؟" + +#: templates/recruitment/agency_access_link_form.html:69 +msgid "Max Submissions" +msgstr "الحد الأقصى لمرات التقديم" + +#: templates/recruitment/agency_access_link_form.html:79 +msgid "" +"Maximum number of candidates agency can submit (leave blank for unlimited)" +msgstr "" +"الحد الأقصى لعدد المرشحين الذين يمكن للوكالة تقديمهم (اتركه فارغًا لعدد غير " +"محدود)" + +#: templates/recruitment/agency_access_link_form.html:99 +msgid "Whether this access link is currently active" +msgstr "ما إذا كان رابط الوصول هذا نشطًا حاليًا" + +#: templates/recruitment/agency_access_link_form.html:105 +msgid "Notes" +msgstr "ملاحظات" + +#: templates/recruitment/agency_access_link_form.html:115 +#, fuzzy +#| msgid "Internal notes about the agency" +msgid "Additional notes or instructions for the agency" +msgstr "ملاحظات أو تعليمات إضافية للوكالة" + +#: templates/recruitment/agency_access_link_form.html:122 +msgid "" +"Access links will be generated with a secure token that agencies can use to " +"log in" +msgstr "" +"سيتم إنشاء روابط الوصول باستخدام رمز آمن يمكن للوكالات استخدامه لتسجيل الدخول" + +#: templates/recruitment/agency_assignment_detail.html:98 +msgid "Assignment Details and Management" +msgstr "تفاصيل التعيين والإدارة" + +#: templates/recruitment/agency_assignment_detail.html:106 +#: templates/recruitment/agency_assignment_detail.html:357 +msgid "Edit Assignment" +msgstr "تعديل التعيين" + +#: templates/recruitment/agency_assignment_detail.html:118 +#: templates/recruitment/agency_portal_assignment_detail.html:110 +#: templates/recruitment/agency_portal_assignment_detail.html:137 +#: templates/recruitment/agency_portal_submit_candidate.html:133 +#, fuzzy +#| msgid "Edit Details" +msgid "Assignment Details" +msgstr "تفاصيل التعيين" + +#: templates/recruitment/agency_assignment_detail.html:215 +#, fuzzy +#| msgid "Meeting Details" +msgid "View Access Links Details" +msgstr "عرض تفاصيل روابط الوصول" + +#: templates/recruitment/agency_assignment_detail.html:225 +#: templates/recruitment/agency_portal_assignment_detail.html:231 +#, fuzzy +#| msgid "New Candidates" +msgid "Submitted Candidates" +msgstr "المرشحون المقدمون" + +#: templates/recruitment/agency_assignment_detail.html:229 +#, fuzzy +#| msgid "Preview" +msgid "Preview Portal" +msgstr "معاينة البوابة" + +#: templates/recruitment/agency_assignment_detail.html:240 +#: templates/recruitment/agency_portal_assignment_detail.html:242 +#: templates/recruitment/candidate_hired_view.html:233 +#: templates/recruitment/candidate_interview_view.html:262 +#: templates/recruitment/candidate_offer_view.html:247 +#, fuzzy +#| msgid "Content" +msgid "Contact" +msgstr "جهة الاتصال" + +#: templates/recruitment/agency_assignment_detail.html:242 +#: templates/recruitment/agency_portal_assignment_detail.html:244 +#, fuzzy +#| msgid "Submit" +msgid "Submitted" +msgstr "تم التقديم" + +#: templates/recruitment/agency_assignment_detail.html:280 +#: templates/recruitment/agency_portal_assignment_detail.html:319 +#, fuzzy +#| msgid "No candidates found." +msgid "No candidates submitted yet" +msgstr "لم يتم تقديم أي مرشحين بعد" + +#: templates/recruitment/agency_assignment_detail.html:282 +#, fuzzy +#| msgid "Candidates will appear here once they apply for this position." +msgid "" +"Candidates will appear here once the agency submits them through their " +"portal." +msgstr "سيظهر المرشحون هنا بمجرد أن تقدمهم الوكالة من خلال بوابتها." + +#: templates/recruitment/agency_assignment_detail.html:294 +#: templates/recruitment/agency_portal_assignment_detail.html:333 +#: templates/recruitment/agency_portal_dashboard.html:181 +msgid "Submission Progress" +msgstr "تقدم التقديم" + +#: templates/recruitment/agency_assignment_detail.html:325 +#: templates/recruitment/agency_portal_assignment_detail.html:175 +#: templates/recruitment/agency_portal_assignment_detail.html:364 +#, fuzzy +#| msgid "Candidates" +msgid "candidates" +msgstr "مرشحين" + +#: templates/recruitment/agency_assignment_detail.html:345 +#: templates/recruitment/agency_detail.html:492 +#: templates/recruitment/agency_portal_assignment_detail.html:513 +#, fuzzy +#| msgid "Error Message" +msgid "Send Message" +msgstr "إرسال رسالة" + +#: templates/recruitment/agency_assignment_detail.html:351 +#: templates/recruitment/agency_assignment_detail.html:433 +#, fuzzy +#| msgid "Deadline:" +msgid "Extend Deadline" +msgstr "تمديد الموعد النهائي" + +#: templates/recruitment/agency_assignment_detail.html:369 +#: templates/recruitment/agency_portal_assignment_detail.html:437 +msgid "Recent Messages" +msgstr "الرسائل الأخيرة" + +#: templates/recruitment/agency_assignment_detail.html:381 +#: templates/recruitment/agency_portal_assignment_detail.html:449 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:186 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:224 +msgid "From" +msgstr "من" + +#: templates/recruitment/agency_assignment_detail.html:385 +#: templates/recruitment/agency_portal_assignment_detail.html:453 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_preview.html:22 +msgid "New" +msgstr "جديد" + +#: templates/recruitment/agency_assignment_detail.html:395 +#: templates/recruitment/agency_portal_assignment_detail.html:463 +#, fuzzy +#| msgid "View API Response" +msgid "View All Messages" +msgstr "عرض جميع الرسائل" + +#: templates/recruitment/agency_assignment_detail.html:410 +msgid "Extend Assignment Deadline" +msgstr "تمديد الموعد النهائي للتعيين" + +#: templates/recruitment/agency_assignment_detail.html:419 +#, fuzzy +#| msgid "Deadline:" +msgid "New Deadline" +msgstr "موعد نهائي جديد" + +#: templates/recruitment/agency_assignment_detail.html:424 +#, fuzzy +#| msgid "Deadline:" +msgid "Current deadline:" +msgstr "الموعد النهائي الحالي:" + +#: templates/recruitment/agency_assignment_detail.html:457 +msgid "Token copied to clipboard!" +msgstr "تم نسخ الرمز المميز إلى الحافظة!" + +#: templates/recruitment/agency_assignment_form.html:110 +msgid "Assign a job to an external hiring agency" +msgstr "تعيين وظيفة لوكالة توظيف خارجية" + +#: templates/recruitment/agency_assignment_form.html:170 +msgid "Maximum number of candidates the agency can submit" +msgstr "الحد الأقصى لعدد المرشحين الذين يمكن للوكالة تقديمهم" + +#: templates/recruitment/agency_assignment_form.html:187 +msgid "Date and time when submission period ends" +msgstr "التاريخ والوقت الذي تنتهي فيه فترة التقديم" + +#: templates/recruitment/agency_assignment_form.html:207 +#, fuzzy +#| msgid "Internal notes about the agency" +msgid "Internal notes about this assignment (not visible to agency)" +msgstr "ملاحظات داخلية حول هذا التعيين (غير مرئية للوكالة)" + +#: templates/recruitment/agency_assignment_list.html:4 +#: templates/recruitment/agency_assignment_list.html:59 +msgid "Agency Assignments" +msgstr "تعيينات الوكالة" + +#: templates/recruitment/agency_assignment_list.html:62 +msgid "Total Assignments:" +msgstr "إجمالي التعيينات:" + +#: templates/recruitment/agency_assignment_list.html:67 +msgid "New Assignment" +msgstr "تعيين جديد" + +#: templates/recruitment/agency_assignment_list.html:79 +msgid "Search by agency or job title..." +msgstr "البحث حسب الوكالة أو المسمى الوظيفي..." + +#: templates/recruitment/agency_assignment_list.html:169 +#, fuzzy +#| msgid "View on LinkedIn" +msgid "View Access Link" +msgstr "عرض رابط الوصول" + +#: templates/recruitment/agency_assignment_list.html:183 +msgid "Assignments pagination" +msgstr "ترقيم صفحات التعيينات" + +#: templates/recruitment/agency_assignment_list.html:228 +#, fuzzy +#| msgid "No meetings found." +msgid "No assignments found" +msgstr "لم يتم العثور على تعيينات" + +#: templates/recruitment/agency_assignment_list.html:229 +msgid "Create your first agency assignment to get started." +msgstr "أنشئ أول تعيين وكالة لك للبدء." + +#: templates/recruitment/agency_assignment_list.html:231 +msgid "Create Assignment" +msgstr "إنشاء تعيين" + +#: templates/recruitment/agency_confirm_delete.html:4 +#: templates/recruitment/agency_confirm_delete.html:179 +#, fuzzy +#| msgid "Delete Meeting" +msgid "Delete Agency" +msgstr "حذف الوكالة" + +#: templates/recruitment/agency_confirm_delete.html:182 +msgid "You are about to delete a hiring agency. This action cannot be undone." +msgstr "أنت على وشك حذف وكالة توظيف. لا يمكن التراجع عن هذا الإجراء." + +#: templates/recruitment/agency_confirm_delete.html:186 +#, fuzzy +#| msgid "Back to Meetings" +msgid "Back to Agency" +msgstr "العودة إلى الوكالة" + +#: templates/recruitment/agency_confirm_delete.html:197 +msgid "Warning: This action cannot be undone!" +msgstr "تحذير: لا يمكن التراجع عن هذا الإجراء!" + +#: templates/recruitment/agency_confirm_delete.html:199 +msgid "" +"Deleting this agency will permanently remove all associated data. Please " +"review the information below carefully before proceeding." +msgstr "" +"سيؤدي حذف هذه الوكالة إلى إزالة جميع البيانات المرتبطة بها بشكل دائم. يرجى " +"مراجعة المعلومات أدناه بعناية قبل المتابعة." + +#: templates/recruitment/agency_confirm_delete.html:208 +msgid "Agency to be Deleted" +msgstr "الوكالة المراد حذفها" + +#: templates/recruitment/agency_confirm_delete.html:277 +#, fuzzy +#| msgid "No candidates found." +msgid "Associated Candidates Found" +msgstr "تم العثور على مرشحين مرتبطين" + +#: templates/recruitment/agency_confirm_delete.html:280 +msgid "candidate(s) are associated with this agency." +msgstr "مرشح(ون) مرتبط(ون) بهذه الوكالة." + +#: templates/recruitment/agency_confirm_delete.html:283 +msgid "" +"Deleting this agency will affect these candidates. Their agency reference " +"will be removed, but the candidates themselves will not be deleted." +msgstr "" +"سيؤثر حذف هذه الوكالة على هؤلاء المرشحين. سيتم إزالة مرجع وكالتهم، لكن لن " +"يتم حذف المرشحين أنفسهم." + +#: templates/recruitment/agency_confirm_delete.html:293 +msgid "What will happen when you delete this agency?" +msgstr "ماذا سيحدث عند حذف هذه الوكالة؟" + +#: templates/recruitment/agency_confirm_delete.html:300 +msgid "The agency profile and all its information will be permanently deleted" +msgstr "سيتم حذف ملف الوكالة وجميع معلوماتها بشكل دائم" + +#: templates/recruitment/agency_confirm_delete.html:304 +#, fuzzy +#| msgid "Edit candidate information and details" +msgid "All contact information and agency details will be removed" +msgstr "ستتم إزالة جميع معلومات الاتصال وتفاصيل الوكالة" + +#: templates/recruitment/agency_confirm_delete.html:309 +msgid "Associated candidates will lose their agency reference" +msgstr "المرشحون المرتبطون سيفقدون مرجع وكالتهم" + +#: templates/recruitment/agency_confirm_delete.html:313 +msgid "Historical data linking candidates to this agency will be lost" +msgstr "ستُفقد البيانات التاريخية التي تربط المرشحين بهذه الوكالة" + +#: templates/recruitment/agency_confirm_delete.html:318 +msgid "This action cannot be undone under any circumstances" +msgstr "لا يمكن التراجع عن هذا الإجراء تحت أي ظرف من الظروف" + +#: templates/recruitment/agency_confirm_delete.html:332 +msgid "Type the agency name to confirm deletion:" +msgstr "اكتب اسم الوكالة لتأكيد الحذف:" + +#: templates/recruitment/agency_confirm_delete.html:341 +msgid "This is required to prevent accidental deletions." +msgstr "هذا مطلوب لمنع عمليات الحذف العرضية." + +#: templates/recruitment/agency_confirm_delete.html:349 +msgid "" +"I understand that this action cannot be undone and I want to permanently " +"delete this agency." +msgstr "" +"أتفهم أنه لا يمكن التراجع عن هذا الإجراء وأريد حذف هذه الوكالة بشكل دائم." + +#: templates/recruitment/agency_confirm_delete.html:364 +msgid "Delete Agency Permanently" +msgstr "حذف الوكالة بشكل دائم" + +#: templates/recruitment/agency_confirm_delete.html:402 +#, fuzzy +#| msgid "Are you sure you want to delete this candidate?" +msgid "" +"Are you absolutely sure you want to delete this agency? This action cannot " +"be undone." +msgstr "" +"هل أنت متأكد تمامًا من رغبتك في حذف هذه الوكالة؟ لا يمكن التراجع عن هذا " +"الإجراء." + +#: templates/recruitment/agency_detail.html:4 +#, fuzzy +#| msgid "Core Details" +msgid "Agency Details" +msgstr "تفاصيل الوكالة" + +#: templates/recruitment/agency_detail.html:220 +msgid "Hiring Agency Details and Candidate Management" +msgstr "تفاصيل وكالة التوظيف وإدارة المرشحين" + +#: templates/recruitment/agency_detail.html:225 +msgid "Assign job" +msgstr "تعيين وظيفة" + +#: templates/recruitment/agency_detail.html:228 +#: templates/recruitment/agency_detail.html:486 +#, fuzzy +#| msgid "Hiring Agency" +msgid "Edit Agency" +msgstr "تعديل الوكالة" + +#: templates/recruitment/agency_detail.html:231 +#: templates/recruitment/agency_form.html:131 +#, fuzzy +#| msgid "Back to List" +msgid "Back to Agencies" +msgstr "العودة إلى الوكالات" + +#: templates/recruitment/agency_detail.html:248 +#: templates/recruitment/agency_list.html:285 +#, fuzzy +#| msgid "Contact & Job" +msgid "Contact:" +msgstr "جهة الاتصال:" + +#: templates/recruitment/agency_detail.html:280 +#: templates/recruitment/agency_form.html:216 +#: templates/recruitment/agency_portal_submit_candidate.html:214 +msgid "Contact Information" +msgstr "معلومات الاتصال" + +#: templates/recruitment/agency_detail.html:330 +#: templates/recruitment/agency_form.html:273 +msgid "Location Information" +msgstr "معلومات الموقع" + +#: templates/recruitment/agency_detail.html:391 +#, fuzzy +#| msgid "Delete Candidate" +msgid "Recent Candidates" +msgstr "المرشحون الجدد" + +#: templates/recruitment/agency_detail.html:394 +#: templates/recruitment/agency_detail.html:489 +#, fuzzy +#| msgid "All Candidates" +msgid "View All Candidates" +msgstr "عرض جميع المرشحين" + +#: templates/recruitment/agency_detail.html:427 +#, fuzzy +#| msgid "No candidates found." +msgid "No candidates yet" +msgstr "لا يوجد مرشحون بعد" + +#: templates/recruitment/agency_detail.html:428 +msgid "This agency hasn't submitted any candidates yet." +msgstr "لم تقدم هذه الوكالة أي مرشحين بعد." + +#: templates/recruitment/agency_detail.html:442 +#, fuzzy +#| msgid "Candidates" +msgid "Candidate Statistics" +msgstr "إحصائيات المرشحين" + +#: templates/recruitment/agency_detail.html:450 +msgid "Total" +msgstr "الإجمالي" + +#: templates/recruitment/agency_detail.html:480 +#, fuzzy +#| msgid "Actions" +msgid "Quick Actions" +msgstr "الإجراءات السريعة" + +#: templates/recruitment/agency_detail.html:496 +msgid "Visit Website" +msgstr "زيارة الموقع الإلكتروني" + +#: templates/recruitment/agency_detail.html:513 +#: templates/recruitment/agency_form.html:390 +#, fuzzy +#| msgid "Join Information" +msgid "Agency Information" +msgstr "معلومات الوكالة" + +#: templates/recruitment/agency_detail.html:522 +#: templates/recruitment/agency_form.html:397 +msgid "Last Updated:" +msgstr "آخر تحديث:" + +#: templates/recruitment/agency_detail.html:526 +#, fuzzy +#| msgid "Agency Name" +msgid "Agency ID:" +msgstr "معرف الوكالة:" + +#: templates/recruitment/agency_form.html:124 +msgid "Update the hiring agency information below." +msgstr "قم بتحديث معلومات وكالة التوظيف أدناه." + +#: templates/recruitment/agency_form.html:126 +msgid "Fill in the details to add a new hiring agency." +msgstr "املأ التفاصيل لإضافة وكالة توظيف جديدة." + +#: templates/recruitment/agency_form.html:144 +msgid "Please correct the errors below:" +msgstr "يرجى تصحيح الأخطاء أدناه:" + +#: templates/recruitment/agency_form.html:159 +msgid "Basic Information" +msgstr "المعلومات الأساسية" + +#: templates/recruitment/agency_form.html:313 +msgid "Additional Information" +msgstr "معلومات إضافية" + +#: templates/recruitment/agency_form.html:362 +msgid "Quick Tips" +msgstr "نصائح سريعة" + +#: templates/recruitment/agency_form.html:367 +msgid "Provide accurate contact information for better communication" +msgstr "قدم معلومات اتصال دقيقة لتواصل أفضل" + +#: templates/recruitment/agency_form.html:371 +msgid "Include a valid website URL if available" +msgstr "أدرج رابط موقع إلكتروني صالح إذا كان متاحًا" + +#: templates/recruitment/agency_form.html:375 +msgid "Add a detailed description to help identify the agency" +msgstr "أضف وصفًا مفصلاً للمساعدة في تحديد الوكالة" + +#: templates/recruitment/agency_form.html:379 +msgid "All fields marked with * are required" +msgstr "جميع الحقول المميزة بعلامة * مطلوبة" + +#: templates/recruitment/agency_form.html:401 +#, fuzzy +#| msgid "Slug" +msgid "Slug:" +msgstr "الرابط المختصر:" + +#: templates/recruitment/agency_list.html:134 +#, fuzzy +#| msgid "Hiring Agencies" +msgid "Total Agencies:" +msgstr "إجمالي الوكالات:" + +#: templates/recruitment/agency_list.html:141 +msgid "View All Job Assignments" +msgstr "عرض جميع تعيينات الوظائف" + +#: templates/recruitment/agency_list.html:145 +msgid "Add New Agency" +msgstr "إضافة وكالة جديدة" + +#: templates/recruitment/agency_list.html:161 +msgid "Search by name, contact person, email, or country..." +msgstr "البحث بالاسم أو جهة الاتصال أو البريد الإلكتروني أو البلد..." + +#: templates/recruitment/agency_list.html:341 +msgid "Agency pagination" +msgstr "ترقيم صفحات الوكالة" + +#: templates/recruitment/agency_list.html:385 +#, fuzzy +#| msgid "No templates match your search \"%(query)s\"." +msgid "No agencies found matching your search criteria." +msgstr "لم يتم العثور على وكالات تطابق معايير البحث الخاصة بك." + +#: templates/recruitment/agency_list.html:387 +msgid "No hiring agencies have been added yet." +msgstr "لم تتم إضافة وكالات توظيف بعد." + +#: templates/recruitment/agency_list.html:391 +msgid "" +"Start by adding your first hiring agency to manage your recruitment partners." +msgstr "ابدأ بإضافة أول وكالة توظيف لك لإدارة شركاء التوظيف لديك." + +#: templates/recruitment/agency_list.html:394 +#, fuzzy +#| msgid "Add Your First Candidate" +msgid "Add Your First Agency" +msgstr "أضف وكالتك الأولى" + +#: templates/recruitment/agency_portal_assignment_detail.html:115 +#, fuzzy +#| msgid "Dashboard" +msgid "Back to Dashboard" +msgstr "العودة إلى لوحة التحكم" + +#: templates/recruitment/agency_portal_assignment_detail.html:118 +#: templates/recruitment/agency_portal_submit_candidate.html:114 +#, fuzzy +#| msgid "New Candidates" +msgid "Submit New Candidate" +msgstr "تقديم مرشح جديد" + +#: templates/recruitment/agency_portal_assignment_detail.html:169 +#, fuzzy +#| msgid "Training" +msgid "days remaining" +msgstr "أيام متبقية" + +#: templates/recruitment/agency_portal_assignment_detail.html:182 +#, fuzzy +#| msgid "Job Description" +msgid "Job Description " +msgstr "وصف الوظيفة" + +#: templates/recruitment/agency_portal_assignment_detail.html:269 +#: templates/recruitment/agency_portal_assignment_detail.html:528 +#, fuzzy +#| msgid "Update Candidate" +msgid "Edit Candidate" +msgstr "تعديل المرشح" + +#: templates/recruitment/agency_portal_assignment_detail.html:272 +#: templates/recruitment/agency_portal_assignment_detail.html:593 +#: templates/recruitment/agency_portal_assignment_detail.html:612 +#, fuzzy +#| msgid "Create Candidate" +msgid "Remove Candidate" +msgstr "إزالة المرشح" + +#: templates/recruitment/agency_portal_assignment_detail.html:321 +msgid "Submit candidates using the form above to get started." +msgstr "قدم المرشحين باستخدام النموذج أعلاه للبدء." + +#: templates/recruitment/agency_portal_assignment_detail.html:374 +#: templates/recruitment/agency_portal_submit_candidate.html:156 +#, fuzzy +#| msgid "Submit" +msgid "Can Submit" +msgstr "يمكن التقديم" + +#: templates/recruitment/agency_portal_assignment_detail.html:376 +#: templates/recruitment/agency_portal_submit_candidate.html:158 +#, fuzzy +#| msgid "Submit" +msgid "Cannot Submit" +msgstr "لا يمكن التقديم" + +#: templates/recruitment/agency_portal_assignment_detail.html:406 +msgid "Assignment Info" +msgstr "معلومات التعيين" + +#: templates/recruitment/agency_portal_assignment_detail.html:415 +msgid "Days Remaining" +msgstr "الأيام المتبقية" + +#: templates/recruitment/agency_portal_assignment_detail.html:417 +#: templates/recruitment/agency_portal_submit_candidate.html:147 +msgid "days" +msgstr "يوم" + +#: templates/recruitment/agency_portal_assignment_detail.html:422 +msgid "Submission Rate" +msgstr "معدل التقديم" + +#: templates/recruitment/agency_portal_assignment_detail.html:478 +msgid "Send Message to Admin" +msgstr "إرسال رسالة إلى المسؤول" + +#: templates/recruitment/agency_portal_assignment_detail.html:493 +msgid "Priority" +msgstr "الأولوية" + +#: templates/recruitment/agency_portal_assignment_detail.html:495 +msgid "Low" +msgstr "منخفض" + +#: templates/recruitment/agency_portal_assignment_detail.html:496 +msgid "Medium" +msgstr "متوسط" + +#: templates/recruitment/agency_portal_assignment_detail.html:497 +msgid "High" +msgstr "مرتفع" + +#: templates/recruitment/agency_portal_assignment_detail.html:498 +#, fuzzy +#| msgid "User Agent" +msgid "Urgent" +msgstr "عاجل" + +#: templates/recruitment/agency_portal_assignment_detail.html:603 +#, fuzzy +#| msgid "Are you sure you want to delete this candidate?" +msgid "" +"Are you sure you want to remove this candidate? This action cannot be undone." +msgstr "" +"هل أنت متأكد من رغبتك في إزالة هذا المرشح؟ لا يمكن التراجع عن هذا الإجراء." + +#: templates/recruitment/agency_portal_assignment_detail.html:605 +#, fuzzy +#| msgid "Candidate" +msgid "Candidate:" +msgstr "المرشح:" + +#: templates/recruitment/agency_portal_assignment_detail.html:644 +msgid "Error loading candidate data. Please try again." +msgstr "خطأ في تحميل بيانات المرشح. يرجى المحاولة مرة أخرى." + +#: templates/recruitment/agency_portal_assignment_detail.html:679 +#: templates/recruitment/agency_portal_assignment_detail.html:684 +msgid "Error updating candidate. Please try again." +msgstr "خطأ في تحديث المرشح. يرجى المحاولة مرة أخرى." + +#: templates/recruitment/agency_portal_assignment_detail.html:706 +#: templates/recruitment/agency_portal_assignment_detail.html:711 +msgid "Error removing candidate. Please try again." +msgstr "خطأ في إزالة المرشح. يرجى المحاولة مرة أخرى." + +#: templates/recruitment/agency_portal_dashboard.html:4 +#: templates/recruitment/agency_portal_dashboard.html:45 +#, fuzzy +#| msgid "Dashboard" +msgid "Agency Dashboard" +msgstr "لوحة تحكم الوكالة" + +#: templates/recruitment/agency_portal_dashboard.html:48 +msgid "Welcome back" +msgstr "مرحبًا بعودتك" + +#: templates/recruitment/agency_portal_dashboard.html:76 +msgid "Total Assignments" +msgstr "إجمالي المهام" + +#: templates/recruitment/agency_portal_dashboard.html:87 +msgid "Active Assignments" +msgstr "المهام النشطة" + +#: templates/recruitment/agency_portal_dashboard.html:98 +#: templates/recruitment/partials/stats_cards.html:28 +#, fuzzy +#| msgid "All Candidates" +msgid "Total Candidates" +msgstr "إجمالي المرشحين" + +#: templates/recruitment/agency_portal_dashboard.html:109 +#, fuzzy +#| msgid "Error Message" +msgid "Unread Messages" +msgstr "الرسائل غير المقروءة" + +#: templates/recruitment/agency_portal_dashboard.html:121 +msgid "Your Job Assignments" +msgstr "مهام الوظائف الخاصة بك" + +#: templates/recruitment/agency_portal_dashboard.html:123 +msgid "assignments" +msgstr "تعيينات" + +#: templates/recruitment/agency_portal_dashboard.html:166 +msgid "days left" +msgstr "أيام متبقية" + +#: templates/recruitment/agency_portal_dashboard.html:168 +msgid "days overdue" +msgstr "أيام متأخرة" + +#: templates/recruitment/agency_portal_dashboard.html:202 +msgid "Submissions Closed" +msgstr "التقديمات مغلقة" + +#: templates/recruitment/agency_portal_dashboard.html:230 +msgid "No Job Assignments Found" +msgstr "لم يتم العثور على مهام وظائف" + +#: templates/recruitment/agency_portal_dashboard.html:232 +msgid "" +"You don't have any job assignments yet. Please contact the administrator if " +"you expect to have assignments." +msgstr "" +"ليس لديك أي مهام وظائف حتى الآن. يرجى التواصل مع المسؤول إذا كنت تتوقع وجود " +"مهام." + +#: templates/recruitment/agency_portal_login.html:4 +msgid "Agency Portal Login" +msgstr "تسجيل الدخول إلى بوابة الوكالة" + +#: templates/recruitment/agency_portal_login.html:128 +#, fuzzy +#| msgid "Edit candidate information and details" +msgid "Submit candidates for job assignments" +msgstr "تقديم المرشحين للتعيينات الوظيفية" + +#: templates/recruitment/agency_portal_login.html:159 +msgid "Enter the access token provided by the hiring organization" +msgstr "أدخل رمز الوصول المميز المقدم من قبل منظمة التوظيف" + +#: templates/recruitment/agency_portal_login.html:181 +msgid "Enter the password for this access token" +msgstr "أدخل كلمة المرور لرمز الوصول المميز هذا" + +#: templates/recruitment/agency_portal_login.html:189 +msgid "Access Portal" +msgstr "الوصول إلى البوابة" + +#: templates/recruitment/agency_portal_login.html:198 +msgid "Need Help?" +msgstr "هل تحتاج إلى مساعدة؟" + +#: templates/recruitment/agency_portal_login.html:206 +#, fuzzy +#| msgid "Contact & Job" +msgid "Contact Support" +msgstr "الاتصال بالدعم" + +#: templates/recruitment/agency_portal_login.html:208 +msgid "Reach out to your hiring contact" +msgstr "تواصل مع جهة الاتصال الخاصة بالتوظيف" + +#: templates/recruitment/agency_portal_login.html:215 +#, fuzzy +#| msgid "Duration" +msgid "Documentation" +msgstr "التوثيق" + +#: templates/recruitment/agency_portal_login.html:217 +msgid "View user guides and tutorials" +msgstr "عرض أدلة المستخدم والبرامج التعليمية" + +#: templates/recruitment/agency_portal_login.html:227 +msgid "Security Notice" +msgstr "إشعار أمني" + +#: templates/recruitment/agency_portal_login.html:230 +msgid "" +"This portal is for authorized agency partners only. Access is monitored and " +"logged." +msgstr "" +"هذه البوابة مخصصة لشركاء الوكالة المعتمدين فقط. يتم مراقبة وتسجيل الوصول." + +#: templates/recruitment/agency_portal_login.html:234 +msgid "" +"If you believe you've received this link in error, please contact the hiring " +"organization immediately." +msgstr "" +"إذا كنت تعتقد أنك تلقيت هذا الرابط عن طريق الخطأ، فيرجى الاتصال بمنظمة " +"التوظيف على الفور." + +#: templates/recruitment/agency_portal_login.html:295 +msgid "Please enter your access token." +msgstr "الرجاء إدخال رمز الوصول المميز الخاص بك." + +#: templates/recruitment/agency_portal_login.html:302 +msgid "Please enter your password." +msgstr "الرجاء إدخال كلمة المرور الخاصة بك." + +#: templates/recruitment/agency_portal_submit_candidate.html:117 +#, fuzzy +#| msgid "No candidates found." +msgid "Submit a candidate for" +msgstr "قدم مرشحًا لـ" + +#: templates/recruitment/agency_portal_submit_candidate.html:136 +#, fuzzy +#| msgid "Position No:" +msgid "Position:" +msgstr "المنصب:" + +#: templates/recruitment/agency_portal_submit_candidate.html:145 +msgid "Days Remaining:" +msgstr "الأيام المتبقية:" + +#: templates/recruitment/agency_portal_submit_candidate.html:154 +#, fuzzy +#| msgid "Status" +msgid "Status:" +msgstr "الحالة:" + +#: templates/recruitment/agency_portal_submit_candidate.html:170 +#: templates/recruitment/candidate_create.html:112 +msgid "Candidate Information" +msgstr "معلومات المرشح" + +#: templates/recruitment/agency_portal_submit_candidate.html:226 +#, fuzzy +#| msgid "Enter email" +msgid "Enter email address" +msgstr "أدخل عنوان البريد الإلكتروني" + +#: templates/recruitment/agency_portal_submit_candidate.html:246 +#, fuzzy +#| msgid "Candidate Information" +msgid "Address Information" +msgstr "معلومات العنوان" + +#: templates/recruitment/agency_portal_submit_candidate.html:251 +#, fuzzy +#| msgid "IP Address" +msgid "Full Address" +msgstr "العنوان الكامل" + +#: templates/recruitment/agency_portal_submit_candidate.html:258 +#, fuzzy +#| msgid "Enter last name" +msgid "Enter full address" +msgstr "أدخل العنوان الكامل" + +#: templates/recruitment/agency_portal_submit_candidate.html:267 +#, fuzzy +#| msgid "Resume" +msgid "Resume/CV" +msgstr "السيرة الذاتية / CV" + +#: templates/recruitment/agency_portal_submit_candidate.html:272 +#, fuzzy +#| msgid "Download Resume" +msgid "Upload Resume" +msgstr "تحميل السيرة الذاتية" + +#: templates/recruitment/agency_portal_submit_candidate.html:284 +msgid "Click to upload or drag and drop" +msgstr "انقر للتحميل أو اسحب وأفلت" + +#: templates/recruitment/agency_portal_submit_candidate.html:286 +msgid "Accepted formats: PDF, DOC, DOCX (Maximum 5MB)" +msgstr "التنسيقات المقبولة: PDF، DOC، DOCX (الحد الأقصى 5 ميجابايت)" + +#: templates/recruitment/agency_portal_submit_candidate.html:294 +msgid "Remove File" +msgstr "إزالة الملف" + +#: templates/recruitment/agency_portal_submit_candidate.html:306 +msgid "Additional Notes" +msgstr "ملاحظات إضافية" + +#: templates/recruitment/agency_portal_submit_candidate.html:311 +msgid "Notes (Optional)" +msgstr "ملاحظات (اختياري)" + +#: templates/recruitment/agency_portal_submit_candidate.html:317 +msgid "Any additional information about the candidate" +msgstr "أي معلومات إضافية حول المرشح" + +#: templates/recruitment/agency_portal_submit_candidate.html:328 +msgid "Submitted candidates will be reviewed by the hiring team." +msgstr "سيتم مراجعة المرشحين المقدمين من قبل فريق التوظيف." + +#: templates/recruitment/agency_portal_submit_candidate.html:349 +#, fuzzy +#| msgid "Create Candidate" +msgid "Cannot Submit Candidates" +msgstr "لا يمكن تقديم المرشحين" + +#: templates/recruitment/agency_portal_submit_candidate.html:353 +msgid "This assignment has expired. Submissions are no longer accepted." +msgstr "انتهت صلاحية هذا التعيين. لم يعد يتم قبول التقديمات." + +#: templates/recruitment/agency_portal_submit_candidate.html:356 +msgid "Maximum candidate limit reached for this assignment." +msgstr "تم الوصول إلى الحد الأقصى لعدد المرشحين لهذا التعيين." + +#: templates/recruitment/agency_portal_submit_candidate.html:359 +msgid "This assignment is not currently active." +msgstr "هذا التعيين غير نشط حاليًا." + +#: templates/recruitment/agency_portal_submit_candidate.html:383 +msgid "Submitting candidate..." +msgstr "جاري تقديم المرشح..." + +#: templates/recruitment/agency_portal_submit_candidate.html:384 +msgid "Please wait while we process your submission." +msgstr "يرجى الانتظار بينما نقوم بمعالجة طلبك." + +#: templates/recruitment/agency_portal_submit_candidate.html:450 +msgid "Please upload a PDF, DOC, or DOCX file." +msgstr "الرجاء تحميل ملف PDF، DOC، أو DOCX." + +#: templates/recruitment/agency_portal_submit_candidate.html:457 +msgid "File size must be less than 5MB." +msgstr "يجب أن يكون حجم الملف أقل من 5 ميجابايت." + +#: templates/recruitment/agency_portal_submit_candidate.html:478 +#, fuzzy +#| msgid "Submit" +msgid "Submitting..." +msgstr "جاري الإرسال..." + +#: templates/recruitment/agency_portal_submit_candidate.html:500 +#, fuzzy +#| msgid "Posted successfully!" +msgid "Candidate submitted successfully!" +msgstr "تم تقديم المرشح بنجاح!" + +#: templates/recruitment/agency_portal_submit_candidate.html:531 +msgid "Error submitting candidate. Please try again." +msgstr "خطأ في تقديم المرشح. يرجى المحاولة مرة أخرى." + +#: templates/recruitment/agency_portal_submit_candidate.html:553 +msgid "Network error. Please check your connection and try again." +msgstr "خطأ في الشبكة. يرجى التحقق من اتصالك والمحاولة مرة أخرى." + +#: templates/recruitment/candidate_create.html:94 +msgid "Create New Candidate" +msgstr "إنشاء مرشح جديد" + +#: templates/recruitment/candidate_create.html:96 +msgid "Enter details to create a new candidate record." +msgstr "أدخل التفاصيل لإنشاء سجل مرشح جديد." + +#: templates/recruitment/candidate_create.html:131 +msgid "Create Candidate" +msgstr "إنشاء مرشح" + +#: templates/recruitment/candidate_detail.html:292 +msgid "Stage:" +msgstr "المرحلة:" + +#: templates/recruitment/candidate_detail.html:297 +msgid "Applied for:" +msgstr "تقدم لـ:" + +#: templates/recruitment/candidate_detail.html:303 +#: templates/recruitment/candidate_exam_view.html:229 +#: templates/recruitment/candidate_interview_view.html:222 +#: templates/recruitment/candidate_offer_view.html:222 +#: templates/recruitment/candidate_screening_view.html:341 +msgid "Change Stage" +msgstr "تغيير المرحلة" + +#: templates/recruitment/candidate_detail.html:313 +msgid "Contact & Job" +msgstr "الاتصال والوظيفة" + +#: templates/recruitment/candidate_detail.html:320 +msgid "Journey Timeline" +msgstr "الجدول الزمني للرحلة" + +#: templates/recruitment/candidate_detail.html:331 +msgid "Core Details" +msgstr "التفاصيل الأساسية" + +#: templates/recruitment/candidate_detail.html:346 +msgid "Position Applied" +msgstr "المنصب الذي تم التقديم عليه" + +#: templates/recruitment/candidate_detail.html:377 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidate Journey" +msgstr "رحلة المرشح" + +#: templates/recruitment/candidate_detail.html:381 +#, fuzzy +#| msgid "Change Stage" +msgid "Current Stage" +msgstr "المرحلة الحالية" + +#: templates/recruitment/candidate_detail.html:385 +#, fuzzy +#| msgid "Last Updated:" +msgid "Latest status update:" +msgstr "آخر تحديث للحالة:" + +#: templates/recruitment/candidate_detail.html:389 +#, fuzzy +#| msgid "Financial & Timeline" +msgid "Historical Timeline" +msgstr "الجدول الزمني التاريخي" + +#: templates/recruitment/candidate_detail.html:397 +#, fuzzy +#| msgid "Application Stage" +msgid "Application Submitted" +msgstr "تم تقديم الطلب" + +#: templates/recruitment/candidate_detail.html:458 +msgid "AI Generated Summary" +msgstr "ملخص تم إنشاؤه بواسطة الذكاء الاصطناعي" + +#: templates/recruitment/candidate_detail.html:468 +msgid "AI Analysis Report" +msgstr "تقرير التحليل بالذكاء الاصطناعي" + +#: templates/recruitment/candidate_detail.html:474 +msgid "Match Score" +msgstr "نقاط التوافق" + +#: templates/recruitment/candidate_detail.html:487 +msgid "Category" +msgstr "الفئة" + +#: templates/recruitment/candidate_detail.html:490 +msgid "Job Fit Narrative" +msgstr "وصف التوافق مع الوظيفة" + +#: templates/recruitment/candidate_detail.html:521 +#, fuzzy +#| msgid "Material Details" +msgid "Professional Details" +msgstr "التفاصيل المهنية" + +#: templates/recruitment/candidate_detail.html:522 +msgid "Years of Experience:" +msgstr "سنوات الخبرة:" + +#: templates/recruitment/candidate_detail.html:523 +msgid "Most Recent Job Title:" +msgstr "أحدث مسمى وظيفي:" + +#: templates/recruitment/candidate_detail.html:524 +msgid "Experience Industry Match:" +msgstr "توافق الصناعة مع الخبرة:" + +#: templates/recruitment/candidate_detail.html:529 +msgid "Soft Skills Score:" +msgstr "نقاط المهارات الشخصية:" + +#: templates/recruitment/candidate_detail.html:534 +#, fuzzy +#| msgid "Sync Status" +msgid "Screening Status" +msgstr "حالة الفرز" + +#: templates/recruitment/candidate_detail.html:536 +msgid "Minimum Requirements Met:" +msgstr "تم استيفاء الحد الأدنى من المتطلبات:" + +#: templates/recruitment/candidate_detail.html:544 +msgid "Screening Stage Rating:" +msgstr "تقييم مرحلة الفرز:" + +#: templates/recruitment/candidate_detail.html:601 +msgid "Resume is being parsed" +msgstr "جاري تحليل السيرة الذاتية" + +#: templates/recruitment/candidate_detail.html:602 +msgid "" +"Our AI is analyzing the candidate's resume to generate insights. This may " +"take a few moments." +msgstr "" +"يقوم الذكاء الاصطناعي الخاص بنا بتحليل السيرة الذاتية للمرشح لتوليد رؤى. قد " +"يستغرق هذا بضع لحظات." + +#: templates/recruitment/candidate_detail.html:622 +msgid "Management Actions" +msgstr "إجراءات الإدارة" + +#: templates/recruitment/candidate_detail.html:625 +msgid "Edit Details" +msgstr "تعديل التفاصيل" + +#: templates/recruitment/candidate_detail.html:627 +msgid "Are you sure you want to delete this candidate?" +msgstr "هل أنت متأكد من رغبتك في حذف هذا المرشح؟" + +#: templates/recruitment/candidate_detail.html:628 +msgid "Delete Candidate" +msgstr "حذف المرشح" + +#: templates/recruitment/candidate_detail.html:637 +#, fuzzy +#| msgid "View API Response" +msgid "View Actual Resume" +msgstr "عرض السيرة الذاتية الفعلية" + +#: templates/recruitment/candidate_detail.html:641 +msgid "Download Resume" +msgstr "تحميل السيرة الذاتية" + +#: templates/recruitment/candidate_detail.html:646 +msgid "View Resume AI Overview" +msgstr "عرض نظرة عامة الذكاء الاصطناعي للسيرة الذاتية" + +#: templates/recruitment/candidate_detail.html:654 +#, fuzzy +#| msgid "Time to Hire:  " +msgid "Time to Hire: " +msgstr "الوقت اللازم للتوظيف: " + +#: templates/recruitment/candidate_detail.html:679 +msgid "Unable to Parse Resume , click to retry" +msgstr "تعذر تحليل السيرة الذاتية، انقر للمحاولة مرة أخرى" + +#: templates/recruitment/candidate_exam_view.html:174 +msgid "Exam Management" +msgstr "إدارة الاختبارات" + +#: templates/recruitment/candidate_exam_view.html:177 +msgid "Candidates in Exam Stage:" +msgstr "المرشحون في مرحلة الاختبار:" + +#: templates/recruitment/candidate_exam_view.html:183 +msgid "Export exam candidates to CSV" +msgstr "تصدير مرشحي الاختبار إلى CSV" + +#: templates/recruitment/candidate_exam_view.html:184 +#: templates/recruitment/candidate_hired_view.html:208 +#: templates/recruitment/candidate_interview_view.html:187 +#: templates/recruitment/candidate_offer_view.html:186 +#: templates/recruitment/candidate_screening_view.html:235 +msgid "Export CSV" +msgstr "تصدير CSV" + +#: templates/recruitment/candidate_exam_view.html:197 +#: templates/recruitment/candidate_screening_view.html:312 +msgid "Candidate List" +msgstr "قائمة المرشحين" + +#: templates/recruitment/candidate_exam_view.html:199 +#, fuzzy +#| msgid "AI Score" +msgid "Sorted by AI Score" +msgstr "نقاط الذكاء الاصطناعي" + +#: templates/recruitment/candidate_exam_view.html:219 +msgid "Interview Stage" +msgstr "مرحلة المقابلة" + +#: templates/recruitment/candidate_exam_view.html:222 +msgid "Screening Stage" +msgstr "مرحلة الفرز" + +#: templates/recruitment/candidate_exam_view.html:251 +#: templates/recruitment/candidate_screening_view.html:367 +msgid "Contact Info" +msgstr "معلومات الاتصال" + +#: templates/recruitment/candidate_exam_view.html:254 +msgid "Exam Results" +msgstr "نتائج الاختبار" + +#: templates/recruitment/candidate_exam_view.html:330 +msgid "No candidates are currently in the Exam stage for this job." +msgstr "لا يوجد مرشحون حاليًا في مرحلة الاختبار لهذه الوظيفة." + +#: templates/recruitment/candidate_exam_view.html:343 +msgid "Candidate Details & Exam Update" +msgstr "تفاصيل المرشح وتحديث الاختبار" + +#: templates/recruitment/candidate_exam_view.html:350 +#: templates/recruitment/candidate_screening_view.html:490 +#, fuzzy +#| msgid "No candidates found." +msgid "Loading candidate data..." +msgstr "جاري تحميل بيانات المرشح..." + +#: templates/recruitment/candidate_hired_view.html:192 +#, fuzzy +#| msgid "New Candidates" +msgid "Hired Candidates" +msgstr "المرشحون الذين تم تعيينهم" + +#: templates/recruitment/candidate_hired_view.html:195 +msgid "Successfully Hired:" +msgstr "تم التوظيف بنجاح:" + +#: templates/recruitment/candidate_hired_view.html:202 +msgid "Sync hired candidates to external sources" +msgstr "مزامنة المرشحين المعينين مع مصادر خارجية" + +#: templates/recruitment/candidate_hired_view.html:203 +#: templates/recruitment/candidate_hired_view.html:407 +#, fuzzy +#| msgid "Sources" +msgid "Sync to Sources" +msgstr "مزامنة مع المصادر" + +#: templates/recruitment/candidate_hired_view.html:207 +msgid "Export hired candidates to CSV" +msgstr "تصدير المرشحين المعينين إلى CSV" + +#: templates/recruitment/candidate_hired_view.html:219 +msgid "Congratulations!" +msgstr "تهانينا!" + +#: templates/recruitment/candidate_hired_view.html:220 +msgid "" +"These candidates have successfully completed the hiring process and joined " +"your team." +msgstr "أكمل هؤلاء المرشحون عملية التوظيف بنجاح وانضموا إلى فريقك." + +#: templates/recruitment/candidate_hired_view.html:234 +#, fuzzy +#| msgid "Open Positions" +msgid "Applied Position" +msgstr "المنصب الذي تم التقديم عليه" + +#: templates/recruitment/candidate_hired_view.html:300 +#, fuzzy +#| msgid "Candidates will appear here once they apply for this position." +msgid "No candidates have been hired for this position yet." +msgstr "لم يتم تعيين أي مرشحين لهذا المنصب بعد." + +#: templates/recruitment/candidate_hired_view.html:313 +#, fuzzy +#| msgid "New Candidates" +msgid "Hired Candidate Details" +msgstr "تفاصيل المرشح المعين" + +#: templates/recruitment/candidate_hired_view.html:320 +#: templates/recruitment/candidate_interview_view.html:440 +#: templates/recruitment/candidate_interview_view.html:629 +#: templates/recruitment/candidate_offer_view.html:333 +msgid "Loading content..." +msgstr "جاري تحميل المحتوى..." + +#: templates/recruitment/candidate_hired_view.html:333 +#, fuzzy +#| msgid "Sync Status" +msgid "Sync Results" +msgstr "نتائج المزامنة" + +#: templates/recruitment/candidate_hired_view.html:340 +msgid "Syncing candidates..." +msgstr "جاري مزامنة المرشحين..." + +#: templates/recruitment/candidate_hired_view.html:369 +msgid "Syncing hired candidates..." +msgstr "جاري مزامنة المرشحين المعينين..." + +#: templates/recruitment/candidate_hired_view.html:370 +msgid "Please wait while we sync candidates to external sources." +msgstr "يرجى الانتظار بينما نقوم بمزامنة المرشحين مع المصادر الخارجية." + +#: templates/recruitment/candidate_hired_view.html:378 +msgid "Syncing..." +msgstr "جاري المزامنة..." + +#: templates/recruitment/candidate_hired_view.html:402 +msgid "An unexpected error occurred during sync." +msgstr "حدث خطأ غير متوقع أثناء المزامنة." + +#: templates/recruitment/candidate_hired_view.html:419 +#, fuzzy +#| msgid "Summary" +msgid "Sync Summary" +msgstr "ملخص المزامنة" + +#: templates/recruitment/candidate_hired_view.html:422 +#, fuzzy +#| msgid "Sources" +msgid "Total Sources:" +msgstr "إجمالي المصادر:" + +#: templates/recruitment/candidate_hired_view.html:425 +msgid "Successful:" +msgstr "ناجح:" + +#: templates/recruitment/candidate_hired_view.html:428 +#, fuzzy +#| msgid "Failed" +msgid "Failed:" +msgstr "فشل:" + +#: templates/recruitment/candidate_hired_view.html:431 +#, fuzzy +#| msgid "Candidates" +msgid "Candidates Synced:" +msgstr "المرشحون الذين تمت مزامنتهم:" + +#: templates/recruitment/candidate_hired_view.html:439 +#, fuzzy +#| msgid "Core Details" +msgid "Source Details" +msgstr "تفاصيل المصدر" + +#: templates/recruitment/candidate_hired_view.html:457 +#, fuzzy +#| msgid "Candidate Profiles" +msgid "Candidates Processed:" +msgstr "المرشحون الذين تمت معالجتهم:" + +#: templates/recruitment/candidate_hired_view.html:461 +#: templates/recruitment/notification_detail.html:71 +#, fuzzy +#| msgid "Duration" +msgid "Duration:" +msgstr "المدة:" + +#: templates/recruitment/candidate_hired_view.html:465 +#: templates/recruitment/notification_confirm_delete.html:21 +#, fuzzy +#| msgid "Error Message" +msgid "Message:" +msgstr "الرسالة:" + +#: templates/recruitment/candidate_hired_view.html:494 +msgid "Sync task failed" +msgstr "فشلت مهمة المزامنة" + +#: templates/recruitment/candidate_hired_view.html:503 +msgid "Failed to check sync status" +msgstr "فشل التحقق من حالة المزامنة" + +#: templates/recruitment/candidate_hired_view.html:510 +msgid "Sync timed out after 5 minutes" +msgstr "انتهت مهلة المزامنة بعد 5 دقائق" + +#: templates/recruitment/candidate_hired_view.html:521 +msgid "Sync in progress..." +msgstr "المزامنة قيد التقدم..." + +#: templates/recruitment/candidate_hired_view.html:532 +#, fuzzy +#| msgid "Failed" +msgid "Sync Failed" +msgstr "فشلت المزامنة" + +#: templates/recruitment/candidate_interview_view.html:177 +#, fuzzy +#| msgid "Interview Date" +msgid "Interview Management" +msgstr "إدارة المقابلات" + +#: templates/recruitment/candidate_interview_view.html:180 +msgid "Candidates in Interview Stage:" +msgstr "المرشحون في مرحلة المقابلة:" + +#: templates/recruitment/candidate_interview_view.html:186 +msgid "Export interview candidates to CSV" +msgstr "تصدير مرشحي المقابلة إلى CSV" + +#: templates/recruitment/candidate_interview_view.html:215 +#, fuzzy +#| msgid "Offer" +msgid "To Offer" +msgstr "إلى العرض" + +#: templates/recruitment/candidate_interview_view.html:218 +#, fuzzy +#| msgid "Exam" +msgid "To Exam" +msgstr "إلى الاختبار" + +#: templates/recruitment/candidate_interview_view.html:232 +#, fuzzy +#| msgid "Interview" +msgid "Schedule Interviews" +msgstr "جدولة المقابلات" + +#: templates/recruitment/candidate_interview_view.html:240 +#, fuzzy +#| msgid "Participant Video" +msgid "Manage Participants" +msgstr "إدارة المشاركين" + +#: templates/recruitment/candidate_interview_view.html:265 +#, fuzzy +#| msgid "Meeting ID" +msgid "Meeting Date" +msgstr "تاريخ الاجتماع" + +#: templates/recruitment/candidate_interview_view.html:267 +#, fuzzy +#| msgid "Meeting Details" +msgid "Meeting Status" +msgstr "حالة الاجتماع" + +#: templates/recruitment/candidate_interview_view.html:268 +#, fuzzy +#| msgid "Interview Date" +msgid "Interview Result" +msgstr "نتيجة المقابلة" + +#: templates/recruitment/candidate_interview_view.html:300 +msgid "Minutes" +msgstr "دقائق" + +#: templates/recruitment/candidate_interview_view.html:421 +msgid "No candidates are currently in the Interview stage for this job." +msgstr "لا يوجد مرشحون حاليًا في مرحلة المقابلة لهذه الوظيفة." + +#: templates/recruitment/candidate_interview_view.html:433 +#: templates/recruitment/candidate_interview_view.html:633 +#: templates/recruitment/candidate_offer_view.html:326 +msgid "Candidate Details / Bulk Action Form" +msgstr "تفاصيل المرشح / نموذج الإجراءات المجمعة" + +#: templates/recruitment/candidate_interview_view.html:453 +msgid "Manage all participants" +msgstr "إدارة جميع المشاركين" + +#: templates/recruitment/candidate_interview_view.html:476 +#: templates/recruitment/candidate_interview_view.html:501 +msgid "Users" +msgstr "المستخدمون" + +#: templates/recruitment/candidate_interview_view.html:530 +msgid "Loading email form..." +msgstr "جاري تحميل نموذج البريد الإلكتروني..." + +#: templates/recruitment/candidate_list.html:193 +msgid "Candidate Profiles" +msgstr "ملفات المرشحين الشخصية" + +#: templates/recruitment/candidate_list.html:197 +msgid "Add New Candidate" +msgstr "إضافة مرشح جديد" + +#: templates/recruitment/candidate_list.html:221 +msgid "Filter by Job" +msgstr "تصفية حسب الوظيفة" + +#: templates/recruitment/candidate_list.html:274 +msgid "Major" +msgstr "التخصص" + +#: templates/recruitment/candidate_list.html:277 +#, fuzzy +#| msgid "Created at" +msgid "created At" +msgstr "تم الإنشاء في" + +#: templates/recruitment/candidate_list.html:401 +#, fuzzy +#| msgid "No candidates found." +msgid "No candidate profiles found" +msgstr "لم يتم العثور على ملفات مرشحين شخصية" + +#: templates/recruitment/candidate_list.html:402 +#, fuzzy +#| msgid "Start by adding a new profile or adjusting your search filters." +msgid "Create your first candidate profile or adjust your filters." +msgstr "أنشئ أول ملف شخصي لمرشح أو عدّل عوامل التصفية الخاصة بك." + +#: templates/recruitment/candidate_list.html:405 +#, fuzzy +#| msgid "Add New Candidate" +msgid "Add Candidate" +msgstr "إضافة مرشح" + +#: templates/recruitment/candidate_offer_view.html:176 +#, fuzzy +#| msgid "Form Management" +msgid "Offer Management" +msgstr "إدارة العروض" + +#: templates/recruitment/candidate_offer_view.html:179 +msgid "Candidates in Offer Stage:" +msgstr "المرشحون في مرحلة العرض:" + +#: templates/recruitment/candidate_offer_view.html:185 +msgid "Export offer candidates to CSV" +msgstr "تصدير مرشحي العروض إلى CSV" + +#: templates/recruitment/candidate_offer_view.html:213 +msgid "To Hired" +msgstr "إلى التعيين" + +#: templates/recruitment/candidate_offer_view.html:216 +#, fuzzy +#| msgid "Rejected" +msgid "To Rejected" +msgstr "إلى الرفض" + +#: templates/recruitment/candidate_offer_view.html:313 +msgid "No candidates are currently in the Offer stage for this job." +msgstr "لا يوجد مرشحون حاليًا في مرحلة العرض لهذه الوظيفة." + +#: templates/recruitment/candidate_screening_view.html:224 +msgid "Applicant Screening" +msgstr "فحص المتقدمين" + +#: templates/recruitment/candidate_screening_view.html:227 +msgid "Job:" +msgstr "الوظيفة:" + +#: templates/recruitment/candidate_screening_view.html:234 +msgid "Export screening candidates to CSV" +msgstr "تصدير مرشحي الفرز إلى CSV" + +#: templates/recruitment/candidate_screening_view.html:249 +msgid "AI Scoring & Top Candidate Filter" +msgstr "تسجيل النقاط بالذكاء الاصطناعي وتصفية أفضل المرشحين" + +#: templates/recruitment/candidate_screening_view.html:257 +msgid "Min AI Score" +msgstr "الحد الأدنى لنقاط الذكاء الاصطناعي" + +#: templates/recruitment/candidate_screening_view.html:266 +msgid "Min Years Exp" +msgstr "الحد الأدنى لسنوات الخبرة" + +#: templates/recruitment/candidate_screening_view.html:278 +msgid "Any Rating" +msgstr "أي تقييم" + +#: templates/recruitment/candidate_screening_view.html:280 +msgid "Highly Qualified" +msgstr "مؤهل عالي" + +#: templates/recruitment/candidate_screening_view.html:283 +msgid "Qualified" +msgstr "مؤهل" + +#: templates/recruitment/candidate_screening_view.html:286 +msgid "Partially Qualified" +msgstr "مؤهل جزئي" + +#: templates/recruitment/candidate_screening_view.html:289 +msgid "Not Qualified" +msgstr "غير مؤهل" + +#: templates/recruitment/candidate_screening_view.html:296 +#, fuzzy +#| msgid "New Candidates" +msgid "Top N Candidates" +msgstr "أفضل N من المرشحين" + +#: templates/recruitment/candidate_screening_view.html:304 +msgid "Update Filters" +msgstr "تصفية" + +#: templates/recruitment/candidate_screening_view.html:333 +msgid "Exam Stage" +msgstr "مرحلة الاختبار" + +#: templates/recruitment/candidate_screening_view.html:373 +msgid "Is Qualified?" +msgstr "هل هو مؤهل؟" + +#: templates/recruitment/candidate_screening_view.html:376 +msgid "Professional Category" +msgstr "الفئة المهنية" + +#: templates/recruitment/candidate_screening_view.html:379 +msgid "Top 3 Skills" +msgstr "أفضل 3 مهارات" + +#: templates/recruitment/candidate_screening_view.html:422 +msgid "AI scoring.." +msgstr "تسجيل النقاط بالذكاء الاصطناعي..." + +#: templates/recruitment/candidate_screening_view.html:469 +msgid "No candidates match the current stage and filter criteria." +msgstr "لا يوجد مرشحون يطابقون المرحلة الحالية ومعايير التصفية." + +#: templates/recruitment/candidate_screening_view.html:483 +#, fuzzy +#| msgid "Candidate Profiles" +msgid "Candidate Criteria Review" +msgstr "مراجعة معايير المرشح" + +#: templates/recruitment/candidate_update.html:92 +msgid "Update Candidate:" +msgstr "تحديث المرشح:" + +#: templates/recruitment/candidate_update.html:94 +msgid "Edit candidate information and details" +msgstr "تعديل معلومات المرشح وتفاصيله" + +#: templates/recruitment/candidate_update.html:102 +msgid "View Candidate" +msgstr "عرض المرشح" + +#: templates/recruitment/candidate_update.html:116 +msgid "Candidate Form" +msgstr "نموذج المرشح" + +#: templates/recruitment/candidate_update.html:135 +msgid "Update Candidate" +msgstr "تحديث المرشح" + +#: templates/recruitment/dashboard.html:4 +#, fuzzy +#| msgid "Dashboard" +msgid "Recruitment Dashboard" +msgstr "لوحة تحكم التوظيف" + +#: templates/recruitment/dashboard.html:123 +msgid "Recruitment Analytics" +msgstr "تحليلات التوظيف" + +#: templates/recruitment/dashboard.html:133 +msgid "Data Scope: " +msgstr "نطاق البيانات: " + +#: templates/recruitment/dashboard.html:135 +msgid "Data Scope: All Jobs" +msgstr "نطاق البيانات: جميع الوظائف" + +#: templates/recruitment/dashboard.html:140 +#, fuzzy +#| msgid "Edit Job" +msgid "Filter Job:" +msgstr "تصفية الوظيفة:" + +#: templates/recruitment/dashboard.html:142 +msgid "All Jobs (Default View)" +msgstr "جميع الوظائف (العرض الافتراضي)" + +#: templates/recruitment/dashboard.html:170 +msgid "Daily Candidate Applications Trend" +msgstr "اتجاه طلبات المرشحين اليومية" + +#: templates/recruitment/dashboard.html:184 +msgid "Top 5 Application Volume" +msgstr "أعلى 5 حجوم طلبات" + +#: templates/recruitment/dashboard.html:200 +msgid "Pipeline Funnel: " +msgstr "مجموع المتقدمين حسب مسار التوظيف: " + +#: templates/recruitment/dashboard.html:202 +msgid "Total Pipeline Funnel (All Jobs)" +msgstr "مجموع المتقدمين حسب مسار التوظيف لجميع الوظائف" + +#: templates/recruitment/dashboard.html:216 +msgid "Time-to-Hire Target Check" +msgstr "فحص هدف وقت التوظيف" + +#: templates/recruitment/dashboard.html:231 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidates From Each Sources" +msgstr "المرشحون من كل مصدر" + +#: templates/recruitment/dashboard.html:278 +msgid "Top 5 Most Applied Jobs" +msgstr "أعلى 5 وظائف من حيث عدد المتقدمين" + +#: templates/recruitment/dashboard.html:286 +#, fuzzy +#| msgid "Applications" +msgid "Total Applications" +msgstr "إجمالي الطلبات" + +#: templates/recruitment/dashboard.html:338 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidate Count" +msgstr "عدد المرشحين" + +#: templates/recruitment/dashboard.html:406 +#, fuzzy +#| msgid "Create Job" +msgid "Current Job" +msgstr "الوظيفة الحالية" + +#: templates/recruitment/dashboard.html:431 +msgid "Daily Applications (Last 30 Days)" +msgstr "الطلبات اليومية (آخر 30 يومًا)" + +#: templates/recruitment/dashboard.html:446 +#: venv/lib/python3.13/site-packages/unfold/widgets.py:598 +#: venv/lib/python3.13/site-packages/unfold/widgets.py:639 +#, fuzzy +#| msgid "End Date" +msgid "Date" +msgstr "التاريخ" + +#: templates/recruitment/dashboard.html:451 +msgid "New Candidates" +msgstr "المرشحون الجدد" + +#: templates/recruitment/notification_confirm_all_read.html:4 +msgid "Mark All as Read" +msgstr "وضع علامة مقروء على الكل" + +#: templates/recruitment/notification_confirm_all_read.html:22 +msgid "What this will do" +msgstr "ماذا سيفعل هذا" + +#: templates/recruitment/notification_confirm_all_read.html:25 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "                                    This will mark %(count)s unread " +#| "notification as read.\n" +#| "                                " +#| msgid_plural "" +#| "\n" +#| "                                    This will mark all %(count)s unread " +#| "notifications as read.\n" +#| "                                " +msgid "" +"\n" +" This will mark %(count)s unread " +"notification as read.\n" +" " +msgid_plural "" +"\n" +" This will mark all %(count)s unread " +"notifications as read.\n" +" " +msgstr[0] "" +"\n" +"سيتم وضع علامة مقروء على إشعار واحد غير مقروء." +msgstr[1] "" +"\n" +"سيتم وضع علامة مقروء على جميع الإشعارات %(count)s غير المقروءة." +msgstr[2] "" +"\n" +"سيتم وضع علامة مقروء على جميع الإشعارات %(count)s غير المقروءة." +msgstr[3] "" +"\n" +"سيتم وضع علامة مقروء على جميع الإشعارات %(count)s غير المقروءة." +msgstr[4] "" +"\n" +"سيتم وضع علامة مقروء على جميع الإشعارات %(count)s غير المقروءة." +msgstr[5] "" +"\n" +"سيتم وضع علامة مقروء على جميع الإشعارات %(count)s غير المقروءة." + +#: templates/recruitment/notification_confirm_all_read.html:32 +msgid "" +"You can still view all notifications in your notification list, but they " +"won't appear as unread." +msgstr "" +"لا يزال بإمكانك عرض جميع الإشعارات في قائمة الإشعارات الخاصة بك، لكنها لن " +"تظهر كغير مقروءة." + +#: templates/recruitment/notification_confirm_all_read.html:38 +msgid "All caught up!" +msgstr "لقد انتهيت من كل شيء!" + +#: templates/recruitment/notification_confirm_all_read.html:41 +msgid "You don't have any unread notifications to mark as read." +msgstr "ليس لديك أي إشعارات غير مقروءة لوضع علامة مقروء عليها." + +#: templates/recruitment/notification_confirm_all_read.html:50 +msgid "Yes, Mark All as Read" +msgstr "نعم، وضع علامة مقروء على الكل" + +#: templates/recruitment/notification_confirm_all_read.html:58 +#: templates/recruitment/notification_detail.html:18 +#, fuzzy +#| msgid "Back to Meetings" +msgid "Back to Notifications" +msgstr "العودة إلى الإشعارات" + +#: templates/recruitment/notification_confirm_delete.html:4 +#, fuzzy +#| msgid "Delete Meeting" +msgid "Delete Notification" +msgstr "حذف الإشعار" + +#: templates/recruitment/notification_confirm_delete.html:20 +msgid "Notification Preview" +msgstr "معاينة الإشعار" + +#: templates/recruitment/notification_confirm_delete.html:30 +#, fuzzy +#| msgid "Delete" +msgid "Yes, Delete" +msgstr "نعم، حذف" + +#: templates/recruitment/notification_detail.html:4 +#: templates/recruitment/notification_detail.html:12 +#, fuzzy +#| msgid "Meeting Details" +msgid "Notification Details" +msgstr "تفاصيل الإشعار" + +#: templates/recruitment/notification_detail.html:14 +msgid "View notification details and manage your preferences" +msgstr "عرض تفاصيل الإشعار وإدارة تفضيلاتك" + +#: templates/recruitment/notification_detail.html:47 +#: templates/recruitment/notification_detail.html:132 +msgid "Mark as Read" +msgstr "وضع علامة مقروء" + +#: templates/recruitment/notification_detail.html:51 +#: templates/recruitment/notification_detail.html:136 +msgid "Mark as Unread" +msgstr "وضع علامة غير مقروء" + +#: templates/recruitment/notification_detail.html:65 +msgid "Topic:" +msgstr "الموضوع:" + +#: templates/recruitment/notification_detail.html:68 +#, fuzzy +#| msgid "Start Time" +msgid "Start Time:" +msgstr "وقت البدء:" + +#: templates/recruitment/notification_detail.html:75 +#, fuzzy +#| msgid "Join Meeting" +msgid "View Meeting" +msgstr "عرض الاجتماع" + +#: templates/recruitment/notification_detail.html:84 +#: templates/recruitment/notification_detail.html:175 +#, fuzzy +#| msgid "Scheduled" +msgid "Scheduled For" +msgstr "مجدول لـ" + +#: templates/recruitment/notification_detail.html:95 +#: templates/recruitment/notification_detail.html:182 +msgid "Delivery Attempts" +msgstr "محاولات التسليم" + +#: templates/recruitment/notification_detail.html:98 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "                                        This notification has been " +#| "attempted %(count)s time.\n" +#| "                                    " +#| msgid_plural "" +#| "\n" +#| "                                        This notification has been " +#| "attempted %(count)s times.\n" +#| "                                    " +msgid "" +"\n" +" This notification has been attempted " +"%(count)s time.\n" +" " +msgid_plural "" +"\n" +" This notification has been attempted " +"%(count)s times.\n" +" " +msgstr[0] "" +"\n" +"تمت محاولة إرسال هذا الإشعار مرة واحدة." +msgstr[1] "" +"\n" +"تمت محاولة إرسال هذا الإشعار %(count)s مرات." +msgstr[2] "" +"\n" +"تمت محاولة إرسال هذا الإشعار %(count)s مرات." +msgstr[3] "" +"\n" +"تمت محاولة إرسال هذا الإشعار %(count)s مرات." +msgstr[4] "" +"\n" +"تمت محاولة إرسال هذا الإشعار %(count)s مرات." +msgstr[5] "" +"\n" +"تمت محاولة إرسال هذا الإشعار %(count)s مرات." + +#: templates/recruitment/notification_detail.html:110 +#, fuzzy +#| msgid "Error" +msgid "Last Error" +msgstr "آخر خطأ" + +#: templates/recruitment/notification_detail.html:150 +#, fuzzy +#| msgid "Join Information" +msgid "Information" +msgstr "المعلومات" + +#: templates/recruitment/notification_list.html:15 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "                    %(count)s notification\n" +#| "                " +#| msgid_plural "" +#| "\n" +#| "                    %(count)s notifications\n" +#| "                " +msgid "" +"\n" +" %(count)s notification\n" +" " +msgid_plural "" +"\n" +" %(count)s notifications\n" +" " +msgstr[0] "" +"\n" +"إشعار واحد" +msgstr[1] "" +"\n" +"%(count)s إشعارات" +msgstr[2] "" +"\n" +"%(count)s إشعارات" +msgstr[3] "" +"\n" +"%(count)s إشعارات" +msgstr[4] "" +"\n" +"%(count)s إشعارات" +msgstr[5] "" +"\n" +"%(count)s إشعارات" + +#: templates/recruitment/notification_list.html:26 +msgid "Mark All Read" +msgstr "وضع علامة مقروء على الكل" + +#: templates/recruitment/notification_list.html:39 +#, fuzzy +#| msgid "Status" +msgid "All Status" +msgstr "جميع الحالات" + +#: templates/recruitment/notification_list.html:40 +#: templates/recruitment/notification_list.html:82 +msgid "Unread" +msgstr "غير مقروء" + +#: templates/recruitment/notification_list.html:48 +msgid "All Types" +msgstr "جميع الأنواع" + +#: templates/recruitment/notification_list.html:57 +msgid "Filter" +msgstr "تصفية" + +#: templates/recruitment/notification_list.html:74 +msgid "Total Notifications" +msgstr "إجمالي الإشعارات" + +#: templates/recruitment/notification_list.html:90 +msgid "Email Notifications" +msgstr "إشعارات البريد الإلكتروني" + +#: templates/recruitment/notification_list.html:122 +#, fuzzy +#| msgid "Create Zoom Meeting" +msgid "Related to meeting:" +msgstr "متعلق بالاجتماع:" + +#: templates/recruitment/notification_list.html:130 +msgid "Mark as read" +msgstr "وضع علامة مقروء" + +#: templates/recruitment/notification_list.html:136 +msgid "Mark as unread" +msgstr "وضع علامة غير مقروء" + +#: templates/recruitment/notification_list.html:142 +#, fuzzy +#| msgid "Delete Candidate" +msgid "Delete notification" +msgstr "حذف الإشعار" + +#: templates/recruitment/notification_list.html:155 +msgid "Notifications pagination" +msgstr "ترقيم صفحات الإشعارات" + +#: templates/recruitment/notification_list.html:190 +#, fuzzy +#| msgid "No candidates found." +msgid "No notifications found" +msgstr "لم يتم العثور على إشعارات" + +#: templates/recruitment/notification_list.html:193 +msgid "Try adjusting your filters to see more notifications." +msgstr "حاول تعديل عوامل التصفية الخاصة بك لرؤية المزيد من الإشعارات." + +#: templates/recruitment/notification_list.html:195 +#, fuzzy +#| msgid "You haven't created any form templates yet." +msgid "You don't have any notifications yet." +msgstr "ليس لديك أي إشعارات بعد." + +#: templates/recruitment/partials/_candidate_table.html:10 +msgid "Name / Contact" +msgstr "الاسم / جهة الاتصال" + +#: templates/recruitment/partials/_candidate_table.html:64 +msgid "View Details and Score Breakdown" +msgstr "عرض التفاصيل وتفصيل النقاط" + +#: templates/recruitment/partials/_candidate_table.html:75 +#, fuzzy +#| msgid "Create Candidate" +msgid "Mark as Potential Candidate" +msgstr "وضع علامة مرشح محتمل" + +#: templates/recruitment/partials/_candidate_table.html:83 +msgid "Move to Next Stage" +msgstr "الانتقال إلى المرحلة التالية" + +#: templates/recruitment/partials/_candidate_table.html:92 +msgid "Move to" +msgstr "الانتقال إلى" + +#: templates/recruitment/partials/_candidate_table.html:102 +#, fuzzy +#| msgid "Exam Status" +msgid "Update Exam Status" +msgstr "تحديث حالة الاختبار" + +#: templates/recruitment/partials/_candidate_table.html:120 +#, fuzzy +#| msgid "No candidates found." +msgid "No candidates found in this list." +msgstr "لم يتم العثور على مرشحين في هذه القائمة." + +#: templates/recruitment/partials/_candidate_table.html:122 +msgid "" +"Adjust your 'Top N' filter in the controls above or check the All Applicants " +"list." +msgstr "" +"عدّل عامل التصفية 'أفضل N' في عناصر التحكم أعلاه أو تحقق من قائمة جميع " +"المتقدمين." + +#: templates/recruitment/partials/_guage_chart.html:36 +#: templates/recruitment/partials/_guage_chart.html:50 +msgid "Days" +msgstr "أيام" + +#: templates/recruitment/partials/_guage_chart.html:50 +msgid "Target:" +msgstr "الهدف:" + +#: templates/recruitment/partials/_guage_chart.html:50 +msgid "Max Scale:" +msgstr "الحد الأقصى للمقياس:" + +#: templates/recruitment/partials/ai_overview_breadcromb.html:25 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_icon.html:7 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_icon.html:9 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_icon.html:11 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_logo.html:5 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_logo.html:7 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_logo.html:9 +msgid "Home" +msgstr "الرئيسية" + +#: templates/recruitment/partials/ai_overview_breadcromb.html:113 +#, fuzzy +#| msgid "Job Overview" +msgid "AI Overview" +msgstr "نظرة عامة بالذكاء الاصطناعي" + +#: templates/recruitment/partials/stats_cards.html:10 +#, fuzzy +#| msgid "Contact & Job" +msgid "Total Jobs" +msgstr "إجمالي الوظائف" + +#: templates/recruitment/partials/stats_cards.html:13 +msgid "All Active & Drafted Positions (Global)" +msgstr "جميع المناصب النشطة والمسودة (عالمي)" + +#: templates/recruitment/partials/stats_cards.html:19 +msgid "Active Jobs" +msgstr "الوظائف النشطة" + +#: templates/recruitment/partials/stats_cards.html:22 +msgid "Currently Open Requisitions (Scoped)" +msgstr "الطلبات المفتوحة حاليًا (نطاق محدد)" + +#: templates/recruitment/partials/stats_cards.html:31 +msgid "Total Profiles in Current Scope" +msgstr "إجمالي الملفات الشخصية في النطاق الحالي" + +#: templates/recruitment/partials/stats_cards.html:40 +msgid "Total Slots to be Filled (Scoped)" +msgstr "إجمالي الأماكن المراد شغلها (نطاق محدد)" + +#: templates/recruitment/partials/stats_cards.html:46 +msgid "Total Participants" +msgstr "إجمالي المشاركين" + +#: templates/recruitment/partials/stats_cards.html:49 +msgid "Total Recruiters/Interviewers (Global)" +msgstr "إجمالي موظفي التوظيف/المحاورين (عالمي)" + +#: templates/recruitment/partials/stats_cards.html:55 +#, fuzzy +#| msgid "LinkedIn Connected" +msgid "LinkedIn Posts" +msgstr "منشورات LinkedIn" + +#: templates/recruitment/partials/stats_cards.html:58 +msgid "Total Job Posts Sent to LinkedIn (Global)" +msgstr "إجمالي إعلانات الوظائف المرسلة إلى LinkedIn (عالمي)" + +#: templates/recruitment/partials/stats_cards.html:63 +msgid "New Apps (7 Days)" +msgstr "طلبات جديدة (7 أيام)" + +#: templates/recruitment/partials/stats_cards.html:66 +msgid "Incoming applications last week" +msgstr "الطلبات الواردة في الأسبوع الماضي" + +#: templates/recruitment/partials/stats_cards.html:71 +msgid "Avg. Apps per Job" +msgstr "متوسط الطلبات لكل وظيفة" + +#: templates/recruitment/partials/stats_cards.html:74 +msgid "Average Applications per Job (Scoped)" +msgstr "متوسط الطلبات لكل وظيفة (نطاق محدد)" + +#: templates/recruitment/partials/stats_cards.html:81 +#, fuzzy +#| msgid "Timezone" +msgid "Time-to-Hire" +msgstr "وقت التوظيف" + +#: templates/recruitment/partials/stats_cards.html:84 +msgid "Avg. Days (Application to Hired)" +msgstr "متوسط الأيام (من تقديم الطلب إلى التوظيف)" + +#: templates/recruitment/partials/stats_cards.html:89 +msgid "Avg. Match Score" +msgstr "متوسط نقاط التوافق" + +#: templates/recruitment/partials/stats_cards.html:92 +msgid "Average AI Score (Current Scope)" +msgstr "متوسط نقاط الذكاء الاصطناعي (النطاق الحالي)" + +#: templates/recruitment/partials/stats_cards.html:100 +#, fuzzy, python-format +#| msgid "Score ≥ 75% Profiles" +msgid "Score ≥ 75%% Profiles" +msgstr "الملفات الشخصية بنقاط ≥ 75%" + +#: templates/recruitment/partials/stats_cards.html:105 +#, fuzzy +#| msgid "Meetings" +msgid "Meetings This Week" +msgstr "اجتماعات هذا الأسبوع" + +#: templates/recruitment/partials/stats_cards.html:108 +msgid "Scheduled Interviews (Current Week)" +msgstr "المقابلات المجدولة (الأسبوع الحالي)" + +#: templates/recruitment/schedule_meeting_form.html:22 +msgid "" +"This candidate has upcoming interviews. You are updating an existing " +"schedule." +msgstr "هذا المرشح لديه مقابلات قادمة. أنت تقوم بتحديث جدول زمني موجود." + +#: templates/recruitment/schedule_meeting_form.html:27 +#, fuzzy +#| msgid "Candidates" +msgid "Back to Candidates" +msgstr "العودة إلى المرشحين" + +#: templates/recruitment/schedule_meeting_form.html:49 +msgid "" +"Default topic will be 'Interview: [Job Title] with [Candidate Name]' if left " +"empty." +msgstr "" +"سيكون الموضوع الافتراضي هو 'مقابلة: [عنوان الوظيفة] مع [اسم المرشح]' إذا تُرك " +"فارغًا." + +#: templates/recruitment/schedule_meeting_form.html:66 +msgid "Please select a date and time for the interview." +msgstr "الرجاء تحديد تاريخ ووقت للمقابلة." + +#: templates/recruitment/training_create.html:107 +msgid "Create New Training Material" +msgstr "إنشاء مادة تدريبية جديدة" + +#: templates/recruitment/training_create.html:109 +msgid "Upload a new document or guide for your team." +msgstr "رفع مستند أو دليل جديد لفريقك." + +#: templates/recruitment/training_create.html:125 +#: templates/recruitment/training_update.html:131 +msgid "Material Details" +msgstr "تفاصيل المادة" + +#: templates/recruitment/training_list.html:132 +msgid "Add New Material" +msgstr "إضافة مادة جديدة" + +#: templates/recruitment/training_list.html:141 +msgid "Search by Title or Creator" +msgstr "البحث حسب العنوان أو المنشئ" + +#: templates/recruitment/training_list.html:170 +#: templates/recruitment/training_list.html:205 +msgid "Created By" +msgstr "أنشأ بواسطة" + +#: templates/recruitment/training_list.html:171 +#, fuzzy +#| msgid "Created" +msgid "Created On" +msgstr "تاريخ الإنشاء" + +#: templates/recruitment/training_list.html:274 +#, fuzzy +#| msgid "No training materials found." +msgid "No training materials found" +msgstr "لم يتم العثور على مواد تدريبية" + +#: templates/recruitment/training_list.html:275 +msgid "It looks like there are no materials yet. Start by adding one!" +msgstr "يبدو أنه لا توجد مواد بعد. ابدأ بإضافة واحدة!" + +#: templates/recruitment/training_list.html:278 +msgid "Create Your First Material" +msgstr "إنشاء أول مادة لك" + +#: templates/recruitment/training_update.html:107 +msgid "Update Training Material:" +msgstr "تحديث المادة التدريبية:" + +#: templates/recruitment/training_update.html:109 +msgid "Edit the details of this training document or guide." +msgstr "تعديل تفاصيل هذا المستند التدريبي أو الدليل." + +#: templates/recruitment/training_update.html:117 +msgid "View Material" +msgstr "عرض المادة" + +#: templates/recruitment/training_update.html:180 +msgid "Update Material" +msgstr "تحديث المادة" + +#: templates/recruitment/training_update.html:182 +msgid "Are you sure you want to delete this material?" +msgstr "هل أنت متأكد من رغبتك في حذف هذه المادة؟" + +#: templates/unfold/components/table.html:43 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/components/table.html:89 +msgid "No data" +msgstr "لا توجد بيانات" + +#: templates/user/admin_settings.html:6 +#, fuzzy +#| msgid "Settings" +msgid "Admin Settings" +msgstr "إعدادات المسؤول" + +#: templates/user/admin_settings.html:149 +msgid "Admin Settings Dashboard" +msgstr "لوحة تحكم إعدادات المسؤول" + +#: templates/user/admin_settings.html:159 +msgid "Staff User List" +msgstr "قائمة مستخدمي الموظفين" + +#: templates/user/admin_settings.html:163 +#, fuzzy +#| msgid "Create New Form" +msgid "Create New User" +msgstr "إنشاء مستخدم جديد" + +#: templates/user/admin_settings.html:177 +#, fuzzy +#| msgid "First Name" +msgid "First Join" +msgstr "أول انضمام" + +#: templates/user/admin_settings.html:178 templates/user/profile.html:178 +msgid "Last Login" +msgstr "آخر تسجيل دخول" + +#: templates/user/admin_settings.html:231 +msgid "Deactivate User" +msgstr "تعطيل المستخدم" + +#: templates/user/admin_settings.html:238 +#, fuzzy +#| msgid "Active Jobs" +msgid "Activate User" +msgstr "تنشيط المستخدم" + +#: templates/user/admin_settings.html:239 +#, fuzzy +#| msgid "Active" +msgid "Activate" +msgstr "تنشيط" + +#: templates/user/admin_settings.html:248 +#, fuzzy +#| msgid "No training materials found." +msgid "No staff users found." +msgstr "لم يتم العثور على مستخدمي موظفين." + +#: templates/user/create_staff.html:6 templates/user/create_staff.html:37 +#: templates/user/create_staff.html:69 +#, fuzzy +#| msgid "Create Material" +msgid "Create Staff User" +msgstr "إنشاء مستخدم موظف" + +#: templates/user/create_staff.html:76 +#, fuzzy +#| msgid "Back to Meetings" +msgid "Back to Settings" +msgstr "العودة إلى الإعدادات" + +#: templates/user/profile.html:5 +#, fuzzy +#| msgid "Profile" +msgid "User Profile" +msgstr "الملف الشخصي للمستخدم" + +#: templates/user/profile.html:142 +msgid "Manage email addresses" +msgstr "إدارة عناوين البريد الإلكتروني" + +#: templates/user/profile.html:157 +msgid "Security" +msgstr "الأمان" + +#: templates/user/profile.html:164 +#, fuzzy +#| msgid "Candidate Profiles" +msgid "Change Profile Image" +msgstr "تغيير صورة الملف الشخصي" + +#: templates/user/profile.html:170 +#, fuzzy +#| msgid "Sync Status" +msgid "Account Status" +msgstr "حالة الحساب" + +#: templates/user/profile.html:173 +msgid "Username" +msgstr "اسم المستخدم" + +#: templates/user/profile.html:185 +msgid "Date Joined" +msgstr "تاريخ الانضمام" + +#: venv/lib/python3.13/site-packages/_pytest/config/argparsing.py:474 +#, python-format +msgid "ambiguous option: %(option)s could match %(matches)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/_termui_impl.py:608 +#, python-brace-format +msgid "{editor}: Editing failed" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/_termui_impl.py:612 +#, python-brace-format +msgid "{editor}: Editing failed: {e}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1104 +#: venv/lib/python3.13/site-packages/click/core.py:1141 +#, python-brace-format +msgid "{text} {deprecated_message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1160 +#: venv/lib/python3.13/site-packages/typer/core.py:633 +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:96 +#, fuzzy +#| msgid "Actions" +msgid "Options" +msgstr "الإجراءات" + +#: venv/lib/python3.13/site-packages/click/core.py:1222 +#, python-brace-format +msgid "Got unexpected extra argument ({args})" +msgid_plural "Got unexpected extra arguments ({args})" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/core.py:1241 +msgid "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1425 +#: venv/lib/python3.13/site-packages/typer/core.py:249 +msgid "Aborted!" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1799 +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:97 +msgid "Commands" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1830 +msgid "Missing command." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1908 +msgid "No such command {name!r}." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:2332 +msgid "Value must be an iterable." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:2355 +#, python-brace-format +msgid "Takes {nargs} values but 1 was given." +msgid_plural "Takes {nargs} values but {len} were given." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/core.py:2505 +msgid "" +"DeprecationWarning: The {param_type} {name!r} is deprecated.{extra_message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:2956 +#: venv/lib/python3.13/site-packages/typer/core.py:553 +#, python-brace-format +msgid "env var: {var}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:2959 +#: venv/lib/python3.13/site-packages/typer/core.py:366 +#: venv/lib/python3.13/site-packages/typer/core.py:574 +#, python-brace-format +msgid "default: {default}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:3023 +#: venv/lib/python3.13/site-packages/typer/core.py:114 +msgid "(dynamic)" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/decorators.py:465 +#, python-format +msgid "%(prog)s, version %(version)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/decorators.py:522 +msgid "Show the version and exit." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/decorators.py:548 +msgid "Show this message and exit." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:50 +#: venv/lib/python3.13/site-packages/click/exceptions.py:89 +#, fuzzy, python-brace-format +#| msgid "Error Message" +msgid "Error: {message}" +msgstr "رسالة الخطأ" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:81 +#, python-brace-format +msgid "Try '{command} {option}' for help." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:130 +#, python-brace-format +msgid "Invalid value: {message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:132 +#, python-brace-format +msgid "Invalid value for {param_hint}: {message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:190 +msgid "Missing argument" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:192 +#, fuzzy +#| msgid "Meeting Information" +msgid "Missing option" +msgstr "معلومات الاجتماع" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:194 +msgid "Missing parameter" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:196 +#, python-brace-format +msgid "Missing {param_type}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:203 +#, python-brace-format +msgid "Missing parameter: {param_name}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:223 +#, python-brace-format +msgid "No such option: {name}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:235 +#, python-brace-format +msgid "Did you mean {possibility}?" +msgid_plural "(Possible options: {possibilities})" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:282 +msgid "unknown error" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:289 +msgid "Could not open file {filename!r}: {message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/formatting.py:156 +#, fuzzy +#| msgid "Stage:" +msgid "Usage:" +msgstr "المرحلة:" + +#: venv/lib/python3.13/site-packages/click/parser.py:199 +msgid "Argument {name!r} takes {nargs} values." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/parser.py:381 +msgid "Option {name!r} does not take a value." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/parser.py:444 +msgid "Option {name!r} requires an argument." +msgid_plural "Option {name!r} requires {nargs} arguments." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/shell_completion.py:332 +msgid "Shell completion is not supported for Bash versions older than 4.4." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/shell_completion.py:339 +msgid "Couldn't detect Bash version, shell completion is not supported." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/termui.py:162 +#, fuzzy +#| msgid "Meeting Information" +msgid "Repeat for confirmation" +msgstr "معلومات الاجتماع" + +#: venv/lib/python3.13/site-packages/click/termui.py:178 +msgid "Error: The value you entered was invalid." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/termui.py:180 +#, fuzzy, python-brace-format +#| msgid "Error Message" +msgid "Error: {e.message}" +msgstr "رسالة الخطأ" + +#: venv/lib/python3.13/site-packages/click/termui.py:191 +msgid "Error: The two entered values do not match." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/termui.py:247 +msgid "Error: invalid input" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/termui.py:866 +msgid "Press any key to continue..." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:332 +#, python-brace-format +msgid "" +"Choose from:\n" +"\t{choices}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:369 +msgid "{value!r} is not {choice}." +msgid_plural "{value!r} is not one of {choices}." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/types.py:460 +msgid "{value!r} does not match the format {format}." +msgid_plural "{value!r} does not match the formats {formats}." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/types.py:482 +msgid "{value!r} is not a valid {number_type}." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:538 +#, python-brace-format +msgid "{value} is not in the range {range}." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:719 +msgid "{value!r} is not a valid boolean. Recognized values: {states}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:747 +msgid "{value!r} is not a valid UUID." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:937 +#, fuzzy +#| msgid "Profile" +msgid "file" +msgstr "الملف الشخصي" + +#: venv/lib/python3.13/site-packages/click/types.py:939 +msgid "directory" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:941 +msgid "path" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:988 +msgid "{name} {filename!r} does not exist." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:997 +msgid "{name} {filename!r} is a file." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1005 +msgid "{name} {filename!r} is a directory." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1014 +msgid "{name} {filename!r} is not readable." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1023 +msgid "{name} {filename!r} is not writable." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1032 +msgid "{name} {filename!r} is not executable." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1099 +#, python-brace-format +msgid "{len_type} values are required, but {len_value} was given." +msgid_plural "{len_type} values are required, but {len_value} were given." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/constructive_geometries/geomatcher.py:240 +msgid "RoW" +msgstr "" + +#: venv/lib/python3.13/site-packages/constructive_geometries/geomatcher.py:240 +#: venv/lib/python3.13/site-packages/constructive_geometries/geomatcher.py:243 +msgid "GLO" +msgstr "" + +#: venv/lib/python3.13/site-packages/constructive_geometries/geomatcher.py:243 +msgid "RoE" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/contrib/messages/apps.py:16 +#, fuzzy +#| msgid "Error Message" +msgid "Messages" +msgstr "رسالة الخطأ" + +#: venv/lib/python3.13/site-packages/django/contrib/sitemaps/apps.py:8 +msgid "Site Maps" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/contrib/staticfiles/apps.py:9 +msgid "Static Files" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/contrib/syndication/apps.py:7 +#, fuzzy +#| msgid "Application" +msgid "Syndication" +msgstr "التقديم" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +#: venv/lib/python3.13/site-packages/django/core/paginator.py:30 +msgid "…" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/paginator.py:32 +msgid "That page number is not an integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/paginator.py:33 +msgid "That page number is less than 1" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/paginator.py:34 +msgid "That page contains no results" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:22 +msgid "Enter a valid value." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:70 +#, fuzzy +#| msgid "Enter last name" +msgid "Enter a valid domain name." +msgstr "أدخل اسم العائلة" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:153 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:775 +msgid "Enter a valid URL." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:200 +msgid "Enter a valid integer." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:211 +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +#: venv/lib/python3.13/site-packages/django/core/validators.py:289 +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:297 +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:309 +#: venv/lib/python3.13/site-packages/django/core/validators.py:318 +#: venv/lib/python3.13/site-packages/django/core/validators.py:332 +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2220 +#, python-format +msgid "Enter a valid %(protocol)s address." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:311 +msgid "IPv4" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:320 +#: venv/lib/python3.13/site-packages/django/utils/ipv6.py:43 +msgid "IPv6" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:334 +msgid "IPv4 or IPv6" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:375 +msgid "Enter only digits separated by commas." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:381 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:416 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:425 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:434 +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:441 +#, python-format +msgid "" +"Ensure this value is a multiple of step size %(limit_value)s, starting from " +"%(offset)s, e.g. %(offset)s, %(valid_value1)s, %(valid_value2)s, and so on." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:473 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:491 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:514 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:366 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:405 +#, fuzzy +#| msgid "Enter phone number" +msgid "Enter a number." +msgstr "أدخل رقم الهاتف" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:516 +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:521 +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:526 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:597 +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:659 +msgid "Null characters are not allowed." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/base.py:1600 +#: venv/lib/python3.13/site-packages/django/forms/models.py:908 +#: venv/lib/python3.13/site-packages/unfold/contrib/inlines/admin.py:108 +msgid "and" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/base.py:1602 +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/constraints.py:22 +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:134 +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:135 +msgid "This field cannot be null." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:136 +msgid "This field cannot be blank." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:137 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:141 +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:180 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1162 +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1163 +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1165 +msgid "Boolean (Either True or False)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1215 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1217 +msgid "String (unlimited)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1326 +#, fuzzy +#| msgid "Comma-separated list of trusted IP addresses" +msgid "Comma-separated integers" +msgstr "قائمة عناوين IP الموثوقة مفصولة بفواصل" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1427 +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1431 +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1566 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1435 +msgid "Date (without time)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1562 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD " +"HH:MM[:ss[.uuuuuu]][TZ] format." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1570 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1575 +msgid "Date (with time)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1702 +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1704 +msgid "Decimal number" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1864 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] " +"[[HH:]MM:]ss[.uuuuuu] format." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1920 +#, fuzzy +#| msgid "IP Address" +msgid "Email address" +msgstr "عنوان IP" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1945 +#, fuzzy +#| msgid "File" +msgid "File path" +msgstr "ملف" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2023 +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2025 +#, fuzzy +#| msgid "Enter phone number" +msgid "Floating point number" +msgstr "أدخل رقم الهاتف" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2065 +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2067 +#, fuzzy +#| msgid "Interview" +msgid "Integer" +msgstr "المقابلة" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2163 +msgid "Big (8 byte) integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2180 +msgid "Small integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2188 +#, fuzzy +#| msgid "IP Address" +msgid "IPv4 address" +msgstr "عنوان IP" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2219 +#, fuzzy +#| msgid "IP Address" +msgid "IP address" +msgstr "عنوان IP" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2310 +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2311 +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2313 +msgid "Boolean (Either True, False or None)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2364 +msgid "Positive big integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2379 +msgid "Positive integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2394 +msgid "Positive small integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2410 +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2446 +msgid "Text" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2526 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2530 +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2534 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:263 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:279 +#: venv/lib/python3.13/site-packages/unfold/widgets.py:599 +#: venv/lib/python3.13/site-packages/unfold/widgets.py:644 +#, fuzzy +#| msgid "Timezone" +msgid "Time" +msgstr "المنطقة الزمنية" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2642 +msgid "URL" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2666 +msgid "Raw binary data" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2731 +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2733 +msgid "Universally unique identifier" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/files.py:420 +msgid "Image" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/json.py:24 +msgid "A JSON object" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/json.py:26 +msgid "Value must be valid JSON." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:979 +#, python-format +msgid "%(model)s instance with %(field)s %(value)r is not a valid choice." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:982 +msgid "Foreign Key (type determined by related field)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:1276 +msgid "One-to-one relationship" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:1333 +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:1335 +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:1383 +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the label +#: venv/lib/python3.13/site-packages/django/forms/boundfield.py:185 +msgid ":?.!" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:95 +msgid "This field is required." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:315 +#, fuzzy +#| msgid "Enter phone number" +msgid "Enter a whole number." +msgstr "أدخل رقم الهاتف" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:486 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1267 +#, fuzzy +#| msgid "Interview Date" +msgid "Enter a valid date." +msgstr "تاريخ المقابلة" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:509 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1268 +#, fuzzy +#| msgid "Enter last name" +msgid "Enter a valid time." +msgstr "أدخل اسم العائلة" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:536 +msgid "Enter a valid date/time." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:570 +#, fuzzy +#| msgid "Internal Information" +msgid "Enter a valid duration." +msgstr "المعلومات الداخلية" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:571 +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:640 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:641 +msgid "No file was submitted." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:642 +msgid "The submitted file is empty." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:644 +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:649 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:717 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:889 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:975 +#: venv/lib/python3.13/site-packages/django/forms/models.py:1592 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:977 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1096 +#: venv/lib/python3.13/site-packages/django/forms/models.py:1590 +#, fuzzy +#| msgid "Enter last name" +msgid "Enter a list of values." +msgstr "أدخل اسم العائلة" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1097 +#, fuzzy +#| msgid "Enter template name" +msgid "Enter a complete value." +msgstr "أدخل اسم القالب" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1339 +msgid "Enter a valid UUID." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1369 +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +#: venv/lib/python3.13/site-packages/django/forms/forms.py:97 +msgid ":" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/forms.py:239 +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:61 +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:65 +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:70 +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:484 +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:491 +msgid "Order" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:901 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:906 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:913 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:922 +msgid "Please correct the duplicate values below." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:1359 +msgid "The inline value did not match the parent instance." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:1450 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:1594 +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/utils.py:229 +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:528 +msgid "Currently" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:529 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/edit_inline/stacked.html:63 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:42 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_title.html:23 +#, fuzzy +#| msgid "Change Stage" +msgid "Change" +msgstr "تغيير المرحلة" + +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:866 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/boolean.html:4 +msgid "Unknown" +msgstr "" + +#. Translators: Please do not add spaces around commas. +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:873 +msgid "yes,no,maybe" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:903 +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:920 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:922 +#, python-format +msgid "%s KB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:924 +#, python-format +msgid "%s MB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:926 +#, python-format +msgid "%s GB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:928 +#, python-format +msgid "%s TB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:930 +#, python-format +msgid "%s PB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:74 +msgid "p.m." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:75 +msgid "a.m." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:80 +msgid "PM" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:81 +msgid "AM" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:153 +msgid "midnight" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:155 +msgid "noon" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:7 +msgid "Monday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:8 +msgid "Tuesday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:9 +msgid "Wednesday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:10 +msgid "Thursday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:11 +msgid "Friday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:12 +msgid "Saturday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:13 +msgid "Sunday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:16 +msgid "Mon" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:17 +msgid "Tue" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:18 +msgid "Wed" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:19 +msgid "Thu" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:20 +msgid "Fri" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:21 +#, fuzzy +#| msgid "Status" +msgid "Sat" +msgstr "الحالة" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:22 +msgid "Sun" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:25 +msgid "January" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:26 +msgid "February" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:27 +#, fuzzy +#| msgid "Search" +msgid "March" +msgstr "بحث" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:28 +msgid "April" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:29 +msgid "May" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:30 +msgid "June" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:31 +msgid "July" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:32 +msgid "August" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:33 +msgid "September" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:34 +msgid "October" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:35 +msgid "November" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:36 +msgid "December" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:39 +msgid "jan" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:40 +msgid "feb" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:41 +#, fuzzy +#| msgid "Summary" +msgid "mar" +msgstr "الملخص" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:42 +msgid "apr" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:43 +#, fuzzy +#| msgid "Summary" +msgid "may" +msgstr "الملخص" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:44 +msgid "jun" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:45 +msgid "jul" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:46 +#, fuzzy +#| msgid "ago" +msgid "aug" +msgstr "منذ" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:47 +msgid "sep" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:48 +msgid "oct" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:49 +msgid "nov" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:50 +msgid "dec" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:53 +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:54 +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:55 +#, fuzzy +#| msgid "Search" +msgctxt "abbrev. month" +msgid "March" +msgstr "بحث" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:56 +msgctxt "abbrev. month" +msgid "April" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:57 +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:58 +msgctxt "abbrev. month" +msgid "June" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:59 +msgctxt "abbrev. month" +msgid "July" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:60 +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:61 +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:62 +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:63 +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:64 +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:67 +msgctxt "alt. month" +msgid "January" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:68 +msgctxt "alt. month" +msgid "February" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:69 +#, fuzzy +#| msgid "Search" +msgctxt "alt. month" +msgid "March" +msgstr "بحث" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:70 +msgctxt "alt. month" +msgid "April" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:71 +msgctxt "alt. month" +msgid "May" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:72 +msgctxt "alt. month" +msgid "June" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:73 +msgctxt "alt. month" +msgid "July" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:74 +msgctxt "alt. month" +msgid "August" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:75 +msgctxt "alt. month" +msgid "September" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:76 +msgctxt "alt. month" +msgid "October" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:77 +msgctxt "alt. month" +msgid "November" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:78 +msgctxt "alt. month" +msgid "December" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/ipv6.py:20 +msgid "This is not a valid IPv6 address." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/text.py:76 +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/text.py:287 +#, fuzzy +#| msgid "More" +msgid "or" +msgstr "المزيد" + +#. Translators: This string is used as a separator between list elements +#: venv/lib/python3.13/site-packages/django/utils/text.py:306 +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:135 +msgid ", " +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:8 +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:9 +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:10 +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:11 +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:12 +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:13 +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:29 +msgid "Forbidden" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:30 +msgid "CSRF verification failed. Request aborted." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:34 +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:40 +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:45 +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:54 +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:60 +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:66 +msgid "More information is available with DEBUG=True." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:44 +msgid "No year specified" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:64 +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:115 +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:214 +msgid "Date out of range" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:94 +msgid "No month specified" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:147 +msgid "No day specified" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:194 +msgid "No week specified" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:353 +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:384 +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:680 +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because " +"%(class_name)s.allow_future is False." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:720 +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/detail.py:56 +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/list.py:70 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/list.py:77 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/list.py:173 +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/static.py:49 +msgid "Directory indexes are not allowed here." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/static.py:51 +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/static.py:68 +#: venv/lib/python3.13/site-packages/django/views/templates/directory_index.html:8 +#: venv/lib/python3.13/site-packages/django/views/templates/directory_index.html:11 +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:7 +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:204 +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:206 +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:208 +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:217 +msgid "Django Documentation" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:218 +msgid "Topics, references, & how-to’s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:226 +msgid "Tutorial: A Polling App" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:227 +msgid "Get started with Django" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:235 +msgid "Django Community" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:236 +msgid "Connect, get help, or contribute" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/permissions.py:18 +msgid "You do not have permission to upload files." +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/permissions.py:25 +msgid "You must be logged in to upload files." +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/validators.py:17 +#, python-format +msgid "File should be at most %(max_size)s MB." +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/views.py:41 +msgid "Invalid form data" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/widgets.py:43 +msgid "Check the correct settings.CKEDITOR_5_CONFIGS " +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:72 +msgid "Only POST method is allowed" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:86 +msgid "Attachment module is disabled" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:93 +msgid "Only authenticated users are allowed" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:99 +msgid "No files were requested" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:140 +msgid "File size exceeds the limit allowed and cannot be saved" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:160 +msgid "Failed to save attachment" +msgstr "" + +#: venv/lib/python3.13/site-packages/isort/main.py:158 +msgid "show this help message and exit" +msgstr "" + +#: venv/lib/python3.13/site-packages/kombu/transport/qpid.py:1311 +#, python-format +msgid "Attempting to connect to qpid with SASL mechanism %s" +msgstr "" + +#: venv/lib/python3.13/site-packages/kombu/transport/qpid.py:1316 +#, python-format +msgid "Connected to qpid with SASL mechanism %s" +msgstr "" + +#: venv/lib/python3.13/site-packages/kombu/transport/qpid.py:1334 +#, python-format +msgid "Unable to connect to qpid with SASL mechanism %s" +msgstr "" + +#: venv/lib/python3.13/site-packages/pycountry/tests/test_general.py:184 +msgid "Germany" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/core.py:368 +#: venv/lib/python3.13/site-packages/typer/core.py:583 +msgid "required" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/core.py:630 +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:95 +msgid "Arguments" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:89 +#, fuzzy +#| msgid "Created at" +msgid "(deprecated) " +msgstr "تم الإنشاء في" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:90 +msgid "[default: {}]" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:91 +msgid "[env var: {}]" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:93 +msgid "[required]" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:99 +msgid "Aborted." +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:100 +#, python-brace-format +msgid "Try [blue]'{command_path} {help_option}'[/] for help." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/admin.py:40 +#: venv/lib/python3.13/site-packages/unfold/templatetags/unfold_list.py:337 +#, fuzzy +#| msgid "Select country" +msgid "Select record" +msgstr "اختر الدولة" + +#: venv/lib/python3.13/site-packages/unfold/admin.py:166 +#, fuzzy +#| msgid "Select country" +msgid "Select action" +msgstr "اختر الدولة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:10 +msgid "Collapse" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:20 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:172 +msgid "Value" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:21 +msgid "Default" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:22 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:59 +msgid "Code" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:23 +msgid "Modified" +msgstr "تم التعديل" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:65 +msgid "Reset to default" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:46 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:103 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:137 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:168 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/dropdown_filters.py:28 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/dropdown_filters.py:61 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/dropdown_filters.py:105 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/mixins.py:71 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/mixins.py:169 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/text_filters.py:29 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/text_filters.py:60 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_date_range.html:5 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html:5 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_numeric_range.html:5 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_numeric_single.html:5 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html:7 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/filter.html:5 +#, python-format +msgid " By %(filter_title)s " +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:193 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:234 +msgid "To" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:258 +msgid "Date from" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:274 +msgid "Date to" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html:30 +#, fuzzy +#| msgid "No data" +msgid "Not enough data." +msgstr "لا توجد بيانات" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/array.html:30 +#, fuzzy +#| msgid "Add New Candidate" +msgid "Add new item" +msgstr "إضافة مرشح جديد" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:7 +msgid "Paragraph" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:11 +msgid "Underlined" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:15 +msgid "Bold" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:19 +msgid "Italic" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:23 +msgid "Strike" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:35 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:39 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:43 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:47 +msgid "Heading" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:55 +msgid "Quote" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:63 +msgid "Unordered list" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:67 +msgid "Ordered list" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:71 +msgid "Indent increase" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:75 +msgid "Indent decrease" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:83 +msgid "Undo" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:87 +msgid "Redo" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:95 +#, fuzzy +#| msgid "Enter email" +msgid "Enter an URL" +msgstr "أدخل البريد الإلكتروني" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:102 +msgid "Unlink" +msgstr "فك الربط" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/change_form.html:8 +msgid "Object permissions" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html:13 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html:14 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:9 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:43 +msgid "Object" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html:16 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:15 +msgid "Group" +msgstr "تجميع" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:7 +msgid "Group permissions" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:67 +#, fuzzy +#| msgid "Manage" +msgid "Manage group" +msgstr "إدارة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:7 +msgid "User permissions" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:67 +#, fuzzy +#| msgid "Manage" +msgid "Manage user" +msgstr "إدارة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/change_form.html:8 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/change_list_export_item.html:4 +msgid "Export" +msgstr "تصدير" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/change_list_import_item.html:4 +msgid "Import" +msgstr "استيراد" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/export.html:18 +#, python-format +msgid "" +"\n" +" Export %(len)s selected item.\n" +" " +msgid_plural "" +"\n" +" Export %(len)s selected items.\n" +" " +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/export.html:37 +msgid "This exporter will export the following fields" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_confirm.html:10 +msgid "" +"Below is a preview of data to be imported. If you are satisfied with the " +"results, click 'Confirm import'" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_confirm.html:15 +#, fuzzy +#| msgid "Confirm Delete" +msgid "Confirm import" +msgstr "تأكيد الحذف" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_errors.html:20 +#, fuzzy +#| msgid "Enter phone number" +msgid "Line number" +msgstr "أدخل رقم الهاتف" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_preview.html:24 +msgid "Skipped" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:6 +msgid "Some rows failed to validate" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:10 +msgid "" +"Please correct these errors in your data where possible, then reupload it " +"using the form above." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:22 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:40 +msgid "Row" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:26 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:44 +#, fuzzy +#| msgid "Error" +msgid "Errors" +msgstr "خطأ" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:70 +msgid "Non field specific" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html:6 +msgid "This exporter will export the following fields: " +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html:8 +msgid "This importer will import the following fields: " +msgstr "" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#: venv/lib/python3.13/site-packages/unfold/contrib/inlines/admin.py:99 +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/inlines/admin.py:111 +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history.html:8 +msgid "" +"Choose a date from the list below to revert to a previous version of this " +"object." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history.html:28 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:57 +#, fuzzy +#| msgid "Country" +msgid "entry" +msgid_plural "entries" +msgstr[0] "الدولة" +msgstr[1] "الدولة" +msgstr[2] "الدولة" +msgstr[3] "الدولة" +msgstr[4] "الدولة" +msgstr[5] "الدولة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history.html:32 +msgid "This object doesn't have a change history." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_form.html:16 +msgid "" +"Press the 'Revert' button below to revert to this version of the object." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_form.html:20 +msgid "Press the 'Change History' button below to edit the history." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:19 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:55 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:10 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:26 +msgid "Date/time" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:27 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:63 +#, fuzzy +#| msgid "Created by" +msgid "Changed by" +msgstr "أنشأ بواسطة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:31 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:77 +#, fuzzy +#| msgid "Change Stage" +msgid "Change reason" +msgstr "تغيير المرحلة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:73 +msgid "None" +msgstr "لا شيء" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/submit_line.html:8 +msgid "Revert" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/submit_line.html:14 +#, fuzzy +#| msgid "Change Stage" +msgid "Change History" +msgstr "تغيير المرحلة" + +#: venv/lib/python3.13/site-packages/unfold/forms.py:71 +#, fuzzy +#| msgid "Select country" +msgid "Select action to run" +msgstr "اختر الدولة" + +#: venv/lib/python3.13/site-packages/unfold/forms.py:129 +msgid "" +"Raw passwords are not stored, so there is no way to see this user’s " +"password, but you can change the password using this form." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/mixins/base_model_admin.py:57 +#: venv/lib/python3.13/site-packages/unfold/mixins/base_model_admin.py:78 +#, fuzzy +#| msgid "Select country" +msgid "Select value" +msgstr "اختر الدولة" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:22 +msgid "Run the selected action" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:23 +msgid "Run" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:43 +msgid "Click here to select the objects across all pages" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:44 +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:50 +msgid "Clear selection" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/app_list.html:12 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:9 +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/app_list.html:48 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list.html:99 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:59 +msgid "You don’t have permission to view or edit anything." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/auth/user/add_form.html:6 +msgid "After you've created a user, you’ll be able to edit more user options." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/auth/user/change_password.html:19 +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/auth/user/change_password.html:30 +#: venv/lib/python3.13/site-packages/unfold/templates/registration/password_change_form.html:29 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/account_links.html:30 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "كلمة المرور" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_form_object_tools.html:6 +msgid "History" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_form_object_tools.html:12 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/edit_inline/stacked.html:77 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_title.html:33 +#, fuzzy +#| msgid "View on LinkedIn" +msgid "View on site" +msgstr "عرض على LinkedIn" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list.html:69 +msgid "Filters" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:32 +msgid "Select all rows" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:46 +#, fuzzy +#| msgid "Toggle user menu" +msgid "Toggle sorting" +msgstr "تبديل قائمة المستخدم" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:54 +msgid "Remove from sorting" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:60 +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:85 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/components/table.html:34 +msgid "Expand row" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_confirmation.html:16 +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_confirmation.html:32 +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_confirmation.html:48 +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_confirmation.html:55 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_selected_confirmation.html:49 +msgid "Objects" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_selected_confirmation.html:15 +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_selected_confirmation.html:28 +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_selected_confirmation.html:42 +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/includes/object_delete_summary.html:5 +msgid "Summary" +msgstr "الملخص" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:15 +msgid "Welcome back to" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:26 +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:47 +msgid "Log in" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:55 +msgid "Forgotten your password or username?" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:60 +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/pagination.html:12 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/popup_header.html:14 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/welcomemsg.html:25 +msgid "Show all" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/search_form.html:18 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command.html:24 +msgid "Type to search" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:28 +msgid "Save and continue editing" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:30 +msgid "Save and view" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:37 +msgid "Save and add another" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:43 +msgid "Save as new" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/logged_out.html:14 +msgid "You have been successfully logged out from the administration" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/logged_out.html:18 +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/logged_out.html:23 +msgid "Log in again" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/password_change_done.html:9 +msgid "Your password was changed." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/password_change_form.html:18 +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/account_links.html:17 +#, fuzzy +#| msgid "View Candidate" +msgid "View site" +msgstr "عرض المرشح" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/account_links.html:40 +#, fuzzy +#| msgid "Sign out" +msgid "Log out" +msgstr "تسجيل الخروج" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/actions_row.html:4 +#, fuzzy +#| msgid "Actions" +msgid "More actions" +msgstr "الإجراءات" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/add_link.html:5 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/add_link.html:8 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/empty_results.html:4 +#, python-format +msgid "Add %(name)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list.html:68 +#, fuzzy +#| msgid "Applications" +msgid "All applications" +msgstr "التقديمات" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:31 +msgid "Add" +msgstr "إضافة" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/boolean.html:4 +msgid "True" +msgstr "صحيح" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/boolean.html:4 +msgid "False" +msgstr "خطأ" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/change_list_filter_actions.html:16 +msgid "Hide counts" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/change_list_filter_actions.html:20 +#, fuzzy +#| msgid "Your account" +msgid "Show counts" +msgstr "حسابك" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/change_list_filter_actions.html:27 +msgid "Clear all filters" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_history.html:7 +msgid "Recent searches" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_history.html:49 +msgid "No recent searches" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_results.html:44 +#, fuzzy +#| msgid "No templates match your search \"%(query)s\"." +msgid "No results matching your query" +msgstr "لا توجد قوالب تطابق بحثك \"%(query)s\"." + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_results.html:57 +msgid "Loading more results..." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_results.html:62 +#, python-format +msgid "" +"\n" +" Found %(counter)s result in %(time)s seconds\n" +" " +msgid_plural "" +"\n" +" Found %(counter)s results in %(time)s seconds\n" +" " +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/delete_submit_line.html:5 +msgid "No, take me back" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/delete_submit_line.html:9 +msgid "Yes, I’m sure" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/display_header.html:10 +msgid "Record picture" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_heading.html:21 +#, fuzzy +#| msgid "Delete" +msgid "Delete?" +msgstr "حذف" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/empty_results.html:12 +#, fuzzy +#| msgid "No meetings found." +msgid "No results found" +msgstr "لم يتم العثور على اجتماعات." + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/empty_results.html:16 +msgid "" +"This page yielded into no results. Create a new item or reset your filters." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/empty_results.html:30 +msgid "Reset filters" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/header_back_button.html:7 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/header_back_button.html:15 +msgid "Go back" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/history.html:9 +#, fuzzy +#| msgid "Management Actions" +msgid "Recent actions" +msgstr "إجراءات الإدارة" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/history.html:28 +msgid "Unknown content" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/messages/errornote.html:5 +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/popup_header.html:14 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/welcomemsg.html:25 +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/popup_header.html:14 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/welcomemsg.html:25 +#, python-format +msgid "%(full_result_count)s total" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/search.html:10 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/search.html:40 +#, fuzzy +#| msgid "Search templates by name..." +msgid "Search apps and models..." +msgstr "البحث عن القوالب بالاسم..." + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/search.html:41 +#, fuzzy +#| msgid "Toggle navigation" +msgid "Filter navigation items" +msgstr "تبديل التنقل" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_branding.html:3 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_branding.html:6 +msgid "Django administration" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/tab_items.html:15 +msgid "General" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/theme_switch.html:16 +msgid "Dark" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/theme_switch.html:23 +msgid "Light" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/theme_switch.html:30 +msgid "System" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/unauthenticated_header.html:6 +msgid "Return to site" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/clearable_file_input.html:6 +#, fuzzy +#| msgid "Interview" +msgid "Image preview" +msgstr "المقابلة" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/clearable_file_input.html:24 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/clearable_file_input_small.html:17 +msgid "Choose file to upload" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/related_widget_wrapper.html:16 +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/related_widget_wrapper.html:26 +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/related_widget_wrapper.html:35 +#, python-format +msgid "View selected %(model)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/related_widget_wrapper.html:45 +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold_crispy/layout/table_inline_formset.html:65 +msgid "Add row" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templatetags/unfold.py:770 +msgid "Welcome" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templatetags/unfold_list.py:118 +msgid "Select all objects on this page for an action" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/widgets.py:821 +#, fuzzy +#| msgid "Select country" +msgid "Select currency" +msgstr "اختر الدولة" + +#~ msgid "Password for accessing the portal (optional)" +#~ msgstr "كلمة المرور للوصول إلى البوابة (اختياري)" + +#, python-format +#~ msgid "" +#~ "Cannot transition from \"%(current)s\" to \"%(new)s\". Allowed " +#~ "transitions: %(allowed)s" +#~ msgstr "" +#~ "لا يمكن الانتقال من \"%(current)s\" إلى \"%(new)s\". الانتقالات المسموح " +#~ "بها: %(allowed)s" + +#~ msgid "Enter title" +#~ msgstr "أدخل العنوان" + +#~ msgid "Save Material" +#~ msgstr "حفظ المادة" + +#~ msgid "Help & Support" +#~ msgstr "المساعدة والدعم" + +#~ msgid "Application URL" +#~ msgstr "رابط التقديم" + +#~ msgid "Full URL where candidates will apply" +#~ msgstr "الرابط الكامل حيث سيقدم المرشحون" + +#~ msgid "Desired Start Date" +#~ msgstr "تاريخ البدء المطلوب" + +#~ msgid "Post Reach Field" +#~ msgstr "حقل وصول المنشور" + +#~ msgid "Hashtags" +#~ msgstr "الوسوم" + +#~ msgid "Start Date:" +#~ msgstr "تاريخ البدء:" + +#~ msgid "Info" +#~ msgstr "معلومات" + +#~ msgid "View All Existing Forms" +#~ msgstr "عرض جميع النماذج الموجودة" + +#~ msgid "Reports To:" +#~ msgstr "يقدم تقاريره إلى:" + +#~ msgid "Host Video" +#~ msgstr "فيديو المضيف" +>>>>>>> update1 #~ msgid "Are you sure?" #~ msgstr "هل أنت متأكد؟" +<<<<<<< HEAD #~ msgid "Cancel Transfer" #~ msgstr "إلغاء النقل" @@ -17181,3 +26190,31 @@ msgstr "تاريخ الإبطال" #~ msgid "show this help message and exit" #~ msgstr "إظهار رسالة المساعدة هذه والخروج" +======= +#~ msgid "Zoom API Response" +#~ msgstr "استجابة واجهة برمجة تطبيقات Zoom" + +#~ msgid "Start Time (ISO 8601):" +#~ msgstr "وقت البدء (ISO 8601):" + +#~ msgid "Duration (minutes):" +#~ msgstr "المدة (دقائق):" + +#~ msgid "Resume Document" +#~ msgstr "وثيقة السيرة الذاتية" + +#~ msgid "Parsed Data" +#~ msgstr "البيانات المحللة" + +#~ msgid "Activity" +#~ msgstr "النشاط" + +#~ msgid "Structured Resume Data" +#~ msgstr "بيانات السيرة الذاتية المنظمة" + +#~ msgid "" +#~ "Activity feed (e.g., stage changes, notes, interview history) will appear " +#~ "here." +#~ msgstr "" +#~ "سجل النشاط (مثلاً، تغييرات المرحلة، ملاحظات، سجل المقابلات) سيظهر هنا." +>>>>>>> update1 diff --git a/locale/ar/LC_MESSAGES/django.po.backup b/locale/ar/LC_MESSAGES/django.po.backup new file mode 100644 index 0000000..bcc2efe --- /dev/null +++ b/locale/ar/LC_MESSAGES/django.po.backup @@ -0,0 +1,9035 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-11-02 21:11+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#: recruitment/forms.py:240 recruitment/forms.py:1063 recruitment/models.py:424 +#: templates/recruitment/agency_portal_assignment_detail.html:539 +#: templates/recruitment/agency_portal_submit_candidate.html:187 +#: templates/user/profile.html:129 +msgid "First Name" +msgstr "الاسم الأول" + +#: recruitment/forms.py:241 recruitment/forms.py:1064 recruitment/models.py:425 +#: templates/recruitment/agency_portal_assignment_detail.html:545 +#: templates/recruitment/agency_portal_submit_candidate.html:198 +#: templates/user/profile.html:134 +msgid "Last Name" +msgstr "اسم العائلة" + +#: recruitment/forms.py:242 recruitment/models.py:427 +#: templates/jobs/job_candidates_list.html:228 +#: templates/jobs/job_candidates_list.html:340 +#: templates/participants/participants_list.html:214 +#: templates/recruitment/agency_confirm_delete.html:253 +#: templates/recruitment/agency_detail.html:289 +#: templates/recruitment/agency_list.html:187 +#: templates/recruitment/agency_portal_assignment_detail.html:560 +msgid "Phone" +msgstr "رقم الهاتف" + +#: recruitment/forms.py:243 recruitment/models.py:426 +#: recruitment/models.py:1660 recruitment/models.py:1734 +#: templates/jobs/job_candidates_list.html:227 +#: templates/meetings/meeting_details.html:305 +#: templates/participants/participants_list.html:213 +#: templates/recruitment/agency_confirm_delete.html:241 +#: templates/recruitment/agency_detail.html:266 +#: templates/recruitment/agency_detail.html:301 +#: templates/recruitment/agency_list.html:186 +#: templates/recruitment/agency_portal_assignment_detail.html:554 +#: templates/recruitment/candidate_detail.html:337 +#: templates/recruitment/candidate_list.html:270 +#: templates/recruitment/notification_list.html:50 +#: templates/user/admin_settings.html:175 +msgid "Email" +msgstr "البريد الإلكتروني" + +#: recruitment/forms.py:244 recruitment/forms.py:1067 recruitment/models.py:429 +msgid "Resume" +msgstr "السيرة الذاتية" + +#: recruitment/forms.py:245 +#, fuzzy +#| msgid "Hiring Agency" +msgid "Hiring Type" +msgstr "وكالة التوظيف" + +#: recruitment/forms.py:246 recruitment/models.py:180 recruitment/models.py:501 +#: recruitment/models.py:1287 +msgid "Hiring Agency" +msgstr "وكالة التوظيف" + +#: recruitment/forms.py:249 +#: templates/recruitment/agency_portal_submit_candidate.html:194 +msgid "Enter first name" +msgstr "أدخل الاسم الأول" + +#: recruitment/forms.py:250 +#: templates/recruitment/agency_portal_submit_candidate.html:205 +msgid "Enter last name" +msgstr "أدخل اسم العائلة" + +#: recruitment/forms.py:251 +#: templates/recruitment/agency_portal_submit_candidate.html:237 +msgid "Enter phone number" +msgstr "أدخل رقم الهاتف" + +#: recruitment/forms.py:252 +msgid "Enter email" +msgstr "أدخل البريد الإلكتروني" + +#: recruitment/forms.py:281 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/export.html:56 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_form.html:21 +msgid "Submit" +msgstr "إرسال" + +#: recruitment/forms.py:292 +msgid "New Application Stage" +msgstr "مرحلة تقديم جديدة" + +#: recruitment/forms.py:303 recruitment/models.py:713 +#: templates/includes/meeting_form.html:10 +#: templates/meetings/create_meeting.html:162 +#: templates/meetings/list_meetings.html:306 +#: templates/meetings/update_meeting.html:215 +#: templates/recruitment/candidate_interview_view.html:263 +msgid "Topic" +msgstr "الموضوع" + +#: recruitment/forms.py:304 recruitment/models.py:717 +#: recruitment/models.py:1564 recruitment/models.py:1583 +#: templates/interviews/schedule_interviews.html:169 +#: templates/interviews/schedule_interviews.html:201 +#: templates/meetings/create_meeting.html:166 +#: templates/meetings/list_meetings.html:310 +#: templates/meetings/reschedule_meeting.html:39 +#: templates/meetings/schedule_meeting_form.html:46 +#: templates/meetings/update_meeting.html:219 +#: templates/recruitment/schedule_meeting_form.html:55 +msgid "Start Time" +msgstr "وقت البدء" + +#: recruitment/forms.py:305 recruitment/models.py:719 +#: templates/meetings/list_meetings.html:266 +#: templates/meetings/list_meetings.html:311 +#: templates/meetings/meeting_details.html:265 +#: templates/recruitment/candidate_interview_view.html:264 +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1868 +msgid "Duration" +msgstr "المدة" + +#: recruitment/forms.py:308 +msgid "Enter meeting topic" +msgstr "أدخل موضوع الاجتماع" + +#: recruitment/forms.py:310 +msgid "60" +msgstr "60" + +#: recruitment/forms.py:324 templates/meetings/create_meeting.html:180 +#: templates/meetings/list_meetings.html:179 +msgid "Create Meeting" +msgstr "إنشاء اجتماع" + +#: recruitment/forms.py:332 recruitment/models.py:688 +#: templates/recruitment/training_list.html:204 +#: templates/recruitment/training_update.html:144 +msgid "Title" +msgstr "العنوان" + +#: recruitment/forms.py:333 recruitment/models.py:689 recruitment/models.py:783 +#: templates/recruitment/training_update.html:158 +msgid "Content" +msgstr "المحتوى" + +#: recruitment/forms.py:334 recruitment/models.py:690 +#: templates/recruitment/training_update.html:150 +msgid "Video Link" +msgstr "رابط الفيديو" + +#: recruitment/forms.py:335 recruitment/models.py:692 +#: templates/recruitment/training_update.html:166 +#: venv/lib/python3.13/site-packages/django/db/models/fields/files.py:244 +msgid "File" +msgstr "ملف" + +#: recruitment/forms.py:338 +#, fuzzy +#| msgid "Enter material content" +msgid "Enter material title" +msgstr "أدخل محتوى المادة" + +#: recruitment/forms.py:339 +msgid "Enter material content" +msgstr "أدخل محتوى المادة" + +#: recruitment/forms.py:340 +msgid "https://www.youtube.com/watch?v=..." +msgstr "https://www.youtube.com/watch?v=..." + +#: recruitment/forms.py:359 +msgid "Create Material" +msgstr "إنشاء مادة" + +#: recruitment/forms.py:516 recruitment/models.py:422 +#: recruitment/models.py:1311 templates/forms/form_templates_list.html:270 +#: templates/meetings/list_meetings.html:256 +#: templates/meetings/list_meetings.html:308 +#: templates/meetings/reschedule_meeting.html:11 +#: templates/meetings/schedule_meeting_form.html:12 +#: templates/recruitment/agency_assignment_detail.html:129 +#: templates/recruitment/agency_assignment_list.html:113 +#: templates/recruitment/candidate_list.html:273 +#: templates/recruitment/schedule_meeting_form.html:18 +msgid "Job" +msgstr "الوظيفة" + +#: recruitment/forms.py:517 templates/forms/form_templates_list.html:269 +msgid "Template Name" +msgstr "اسم القالب" + +#: recruitment/forms.py:518 recruitment/models.py:1097 +#: templates/recruitment/agency_detail.html:377 +msgid "Description" +msgstr "الوصف" + +#: recruitment/forms.py:519 recruitment/models.py:1131 +#: recruitment/models.py:1296 templates/jobs/job_list.html:230 +#: templates/recruitment/agency_access_link_detail.html:31 +#: templates/recruitment/agency_access_link_form.html:89 +#: templates/recruitment/agency_assignment_list.html:87 +#: templates/recruitment/agency_detail.html:456 +#: templates/recruitment/agency_portal_dashboard.html:148 +#: templates/user/admin_settings.html:192 +msgid "Active" +msgstr "نشط" + +#: recruitment/forms.py:524 +msgid "Enter template name" +msgstr "أدخل اسم القالب" + +#: recruitment/forms.py:530 +msgid "Enter template description (optional)" +msgstr "أدخل وصف القالب (اختياري)" + +#: recruitment/forms.py:549 templates/forms/form_templates_list.html:384 +msgid "Create Template" +msgstr "إنشاء قالب" + +#: recruitment/forms.py:620 +msgid "Enter your comment or note" +msgstr "أدخل تعليقك أو ملاحظتك" + +#: recruitment/forms.py:625 templates/meetings/meeting_details.html:422 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:23 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:59 +msgid "Comment" +msgstr "تعليق" + +#: recruitment/forms.py:637 +msgid "Add Comment" +msgstr "إضافة تعليق" + +#: templates/recruitment/agency_portal_dashboard.html:76 +msgid "Total Assignments" +msgstr "إجمالي المهام" + +#: templates/recruitment/agency_portal_dashboard.html:87 +msgid "Active Assignments" +msgstr "المهام النشطة" + +#: templates/recruitment/agency_portal_dashboard.html:121 +msgid "Your Job Assignments" +msgstr "مهام الوظائف الخاصة بك" + +#: templates/recruitment/agency_portal_dashboard.html:166 +msgid "days left" +msgstr "أيام متبقية" + +#: templates/recruitment/agency_portal_dashboard.html:168 +msgid "days overdue" +msgstr "أيام متأخرة" + +#: templates/recruitment/agency_portal_dashboard.html:181 +msgid "Submission Progress" +msgstr "تقدم التقديم" + +#: templates/recruitment/agency_portal_dashboard.html:202 +msgid "Submissions Closed" +msgstr "التقديمات مغلقة" + +#: templates/recruitment/agency_portal_dashboard.html:230 +msgid "No Job Assignments Found" +msgstr "لم يتم العثور على مهام وظائف" + +#: templates/recruitment/agency_portal_dashboard.html:232 +msgid "You don't have any job assignments yet. Please contact the administrator if you expect to have assignments." +msgstr "ليس لديك أي مهام وظائف حتى الآن. يرجى التواصل مع المسؤول إذا كنت تتوقع وجود مهام." + +#: recruitment/forms.py:768 recruitment/models.py:1272 +#: templates/recruitment/agency_confirm_delete.html:218 +#: templates/recruitment/agency_list.html:184 +msgid "Agency Name" +msgstr "اسم الوكالة" + +#: recruitment/forms.py:769 recruitment/models.py:1274 +#: templates/recruitment/agency_confirm_delete.html:229 +#: templates/recruitment/agency_list.html:185 +msgid "Contact Person" +msgstr "شخص الاتصال" + +#: recruitment/forms.py:770 recruitment/forms.py:1065 +#: templates/participants/participants_detail.html:172 +#: templates/recruitment/agency_portal_submit_candidate.html:219 +#: templates/user/profile.html:139 +#, fuzzy +#| msgid "IP Address" +msgid "Email Address" +msgstr "عنوان IP" + +#: recruitment/forms.py:771 recruitment/forms.py:1066 recruitment/models.py:32 +#: recruitment/models.py:1735 templates/meetings/meeting_details.html:306 +#: templates/participants/participants_detail.html:178 +#: templates/recruitment/agency_portal_submit_candidate.html:230 +#, fuzzy +#| msgid "Position Number" +msgid "Phone Number" +msgstr "رقم المنصب" + +#: recruitment/forms.py:772 templates/recruitment/agency_detail.html:261 +#: templates/recruitment/agency_detail.html:313 +#: templates/recruitment/agency_list.html:189 +msgid "Website" +msgstr "الموقع الإلكتروني" + +#: recruitment/forms.py:773 templates/jobs/create_job.html:302 +#: templates/jobs/edit_job.html:302 +#: templates/recruitment/agency_detail.html:363 +#: templates/recruitment/agency_list.html:188 +msgid "Country" +msgstr "الدولة" + +#: recruitment/forms.py:774 recruitment/models.py:428 +#: templates/recruitment/agency_detail.html:339 +#: templates/recruitment/agency_portal_assignment_detail.html:568 +#, fuzzy +#| msgid "IP Address" +msgid "Address" +msgstr "عنوان IP" + +#: recruitment/forms.py:775 +#, fuzzy +#| msgid "Internal Job ID:" +msgid "Internal Notes" +msgstr "معرف الوظيفة الداخلي:" + +#: recruitment/forms.py:799 +#, fuzzy +#| msgid "Submitted by Agency" +msgid "Save Agency" +msgstr "مقدم من الوكالة" + +#: recruitment/forms.py:888 recruitment/models.py:1305 +#: templates/recruitment/agency_access_link_detail.html:46 +#: templates/recruitment/agency_assignment_detail.html:124 +#: templates/recruitment/agency_assignment_list.html:112 +#, fuzzy +#| msgid "Agency Name" +msgid "Agency" +msgstr "اسم الوكالة" + +#: recruitment/forms.py:889 +#, fuzzy +#| msgid "Create New Job Posting" +msgid "Job Posting" +msgstr "إنشاء إعلان وظيفة جديد" + +#: recruitment/forms.py:890 recruitment/models.py:1316 +#: templates/recruitment/agency_portal_assignment_detail.html:174 +#, fuzzy +#| msgid "Candidates" +msgid "Maximum Candidates" +msgstr "المرشحون" + +#: recruitment/forms.py:891 recruitment/models.py:1328 +#, fuzzy +#| msgid "Deadline:" +msgid "Deadline Date" +msgstr "الموعد النهائي:" + +#: recruitment/forms.py:892 recruitment/forms.py:987 recruitment/models.py:1333 +#: recruitment/models.py:1501 +#, fuzzy +#| msgid "Active" +msgid "Is Active" +msgstr "نشط" + +#: recruitment/forms.py:893 recruitment/models.py:746 +#: recruitment/models.py:1338 recruitment/models.py:1687 +#: templates/includes/candidate_modal_body.html:70 +#: templates/includes/easy_logs.html:209 +#: templates/meetings/list_meetings.html:312 +#: templates/recruitment/agency_access_link_form.html:84 +#: templates/recruitment/agency_assignment_detail.html:136 +#: templates/recruitment/agency_assignment_list.html:84 +#: templates/recruitment/agency_assignment_list.html:116 +#: templates/recruitment/agency_portal_assignment_detail.html:148 +#: templates/recruitment/candidate_detail.html:557 +#: templates/recruitment/candidate_hired_view.html:236 +#: templates/recruitment/notification_detail.html:155 +#: templates/recruitment/notification_list.html:37 +#: templates/recruitment/partials/_candidate_table.html:12 +#: templates/user/admin_settings.html:176 +msgid "Status" +msgstr "الحالة" + +#: recruitment/forms.py:894 recruitment/models.py:1356 +#: templates/recruitment/agency_assignment_detail.html:160 +msgid "Admin Notes" +msgstr "ملاحظات المسؤول" + +#: recruitment/forms.py:928 +msgid "Save Assignment" +msgstr "حفظ التكليف" + +#: recruitment/forms.py:985 recruitment/models.py:1466 +#: templates/recruitment/agency_access_link_detail.html:37 +#: templates/recruitment/agency_access_link_form.html:37 +msgid "Assignment" +msgstr "التكليف" + +#: recruitment/forms.py:986 recruitment/models.py:1485 +#: templates/recruitment/agency_access_link_detail.html:56 +#: templates/recruitment/agency_access_link_form.html:52 +msgid "Expires At" +msgstr "ينتهي في" + +#: recruitment/forms.py:1011 +#: templates/recruitment/agency_access_link_form.html:4 +#: templates/recruitment/agency_access_link_form.html:12 +#: templates/recruitment/agency_access_link_form.html:130 +#, fuzzy +#| msgid "Create Meeting" +msgid "Create Access Link" +msgstr "إنشاء اجتماع" + +#: recruitment/forms.py:1091 +#: templates/recruitment/agency_portal_dashboard.html:198 +#: templates/recruitment/agency_portal_submit_candidate.html:4 +#: templates/recruitment/agency_portal_submit_candidate.html:104 +#: templates/recruitment/agency_portal_submit_candidate.html:337 +#: templates/recruitment/agency_portal_submit_candidate.html:561 +#, fuzzy +#| msgid "View Candidate" +msgid "Submit Candidate" +msgstr "عرض المرشح" + +#: recruitment/forms.py:1155 +#: templates/recruitment/agency_access_link_detail.html:98 +#: templates/recruitment/agency_assignment_detail.html:187 +#: templates/recruitment/agency_portal_login.html:145 +msgid "Access Token" +msgstr "رمز الوصول" + +#: recruitment/forms.py:1164 recruitment/models.py:729 +#: templates/account/login.html:164 templates/meetings/list_meetings.html:266 +#: templates/recruitment/agency_access_link_detail.html:109 +#: templates/recruitment/agency_assignment_detail.html:198 +#: templates/recruitment/agency_portal_login.html:167 +msgid "Password" +msgstr "كلمة المرور" + +#: recruitment/forms.py:1253 +#, fuzzy +#| msgid "Participant Video" +msgid "Select Participants" +msgstr "فيديو المشارك" + +#: recruitment/forms.py:1259 +#, fuzzy +#| msgid "Select country" +msgid "Select Users" +msgstr "اختر الدولة" + +#: recruitment/forms.py:1276 templates/includes/email_compose_form.html:20 +#: templates/recruitment/agency_portal_assignment_detail.html:488 +msgid "Subject" +msgstr "الموضوع" + +#: recruitment/forms.py:1287 templates/includes/email_compose_form.html:56 +#: templates/recruitment/agency_portal_assignment_detail.html:503 +#, fuzzy +#| msgid "Error Message" +msgid "Message" +msgstr "رسالة الخطأ" + +#: recruitment/forms.py:1295 templates/includes/email_compose_form.html:35 +msgid "Recipients" +msgstr "المستلمون" + +#: recruitment/forms.py:1303 +#, fuzzy +#| msgid "Candidate Information" +msgid "Include candidate information" +msgstr "معلومات المرشح" + +#: recruitment/forms.py:1312 +#, fuzzy +#| msgid "Meeting Details" +msgid "Include meeting details" +msgstr "تفاصيل الاجتماع" + +#: recruitment/forms.py:1376 +#, fuzzy +#| msgid "Please select a stage." +msgid "Please select at least one recipient." +msgstr "الرجاء اختيار مرحلة." + +#: recruitment/models.py:20 +msgid "Created at" +msgstr "تم الإنشاء في" + +#: recruitment/models.py:21 +msgid "Updated at" +msgstr "تم التحديث في" + +#: recruitment/models.py:23 +msgid "Slug" +msgstr "الرابط المختصر" + +#: recruitment/models.py:60 +#, fuzzy +#| msgid "Internal Information" +msgid "Internal Participant" +msgstr "المعلومات الداخلية" + +#: recruitment/models.py:61 +msgid "Internal staff involved in the recruitment process for this job" +msgstr "الموظفون الداخليون المشاركون في عملية التوظيف لهذه الوظيفة" + +#: recruitment/models.py:66 +msgid "External Participant" +msgstr "مشارك خارجي" + +#: recruitment/models.py:67 +msgid "External participants involved in the recruitment process for this job" +msgstr "المشاركون الخارجيون المشاركون في عملية التوظيف لهذه الوظيفة" + +#: recruitment/models.py:182 +msgid "External agency responsible for sourcing candidates for this role" +msgstr "وكالة خارجية مسؤولة عن توفير المرشحين لهذا المنصب" + +#: recruitment/models.py:187 +msgid "Reason for canceling the job posting" +msgstr "سبب إلغاء نشر الوظيفة" + +#: recruitment/models.py:188 +#, fuzzy +#| msgid "Cancelled" +msgid "Cancel Reason" +msgstr "ملغى" + +#: recruitment/models.py:193 +msgid "Name of person who cancelled this job" +msgstr "اسم الشخص الذي ألغى هذه الوظيفة" + +#: recruitment/models.py:194 +#, fuzzy +#| msgid "Cancelled" +msgid "Cancelled By" +msgstr "ملغى" + +#: recruitment/models.py:392 recruitment/models.py:437 +#: templates/jobs/job_candidates_list.html:211 +#: templates/recruitment/candidate_list.html:231 +msgid "Applied" +msgstr "تم التقديم" + +#: recruitment/models.py:393 templates/jobs/job_candidates_list.html:212 +#: templates/jobs/job_list.html:283 +#: templates/jobs/partials/applicant_tracking.html:128 +#: templates/recruitment/candidate_detail.html:410 +#: templates/recruitment/candidate_list.html:232 +msgid "Exam" +msgstr "الاختبار" + +#: recruitment/models.py:394 templates/jobs/job_candidates_list.html:213 +#: templates/jobs/job_list.html:284 +#: templates/jobs/partials/applicant_tracking.html:144 +#: templates/recruitment/candidate_detail.html:424 +#: templates/recruitment/candidate_list.html:233 +msgid "Interview" +msgstr "المقابلة" + +#: recruitment/models.py:395 templates/jobs/job_candidates_list.html:214 +#: templates/jobs/job_list.html:285 +#: templates/jobs/partials/applicant_tracking.html:160 +#: templates/recruitment/candidate_detail.html:439 +#: templates/recruitment/candidate_list.html:234 +#: templates/recruitment/candidate_offer_view.html:248 +msgid "Offer" +msgstr "العرض" + +#: recruitment/models.py:396 +#: templates/jobs/partials/applicant_tracking.html:176 +#: templates/recruitment/agency_detail.html:462 +#: templates/recruitment/candidate_hired_view.html:273 +msgid "Hired" +msgstr "تم التوظيف" + +#: recruitment/models.py:399 +#: templates/includes/candidate_update_exam_form.html:5 +#: templates/includes/candidate_update_interview_form.html:5 +msgid "Passed" +msgstr "نجح" + +#: recruitment/models.py:400 recruitment/models.py:1667 +#: templates/includes/candidate_update_exam_form.html:8 +#: templates/includes/candidate_update_interview_form.html:8 +#: templates/includes/easy_logs.html:267 +msgid "Failed" +msgstr "رسب" + +#: recruitment/models.py:403 +#: templates/includes/candidate_update_offer_form.html:5 +msgid "Accepted" +msgstr "قبل" + +#: recruitment/models.py:404 +#: templates/includes/candidate_update_offer_form.html:8 +#: templates/recruitment/agency_detail.html:468 +msgid "Rejected" +msgstr "رفض" + +#: recruitment/models.py:407 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant" +msgstr "المتقدمون" + +#: recruitment/models.py:408 recruitment/models.py:505 +#: templates/meetings/list_meetings.html:248 +#: templates/meetings/list_meetings.html:307 +msgid "Candidate" +msgstr "المرشح" + +#: recruitment/models.py:431 +#, fuzzy +#| msgid "Resume" +msgid "Resume Parsed" +msgstr "السيرة الذاتية" + +#: recruitment/models.py:434 +#, fuzzy +#| msgid "Create Candidate" +msgid "Potential Candidate" +msgstr "إنشاء مرشح" + +#: recruitment/models.py:436 +msgid "Parsed Summary" +msgstr "ملخص محلل" + +#: recruitment/models.py:442 templates/jobs/job_candidates_list.html:229 +#: templates/recruitment/agency_assignment_detail.html:241 +#: templates/recruitment/agency_portal_assignment_detail.html:243 +#: templates/recruitment/candidate_list.html:275 +#: templates/recruitment/partials/_candidate_table.html:13 +msgid "Stage" +msgstr "المرحلة" + +#: recruitment/models.py:450 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant Status" +msgstr "المتقدمون" + +#: recruitment/models.py:452 templates/recruitment/candidate_exam_view.html:253 +msgid "Exam Date" +msgstr "تاريخ الاختبار" + +#: recruitment/models.py:458 +msgid "Exam Status" +msgstr "حالة الاختبار" + +#: recruitment/models.py:461 recruitment/models.py:1630 +msgid "Interview Date" +msgstr "تاريخ المقابلة" + +#: recruitment/models.py:468 +msgid "Interview Status" +msgstr "حالة المقابلة" + +#: recruitment/models.py:470 +msgid "Offer Date" +msgstr "تاريخ العرض" + +#: recruitment/models.py:476 +msgid "Offer Status" +msgstr "حالة العرض" + +#: recruitment/models.py:478 +#: templates/recruitment/candidate_hired_view.html:235 +#, fuzzy +#| msgid "Applied Date" +msgid "Hired Date" +msgstr "تاريخ التقديم" + +#: recruitment/models.py:479 +msgid "Join Date" +msgstr "تاريخ الانضمام" + +#: recruitment/models.py:491 templates/recruitment/candidate_list.html:276 +#, fuzzy +#| msgid "Hiring Agencies" +msgid "Hiring Source" +msgstr "وكالات التوظيف" + +#: recruitment/models.py:506 +#: templates/recruitment/agency_assignment_list.html:114 +#: templates/recruitment/agency_portal_dashboard.html:173 +msgid "Candidates" +msgstr "المرشحون" + +#: recruitment/models.py:695 +msgid "Created by" +msgstr "أنشأ بواسطة" + +#: recruitment/models.py:699 +msgid "Training Material" +msgstr "مادة تدريبية" + +#: recruitment/models.py:700 templates/recruitment/training_list.html:4 +#: templates/recruitment/training_list.html:128 +msgid "Training Materials" +msgstr "المواد التدريبية" + +#: recruitment/models.py:708 templates/meetings/list_meetings.html:203 +msgid "Waiting" +msgstr "في الانتظار" + +#: recruitment/models.py:709 templates/meetings/list_meetings.html:204 +msgid "Started" +msgstr "بدأ" + +#: recruitment/models.py:710 templates/meetings/list_meetings.html:205 +msgid "Ended" +msgstr "انتهى" + +#: recruitment/models.py:711 recruitment/models.py:1299 +#: recruitment/models.py:1637 +#: templates/recruitment/agency_assignment_list.html:90 +#: templates/recruitment/agency_portal_dashboard.html:152 +msgid "Cancelled" +msgstr "ملغى" + +#: recruitment/models.py:715 templates/meetings/meeting_details.html:266 +msgid "Meeting ID" +msgstr "معرف الاجتماع" + +#: recruitment/models.py:721 +msgid "Timezone" +msgstr "المنطقة الزمنية" + +#: recruitment/models.py:723 templates/meetings/meeting_details.html:274 +msgid "Join URL" +msgstr "رابط الانضمام" + +#: recruitment/models.py:726 +msgid "Participant Video" +msgstr "فيديو المشارك" + +#: recruitment/models.py:732 +msgid "Join Before Host" +msgstr "الانضمام قبل المضيف" + +#: recruitment/models.py:735 +msgid "Mute Upon Entry" +msgstr "كتم الصوت عند الدخول" + +#: recruitment/models.py:737 +msgid "Waiting Room" +msgstr "غرفة الانتظار" + +#: recruitment/models.py:740 +msgid "Zoom Gateway Response" +msgstr "استجابة بوابة زوم" + +#: recruitment/models.py:774 +#, fuzzy +#| msgid "Meetings" +msgid "Meeting" +msgstr "الاجتماعات" + +#: recruitment/models.py:780 +msgid "Author" +msgstr "المؤلف" + +#: recruitment/models.py:789 +#, fuzzy +#| msgid "Meeting Details" +msgid "Meeting Comment" +msgstr "تفاصيل الاجتماع" + +#: recruitment/models.py:790 +#, fuzzy +#| msgid "Meeting Details" +msgid "Meeting Comments" +msgstr "تفاصيل الاجتماع" + +#: recruitment/models.py:1089 +msgid "Source Name" +msgstr "اسم المصدر" + +#: recruitment/models.py:1090 recruitment/models.py:1093 +msgid "e.g., ATS, ERP " +msgstr "مثلاً، ATS, ERP" + +#: recruitment/models.py:1093 templates/meetings/meeting_details.html:307 +msgid "Source Type" +msgstr "نوع المصدر" + +#: recruitment/models.py:1098 +msgid "A description of the source" +msgstr "وصف المصدر" + +#: recruitment/models.py:1103 recruitment/models.py:1243 +#: templates/includes/easy_logs.html:210 +msgid "IP Address" +msgstr "عنوان IP" + +#: recruitment/models.py:1104 +msgid "The IP address of the source" +msgstr "عنوان IP الخاص بالمصدر" + +#: recruitment/models.py:1113 +msgid "API Key" +msgstr "مفتاح واجهة برمجة التطبيقات" + +#: recruitment/models.py:1114 +msgid "API key for authentication (will be encrypted)" +msgstr "مفتاح واجهة برمجة التطبيقات للمصادقة (سيتم تشفيره)" + +#: recruitment/models.py:1120 +msgid "API Secret" +msgstr "سر واجهة برمجة التطبيقات" + +#: recruitment/models.py:1121 +msgid "API secret for authentication (will be encrypted)" +msgstr "سر واجهة برمجة التطبيقات للمصادقة (سيتم تشفيره)" + +#: recruitment/models.py:1126 +msgid "Trusted IP Addresses" +msgstr "عناوين IP الموثوقة" + +#: recruitment/models.py:1127 +msgid "Comma-separated list of trusted IP addresses" +msgstr "قائمة عناوين IP الموثوقة مفصولة بفواصل" + +#: recruitment/models.py:1132 +msgid "Whether this source is active for integration" +msgstr "ما إذا كان هذا المصدر نشطاً للتكامل" + +#: recruitment/models.py:1137 +msgid "Integration Version" +msgstr "إصدار التكامل" + +#: recruitment/models.py:1138 +msgid "Version of the integration protocol" +msgstr "إصدار بروتوكول التكامل" + +#: recruitment/models.py:1143 +msgid "Last Sync At" +msgstr "آخر مزامنة في" + +#: recruitment/models.py:1144 +msgid "Timestamp of the last successful synchronization" +msgstr "الطابع الزمني لآخر مزامنة ناجحة" + +#: recruitment/models.py:1156 +msgid "Sync Status" +msgstr "حالة المزامنة" + +#: recruitment/models.py:1163 +#, fuzzy +#| msgid "Endpoint" +msgid "Sync Endpoint" +msgstr "نقطة النهاية" + +#: recruitment/models.py:1164 +msgid "Endpoint URL for sending candidate data (for outbound sync)" +msgstr "عنوان URL لنقطة النهاية لإرسال بيانات المرشح (للمزامنة الصادرة)" + +#: recruitment/models.py:1174 +#, fuzzy +#| msgid "HTTP Method" +msgid "Sync Method" +msgstr "طريقة HTTP" + +#: recruitment/models.py:1175 +msgid "HTTP method for outbound sync requests" +msgstr "طريقة HTTP لطلبات المزامنة الصادرة" + +#: recruitment/models.py:1185 +#, fuzzy +#| msgid "HTTP Method" +msgid "Test Method" +msgstr "طريقة HTTP" + +#: recruitment/models.py:1186 +msgid "HTTP method for connection testing" +msgstr "طريقة HTTP لاختبار الاتصال" + +#: recruitment/models.py:1191 +msgid "Custom Headers" +msgstr "رؤوس مخصصة" + +#: recruitment/models.py:1192 +msgid "JSON object with custom HTTP headers for sync requests" +msgstr "كائن JSON يحتوي على رؤوس HTTP مخصصة لطلبات المزامنة" + +#: recruitment/models.py:1196 +msgid "Supports Outbound Sync" +msgstr "يدعم المزامنة الصادرة" + +#: recruitment/models.py:1197 +msgid "Whether this source supports receiving candidate data from ATS" +msgstr "ما إذا كان هذا المصدر يدعم استقبال بيانات المرشح من نظام تتبع المتقدمين" + +#: recruitment/models.py:1204 recruitment/models.py:1226 +#: templates/jobs/job_list.html:269 +msgid "Source" +msgstr "المصدر" + +#: recruitment/models.py:1205 +msgid "Sources" +msgstr "المصادر" + +#: recruitment/models.py:1215 +msgid "Request" +msgstr "الطلب" + +#: recruitment/models.py:1216 +msgid "Response" +msgstr "الاستجابة" + +#: recruitment/models.py:1217 +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:98 +msgid "Error" +msgstr "خطأ" + +#: recruitment/models.py:1218 +msgid "Sync" +msgstr "مزامنة" + +#: recruitment/models.py:1219 templates/jobs/job_list.html:395 +msgid "Create Job" +msgstr "إنشاء وظيفة" + +#: recruitment/models.py:1220 +msgid "Update Job" +msgstr "تحديث وظيفة" + +#: recruitment/models.py:1229 templates/includes/easy_logs.html:199 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:25 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:49 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:25 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:49 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:18 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:34 +msgid "Action" +msgstr "الإجراء" + +#: recruitment/models.py:1231 +msgid "Endpoint" +msgstr "نقطة النهاية" + +#: recruitment/models.py:1232 +msgid "HTTP Method" +msgstr "طريقة HTTP" + +#: recruitment/models.py:1234 +msgid "Request Data" +msgstr "بيانات الطلب" + +#: recruitment/models.py:1237 +msgid "Response Data" +msgstr "بيانات الاستجابة" + +#: recruitment/models.py:1240 +msgid "Status Code" +msgstr "رمز الحالة" + +#: recruitment/models.py:1242 +msgid "Error Message" +msgstr "رسالة الخطأ" + +#: recruitment/models.py:1245 +msgid "User Agent" +msgstr "وكيل المستخدم" + +#: recruitment/models.py:1248 +msgid "Processing Time (seconds)" +msgstr "وقت المعالجة (ثواني)" + +#: recruitment/models.py:1256 +msgid "Integration Log" +msgstr "سجل التكامل" + +#: recruitment/models.py:1257 +msgid "Integration Logs" +msgstr "سجلات التكامل" + +#: recruitment/models.py:1279 +msgid "Internal notes about the agency" +msgstr "ملاحظات داخلية حول الوكالة" + +#: recruitment/models.py:1280 +msgid "Select country" +msgstr "اختر الدولة" + +#: recruitment/models.py:1288 templates/recruitment/agency_list.html:4 +#: templates/recruitment/agency_list.html:131 +msgid "Hiring Agencies" +msgstr "وكالات التوظيف" + +#: recruitment/models.py:1297 recruitment/models.py:1638 +#: templates/recruitment/agency_assignment_list.html:89 +#: templates/recruitment/agency_portal_dashboard.html:150 +msgid "Completed" +msgstr "مكتمل" + +#: recruitment/models.py:1298 +#: templates/recruitment/agency_access_link_detail.html:60 +#: templates/recruitment/agency_assignment_detail.html:151 +#: templates/recruitment/agency_assignment_list.html:88 +#: templates/recruitment/agency_assignment_list.html:148 +#: templates/recruitment/agency_portal_assignment_detail.html:165 +#: templates/recruitment/agency_portal_dashboard.html:154 +msgid "Expired" +msgstr "منتهي الصلاحية" + +#: recruitment/models.py:1317 +msgid "Maximum candidates agency can submit for this job" +msgstr "الحد الأقصى للمرشحين الذين يمكن للوكالة تقديمهم لهذه الوظيفة" + +#: recruitment/models.py:1321 +#: templates/recruitment/agency_access_link_detail.html:71 +#, fuzzy +#| msgid "Candidates" +msgid "Candidates Submitted" +msgstr "المرشحون" + +#: recruitment/models.py:1322 +msgid "Number of candidates submitted so far" +msgstr "" + +#: recruitment/models.py:1326 +#: templates/recruitment/agency_portal_assignment_detail.html:410 +#, fuzzy +#| msgid "Applied Date" +msgid "Assigned Date" +msgstr "تاريخ التقديم" + +#: recruitment/models.py:1329 +msgid "Deadline for agency to submit candidates" +msgstr "" + +#: recruitment/models.py:1344 +#, fuzzy +#| msgid "Deadline:" +msgid "Deadline Extended" +msgstr "الموعد النهائي:" + +#: recruitment/models.py:1349 +#, fuzzy +#| msgid "Application Deadline" +msgid "Original Deadline" +msgstr "موعد نهائي للتقديم" + +#: recruitment/models.py:1350 +msgid "Original deadline before extensions" +msgstr "" + +#: recruitment/models.py:1357 +#, fuzzy +#| msgid "Internal notes about the agency" +msgid "Internal notes about this assignment" +msgstr "ملاحظات داخلية حول الوكالة" + +#: recruitment/models.py:1361 +msgid "Agency Job Assignment" +msgstr "" + +#: recruitment/models.py:1362 +msgid "Agency Job Assignments" +msgstr "" + +#: recruitment/models.py:1401 +msgid "Deadline date must be in the future" +msgstr "" + +#: recruitment/models.py:1404 +msgid "Maximum candidates must be greater than 0" +msgstr "" + +#: recruitment/models.py:1407 +msgid "Candidates submitted cannot exceed maximum candidates" +msgstr "" + +#: recruitment/models.py:1474 +msgid "Unique Token" +msgstr "" + +#: recruitment/models.py:1478 +#, fuzzy +#| msgid "Password" +msgid "Access Password" +msgstr "كلمة المرور" + +#: recruitment/models.py:1479 +msgid "Password for agency access" +msgstr "" + +#: recruitment/models.py:1483 templates/participants/participants_list.html:217 +#: templates/recruitment/agency_access_link_detail.html:51 +#, fuzzy +#| msgid "Created at" +msgid "Created At" +msgstr "تم الإنشاء في" + +#: recruitment/models.py:1486 +msgid "When this access link expires" +msgstr "" + +#: recruitment/models.py:1491 +#: templates/recruitment/agency_access_link_detail.html:141 +msgid "Last Accessed" +msgstr "" + +#: recruitment/models.py:1497 +msgid "Access Count" +msgstr "" + +#: recruitment/models.py:1505 +msgid "Agency Access Link" +msgstr "" + +#: recruitment/models.py:1506 +msgid "Agency Access Links" +msgstr "" + +#: recruitment/models.py:1520 +msgid "Expiration date must be in the future" +msgstr "" + +#: recruitment/models.py:1565 recruitment/models.py:1584 +#: templates/interviews/schedule_interviews.html:176 +#: templates/interviews/schedule_interviews.html:205 +msgid "End Time" +msgstr "وقت الانتهاء" + +#: recruitment/models.py:1578 templates/interviews/schedule_interviews.html:146 +msgid "Start Date" +msgstr "تاريخ البدء" + +#: recruitment/models.py:1579 templates/interviews/schedule_interviews.html:153 +msgid "End Date" +msgstr "تاريخ الانتهاء" + +#: recruitment/models.py:1581 templates/interviews/schedule_interviews.html:160 +msgid "Working Days" +msgstr "أيام العمل" + +#: recruitment/models.py:1586 +msgid "Break Start Time" +msgstr "وقت بدء الاستراحة" + +#: recruitment/models.py:1587 +msgid "Break End Time" +msgstr "وقت انتهاء الاستراحة" + +#: recruitment/models.py:1590 +msgid "Interview Duration (minutes)" +msgstr "مدة المقابلة (دقائق)" + +#: recruitment/models.py:1593 +msgid "Buffer Time (minutes)" +msgstr "وقت المخزن المؤقت (دقائق)" + +#: recruitment/models.py:1631 +msgid "Interview Time" +msgstr "وقت المقابلة" + +#: recruitment/models.py:1635 +msgid "Scheduled" +msgstr "مجدول" + +#: recruitment/models.py:1636 +msgid "Confirmed" +msgstr "مؤكد" + +#: recruitment/models.py:1661 templates/recruitment/notification_list.html:49 +msgid "In-App" +msgstr "" + +#: recruitment/models.py:1664 +msgid "Pending" +msgstr "" + +#: recruitment/models.py:1665 templates/recruitment/notification_list.html:42 +msgid "Sent" +msgstr "" + +#: recruitment/models.py:1666 templates/recruitment/notification_list.html:41 +msgid "Read" +msgstr "" + +#: recruitment/models.py:1668 +msgid "Retrying" +msgstr "" + +#: recruitment/models.py:1674 +msgid "Recipient" +msgstr "" + +#: recruitment/models.py:1676 +msgid "Notification Message" +msgstr "" + +#: recruitment/models.py:1681 +msgid "Notification Type" +msgstr "" + +#: recruitment/models.py:1695 templates/recruitment/notification_detail.html:62 +#, fuzzy +#| msgid "Create Meeting" +msgid "Related Meeting" +msgstr "إنشاء اجتماع" + +#: recruitment/models.py:1698 +#, fuzzy +#| msgid "Scheduled" +msgid "Scheduled Send Time" +msgstr "مجدول" + +#: recruitment/models.py:1699 +msgid "The date and time this notification is scheduled to be sent." +msgstr "التاريخ والوقت المحدد لإرسال هذا الإشعار." + +#: recruitment/models.py:1703 +msgid "Send Attempts" +msgstr "محاولات الإرسال" + +#: recruitment/models.py:1704 +#, fuzzy +#| msgid "Error Message" +msgid "Last Error Message" +msgstr "رسالة الخطأ" + +#: recruitment/models.py:1708 +#, fuzzy +#| msgid "Location" +msgid "Notification" +msgstr "الموقع" + +#: recruitment/models.py:1709 templates/recruitment/notification_list.html:4 +#: templates/recruitment/notification_list.html:12 +#, fuzzy +#| msgid "Location" +msgid "Notifications" +msgstr "الموقع" + +#: recruitment/models.py:1733 +#, fuzzy +#| msgid "Participant Video" +msgid "Participant Name" +msgstr "فيديو المشارك" + +#: recruitment/models.py:1737 +#: templates/participants/participants_detail.html:184 +#: templates/participants/participants_list.html:216 +#, fuzzy +#| msgid "Description" +msgid "Designation" +msgstr "الوصف" + +#: recruitment/views.py:614 +msgid "Failed to start the job posting process. Please try again." +msgstr "فشل في بدء عملية نشر الوظيفة. يرجى المحاولة مرة أخرى." + +#: recruitment/views.py:2181 templates/includes/easy_logs.html:176 +#, fuzzy +#| msgid "User Agent" +msgid "User Authentication" +msgstr "وكيل المستخدم" + +#: recruitment/views.py:2184 templates/includes/easy_logs.html:182 +#, fuzzy +#| msgid "Request" +msgid "HTTP Requests" +msgstr "الطلب" + +#: recruitment/views.py:2187 templates/includes/easy_logs.html:170 +msgid "Model Changes (CRUD)" +msgstr "" + +#: recruitment/views_frontend.py:275 +msgid "You don't have permission to view this page." +msgstr "ليس لديك إذن لعرض هذه الصفحة." + +#: templates/account/account_inactive.html:7 +#: templates/account/account_inactive.html:131 +msgid "Account Inactive" +msgstr "الحساب غير نشط" + +#: templates/account/account_inactive.html:114 +#: templates/account/password_reset_done.html:136 +#: templates/account/password_reset_from_key.html:55 +#: templates/account/password_reset_from_key_done.html:52 +msgid "جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية" +msgstr "" + +#: templates/account/account_inactive.html:115 +#: templates/account/password_reset_done.html:137 +#: templates/account/password_reset_from_key.html:56 +#: templates/account/password_reset_from_key_done.html:53 +msgid "ومستشفى الملك عبدالله بن عبدالعزيز التخصصي" +msgstr "" + +#: templates/account/account_inactive.html:116 +#: templates/account/password_reset_done.html:138 +#: templates/account/password_reset_from_key.html:57 +#: templates/account/password_reset_from_key_done.html:54 +msgid "Princess Nourah bint Abdulrahman University" +msgstr "جامعة الأميرة نورة بنت عبدالرحمن" + +#: templates/account/account_inactive.html:117 +#: templates/account/password_reset_done.html:139 +#: templates/account/password_reset_from_key.html:58 +#: templates/account/password_reset_from_key_done.html:55 +#, fuzzy +#| msgid "King Abdullah Academic University Hospital" +msgid "King Abdullah bin Abdulaziz University Hospital" +msgstr "مستشفى الملك عبدالله الجامعي" + +#: templates/account/account_inactive.html:135 +msgid "" +"Access denied. This account has been marked as inactive by an administrator." +msgstr "" + +#: templates/account/account_inactive.html:138 +msgid "" +"If you believe this is an error, please contact the system administrator for " +"assistance." +msgstr "" + +#: templates/account/account_inactive.html:143 +#: templates/account/password_reset_from_key.html:109 +#, fuzzy +#| msgid "Re-post to LinkedIn" +msgid "Return to Sign In" +msgstr "إعادة النشر على LinkedIn" + +#: templates/account/email.html:6 templates/account/email.html:33 +#: templates/account/email.html:52 templates/account/logout.html:29 +#, fuzzy +#| msgid "IP Address" +msgid "Email Addresses" +msgstr "عنوان IP" + +#: templates/account/email.html:13 templates/account/logout.html:12 +#: templates/user/profile.html:111 +#, fuzzy +#| msgid "Settings" +msgid "Account Settings" +msgstr "الإعدادات" + +#: templates/account/email.html:14 templates/account/logout.html:13 +#: templates/user/profile.html:112 +msgid "Manage your personal details and security." +msgstr "إدارة تفاصيلك الشخصية والأمان." + +#: templates/account/email.html:28 templates/account/logout.html:26 +#: templates/recruitment/agency_portal_submit_candidate.html:182 +#: templates/user/profile.html:123 +#, fuzzy +#| msgid "Internal Information" +msgid "Personal Information" +msgstr "المعلومات الداخلية" + +#: templates/account/email.html:36 templates/account/logout.html:32 +#: templates/account/password_change.html:4 +#: templates/account/password_change.html:15 +#: templates/account/password_change.html:34 +#: templates/account/password_reset_from_key.html:90 +#: templates/user/admin_settings.html:223 templates/user/profile.html:161 +#: templates/user/staff_password_create.html:4 +#: templates/user/staff_password_create.html:16 +#: templates/user/staff_password_create.html:35 +#, fuzzy +#| msgid "Password" +msgid "Change Password" +msgstr "كلمة المرور" + +#: templates/account/email.html:40 templates/account/logout.html:5 +#: templates/account/logout.html:37 templates/account/logout.html:66 +#: templates/base.html:213 +msgid "Sign Out" +msgstr "تسجيل الخروج" + +#: templates/account/email.html:53 +msgid "" +"These email addresses are linked to your account. You can set the primary " +"address, resend verification, or remove an address." +msgstr "" + +#: templates/account/email.html:73 +msgid "Primary" +msgstr "أساسي" + +#: templates/account/email.html:76 +msgid "Verified" +msgstr "موثق" + +#: templates/account/email.html:78 +msgid "Unverified" +msgstr "غير موثق" + +#: templates/account/email.html:89 +msgid "Make Primary" +msgstr "جعل أساسي" + +#: templates/account/email.html:98 +#, fuzzy +#| msgid "Required Qualifications" +msgid "Re-send Verification" +msgstr "المؤهلات المطلوبة" + +#: templates/account/email.html:107 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_delete.html:4 +msgid "Remove" +msgstr "إزالة" + +#: templates/account/email.html:114 +#, fuzzy +#| msgid "No candidates found." +msgid "No email addresses found." +msgstr "لم يتم العثور على مرشحين." + +#: templates/account/email.html:121 +msgid "Add Email Address" +msgstr "إضافة عنوان بريد إلكتروني" + +#: templates/account/email.html:136 +#, fuzzy +#| msgid "Email" +msgid "Add Email" +msgstr "البريد الإلكتروني" + +#: templates/account/email/email_confirmation_message.html:5 +#: templates/account/email/email_confirmation_message.txt:4 +#: templates/account/email/password_reset_key_message.html:14 +#: templates/account/email/password_reset_key_message.txt:7 +msgid "Hello," +msgstr "مرحباً،" + +#: templates/account/email/email_confirmation_message.html:9 +#: templates/account/email/email_confirmation_message.txt:6 +msgid "" +"To verify the ownership of your email address, please click the confirmation " +"link below:" +msgstr "" + +#: templates/account/email/email_confirmation_message.html:15 +#: templates/account/email/email_confirmation_message.txt:9 +msgid "Confirm My KAAUH ATS Email" +msgstr "تأكيد بريدي الإلكتروني في نظام توظيف جامعة نورة" + +#: templates/account/email/email_confirmation_message.html:20 +#: templates/account/email/email_confirmation_message.txt:13 +msgid "" +"If you did not request this verification, you can safely ignore this email." +msgstr "" + +#: templates/account/email/email_confirmation_message.html:24 +#: templates/account/email/email_confirmation_message.txt:15 +msgid "Alternatively, copy and paste this link into your browser:" +msgstr "بدلاً من ذلك، انسخ والصق هذا الرابط في متصفحك:" + +#: templates/account/email/password_reset_key_message.html:10 +#: templates/account/email/password_reset_key_message.txt:5 +msgid "Password Reset Request" +msgstr "طلب إعادة تعيين كلمة المرور" + +#: templates/account/email/password_reset_key_message.html:16 +#: templates/account/email/password_reset_key_message.txt:9 +msgid "" +"You are receiving this email because you or someone else has requested a " +"password reset for your account at" +msgstr "" + +#: templates/account/email/password_reset_key_message.html:21 +#: templates/account/email/password_reset_key_message.txt:12 +msgid "Click Here to Reset Your Password" +msgstr "اضغط هنا لإعادة تعيين كلمة المرور" + +#: templates/account/email/password_reset_key_message.html:25 +#: templates/account/email/password_reset_key_message.txt:16 +msgid "This link is only valid for a limited time." +msgstr "هذا الرابط صالح لفترة محدودة فقط." + +#: templates/account/email/password_reset_key_message.html:27 +#: templates/account/email/password_reset_key_message.txt:18 +msgid "" +"If you did not request a password reset, please ignore this email. Your " +"password will remain unchanged." +msgstr "" + +#: templates/account/email/password_reset_key_message.html:30 +#: templates/account/email/password_reset_key_message.txt:20 +msgid "Thank you," +msgstr "شكراً لك،" + +#: templates/account/email/password_reset_key_message.html:31 +#: templates/account/email/password_reset_key_message.txt:21 +msgid "KAAUH ATS Team" +msgstr "فريق نظام توظيف جامعة نورة" + +#: templates/account/email/password_reset_key_message.html:36 +#: templates/account/email/password_reset_key_message.txt:24 +msgid "" +"If the button above does not work, copy and paste the following link into " +"your browser:" +msgstr "" + +#: templates/account/email_confirm.html:5 +msgid "Confirm Email Address" +msgstr "تأكيد عنوان البريد الإلكتروني" + +#: templates/account/email_confirm.html:13 +msgid "Account Verification" +msgstr "التحقق من الحساب" + +#: templates/account/email_confirm.html:14 +msgid "Verify your email to secure your account and unlock full features." +msgstr "تحقق من بريدك الإلكتروني لتأمين حسابك وإلغاء قفل جميع الميزات." + +#: templates/account/email_confirm.html:32 +msgid "Confirm Your Email Address" +msgstr "تأكيد عنوان بريدك الإلكتروني" + +#: templates/account/email_confirm.html:35 +#, python-format +msgid "" +"Please confirm that **%(email)s** is the correct email address for your " +"account." +msgstr "" + +#: templates/account/email_confirm.html:44 +#, fuzzy +#| msgid "Confirm Delete" +msgid "Confirm & Activate" +msgstr "تأكيد الحذف" + +#: templates/account/email_confirm.html:53 +msgid "Verification Failed" +msgstr "فشل التحقق" + +#: templates/account/email_confirm.html:56 +msgid "The email confirmation link is expired or invalid." +msgstr "رابط تأكيد البريد الإلكتروني منتهي الصلاحية أو غير صالح." + +#: templates/account/email_confirm.html:59 +msgid "" +"If you recently requested a link, please ensure you use the newest one. You " +"can request a new verification email from your account settings." +msgstr "" + +#: templates/account/email_confirm.html:63 +#, fuzzy +#| msgid "Settings" +msgid "Go to Settings" +msgstr "الإعدادات" + +#: templates/account/login.html:151 templates/account/login.html:178 +#, fuzzy +#| msgid "Sign out" +msgid "Sign In" +msgstr "تسجيل الخروج" + +#: templates/account/login.html:158 +#, fuzzy +#| msgid "Email" +msgid "Email *" +msgstr "البريد الإلكتروني" + +#: templates/account/login.html:159 +#, fuzzy +#| msgid "Enter email" +msgid "Enter your email" +msgstr "أدخل البريد الإلكتروني" + +#: templates/account/login.html:163 +#, fuzzy +#| msgid "Password" +msgid "Password *" +msgstr "كلمة المرور" + +#: templates/account/login.html:167 templates/account/password_reset.html:150 +#, fuzzy +#| msgid "Password" +msgid "Forgot Password?" +msgstr "كلمة المرور" + +#: templates/account/login.html:174 +msgid "Keep me signed in" +msgstr "ابق مسجلاً للدخول" + +#: templates/account/logout.html:50 +#, fuzzy +#| msgid "Sign Out" +msgid "Confirm Sign Out" +msgstr "تسجيل الخروج" + +#: templates/account/logout.html:52 +#, fuzzy +#| msgid "Are you sure you want to sign out?" +msgid "Are you sure you want to sign out of your account?" +msgstr "هل أنت متأكد من رغبتك في تسجيل الخروج؟" + +#: templates/account/logout.html:71 +#: templates/forms/form_templates_list.html:382 +#: templates/includes/email_compose_form.html:110 +#: templates/includes/meeting_form.html:40 +#: templates/interviews/schedule_interviews.html:221 +#: templates/jobs/create_job.html:327 templates/jobs/edit_job.html:327 +#: templates/jobs/job_detail.html:536 +#: templates/meetings/create_meeting.html:184 +#: templates/meetings/delete_meeting_form.html:8 +#: templates/meetings/meeting_details.html:399 +#: templates/meetings/set_candidate_form.html:6 +#: templates/meetings/update_meeting.html:237 +#: templates/participants/participants_detail.html:252 +#: templates/participants/participants_list.html:334 +#: templates/recruitment/agency_access_link_form.html:127 +#: templates/recruitment/agency_assignment_detail.html:430 +#: templates/recruitment/agency_assignment_form.html:213 +#: templates/recruitment/agency_confirm_delete.html:357 +#: templates/recruitment/agency_form.html:337 +#: templates/recruitment/agency_portal_assignment_detail.html:510 +#: templates/recruitment/agency_portal_assignment_detail.html:575 +#: templates/recruitment/agency_portal_assignment_detail.html:609 +#: templates/recruitment/agency_portal_submit_candidate.html:333 +#: templates/recruitment/notification_confirm_all_read.html:53 +#: templates/recruitment/notification_confirm_delete.html:33 +#: templates/recruitment/schedule_meeting_form.html:89 +msgid "Cancel" +msgstr "إلغاء" + +#: templates/account/password_change.html:19 +#: templates/user/staff_password_create.html:20 +msgid "" +"Please enter your current password and a new password to secure your account." +msgstr "" + +#: templates/account/password_change.html:41 +msgid "Return to Profile" +msgstr "العودة إلى الملف الشخصي" + +#: templates/account/password_reset.html:154 +msgid "Enter your e-mail address to reset your password." +msgstr "أدخل عنوان بريدك الإلكتروني لإعادة تعيين كلمة المرور." + +#: templates/account/password_reset.html:162 +#, fuzzy +#| msgid "IP Address" +msgid "E-mail Address" +msgstr "عنوان IP" + +#: templates/account/password_reset.html:179 +#, fuzzy +#| msgid "Password" +msgid "Reset My Password" +msgstr "كلمة المرور" + +#: templates/account/password_reset.html:185 +msgid "Remember your password?" +msgstr "تتذكر كلمة المرور؟" + +#: templates/account/password_reset.html:186 +msgid "Log In" +msgstr "تسجيل الدخول" + +#: templates/account/password_reset_done.html:7 +#: templates/account/password_reset_done.html:151 +msgid "Password Reset Sent" +msgstr "تم إرسال إعادة تعيين كلمة المرور" + +#: templates/account/password_reset_done.html:160 +msgid "" +"\n" +" We've **sent an email** to the address you provided " +"with instructions on how to reset your password.\n" +" " +msgstr "" + +#: templates/account/password_reset_done.html:168 +msgid "" +"Please check your inbox (and spam folder). The link in the email is " +"temporary and will expire soon for security reasons." +msgstr "" + +#: templates/account/password_reset_done.html:175 +msgid "Return to Login" +msgstr "العودة إلى تسجيل الدخول" + +#: templates/account/password_reset_from_key.html:7 +#: templates/account/password_reset_from_key.html:70 +#, fuzzy +#| msgid "Password" +msgid "Set New Password" +msgstr "كلمة المرور" + +#: templates/account/password_reset_from_key.html:76 +msgid "Please enter your new password below." +msgstr "يرجى إدخال كلمة المرور الجديدة أدناه." + +#: templates/account/password_reset_from_key.html:79 +msgid "You can then log in." +msgstr "" + +#: templates/account/password_reset_from_key.html:96 +msgid "Password Reset Failed" +msgstr "" + +#: templates/account/password_reset_from_key.html:98 +msgid "The password reset link is invalid or has expired." +msgstr "" + +#: templates/account/password_reset_from_key.html:102 +msgid "Request New Reset Link" +msgstr "" + +#: templates/account/password_reset_from_key_done.html:7 +#, fuzzy +#| msgid "Password" +msgid "Password Changed" +msgstr "كلمة المرور" + +#: templates/account/password_reset_from_key_done.html:69 +#, fuzzy +#| msgid "Posted successfully!" +msgid "Password Changed Successfully" +msgstr "تم النشر بنجاح!" + +#: templates/account/password_reset_from_key_done.html:72 +msgid "" +"Your password has been set. You can now use your new password to sign in." +msgstr "" + +#: templates/account/password_reset_from_key_done.html:77 +#: templates/account/verification_sent.html:182 +#, fuzzy +#| msgid "Post to LinkedIn" +msgid "Go to Sign In" +msgstr "النشر على LinkedIn" + +#: templates/account/verification_sent.html:153 +msgid "Verify Your Email Address" +msgstr "" + +#: templates/account/verification_sent.html:159 +msgid "" +"\n" +" We have sent an email to your email id for " +"verification. Follow the link provided to finalize the signup process.\n" +" " +msgstr "" + +#: templates/account/verification_sent.html:165 +msgid "" +"If you do not see the verification email in your main inbox, please check " +"your spam folder." +msgstr "" + +#: templates/account/verification_sent.html:169 +msgid "" +"Please contact us if you do not receive the verification email within a few " +"minutes." +msgstr "" + +#: templates/account/verification_sent.html:176 +msgid "Change or Resend Email" +msgstr "" + +#: templates/admin/sync_dashboard.html:4 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/app_index.html:5 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/base_site.html:3 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/index.html:5 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:15 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/layouts/base.html:7 +msgid "Django site admin" +msgstr "" + +#: templates/agency_base.html:9 +#, fuzzy +#| msgid "King Abdullah Academic University Hospital" +msgid "King Abdullah Academic University Hospital - Agency Portal" +msgstr "مستشفى الملك عبدالله الجامعي" + +#: templates/agency_base.html:10 +msgid "KAAUH Agency Portal" +msgstr "" + +#: templates/agency_base.html:31 templates/base.html:38 +msgid "Saudi Vision 2030" +msgstr "رؤية السعودية 2030" + +#: templates/agency_base.html:59 templates/base.html:59 templates/base.html:63 +msgid "kaauh logo green bg" +msgstr "" + +#: templates/agency_base.html:60 templates/agency_base.html:137 +#: templates/recruitment/agency_portal_login.html:126 +#, fuzzy +#| msgid "Agency Name" +msgid "Agency Portal" +msgstr "اسم الوكالة" + +#: templates/agency_base.html:64 templates/base.html:68 +msgid "Toggle navigation" +msgstr "تبديل التنقل" + +#: templates/agency_base.html:75 templates/base.html:77 +#: templates/forms/partials/candidate_facing_base.html:267 +msgid "Toggle language menu" +msgstr "تبديل قائمة اللغة" + +#: templates/agency_base.html:103 templates/includes/easy_logs.html:261 +msgid "Logout" +msgstr "تسجيل الخروج" + +#: templates/agency_base.html:119 templates/base.html:302 +#: templates/jobs/job_detail.html:520 +#: templates/recruitment/candidate_exam_view.html:355 +#: templates/recruitment/candidate_hired_view.html:344 +#: templates/recruitment/candidate_interview_view.html:508 +#: templates/recruitment/candidate_screening_view.html:495 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/submit_line.html:19 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:52 +msgid "Close" +msgstr "إغلاق" + +#: templates/agency_base.html:133 templates/base.html:315 +msgid "King Abdullah Academic University Hospital (KAAUH)." +msgstr "مستشفى الملك عبدالله الجامعي (KAAUH)." + +#: templates/agency_base.html:134 templates/base.html:316 +msgid "All rights reserved." +msgstr "جميع الحقوق محفوظة." + +#: templates/agency_base.html:175 +#, fuzzy +#| msgid "Are you sure you want to sign out?" +msgid "Are you sure you want to logout?" +msgstr "هل أنت متأكد من رغبتك في تسجيل الخروج؟" + +#: templates/base.html:9 +msgid "King Abdullah Academic University Hospital - Applicant Tracking System" +msgstr "مستشفى الملك عبدالله الجامعي - نظام تتبع المتقدمين" + +#: templates/base.html:10 +msgid "University ATS" +msgstr "نظام تتبع المتقدمين الجامعي" + +#: templates/base.html:134 +msgid "Toggle user menu" +msgstr "تبديل قائمة المستخدم" + +#: templates/base.html:141 templates/base.html:143 templates/base.html:159 +msgid "Your account" +msgstr "حسابك" + +#: templates/base.html:175 +msgid "My Profile" +msgstr "ملفي الشخصي" + +#: templates/base.html:179 +msgid "Settings" +msgstr "الإعدادات" + +#: templates/base.html:180 +msgid "Activity Log" +msgstr "سجل النشاط" + +#: templates/base.html:192 +msgid "Connect LinkedIn" +msgstr "ربط LinkedIn" + +#: templates/base.html:198 +msgid "LinkedIn Connected" +msgstr "LinkedIn متصل" + +#: templates/base.html:210 +msgid "Sign out" +msgstr "تسجيل الخروج" + +#: templates/base.html:232 templates/jobs/job_candidates_list.html:123 +#: templates/recruitment/partials/ai_overview_breadcromb.html:49 +msgid "Jobs" +msgstr "الوظائف" + +#: templates/base.html:240 templates/jobs/job_candidates_list.html:125 +#: templates/jobs/job_candidates_list.html:207 +#: templates/jobs/job_detail.html:291 templates/jobs/job_list.html:354 +#: templates/recruitment/partials/ai_overview_breadcromb.html:71 +msgid "Applicants" +msgstr "المتقدمون" + +#: templates/base.html:248 +#, fuzzy +#| msgid "Hiring Agencies" +msgid "Agencies" +msgstr "وكالات التوظيف" + +#: templates/base.html:258 +msgid "Meetings" +msgstr "الاجتماعات" + +#: templates/base.html:270 +#: templates/recruitment/candidate_interview_view.html:475 +#: templates/recruitment/candidate_interview_view.html:495 +#, fuzzy +#| msgid "Participant Video" +msgid "Participants" +msgstr "فيديو المشارك" + +#: templates/base.html:320 +#, fuzzy +#| msgid "Created by" +msgid "Powered by" +msgstr "أنشأ بواسطة" + +#: templates/base.html:361 +msgid "Are you sure you want to sign out?" +msgstr "هل أنت متأكد من رغبتك في تسجيل الخروج؟" + +#: templates/forms/application_detail.html:7 +#: templates/forms/application_detail.html:61 +msgid "Job Overview" +msgstr "نظرة عامة على الوظيفة" + +#: templates/forms/application_detail.html:39 +msgid "Ready to Apply?" +msgstr "" + +#: templates/forms/application_detail.html:42 +msgid "Review the job details, then apply below." +msgstr "" + +#: templates/forms/application_detail.html:46 +#: templates/forms/application_detail.html:106 +msgid "Apply for this Position" +msgstr "" + +#: templates/forms/application_detail.html:66 +#: templates/jobs/job_detail.html:240 +msgid "Salary:" +msgstr "الراتب:" + +#: templates/forms/application_detail.html:73 +#: templates/jobs/job_detail.html:177 +#: templates/recruitment/agency_portal_submit_candidate.html:142 +msgid "Deadline:" +msgstr "الموعد النهائي:" + +#: templates/forms/application_detail.html:77 +msgid "EXPIRED" +msgstr "منتهي الصلاحية" + +#: templates/forms/application_detail.html:80 +msgid "Not specified" +msgstr "" + +#: templates/forms/application_detail.html:84 +#: templates/jobs/job_candidates_list.html:149 +#: templates/jobs/job_detail.html:231 +msgid "Job Type:" +msgstr "نوع الوظيفة:" + +#: templates/forms/application_detail.html:85 +#: templates/jobs/job_candidates_list.html:146 +#: templates/jobs/job_detail.html:237 +msgid "Location:" +msgstr "الموقع:" + +#: templates/forms/application_detail.html:86 +#: templates/jobs/job_candidates_list.html:143 +#: templates/jobs/job_detail.html:225 +#: templates/recruitment/agency_portal_submit_candidate.html:139 +msgid "Department:" +msgstr "القسم:" + +#: templates/forms/application_detail.html:87 +msgid "JOB ID:" +msgstr "" + +#: templates/forms/application_detail.html:88 +#: templates/jobs/job_candidates_list.html:152 +#: templates/jobs/job_detail.html:234 +msgid "Workplace:" +msgstr "مكان العمل:" + +#: templates/forms/application_detail.html:91 +#: templates/jobs/create_job.html:189 templates/jobs/edit_job.html:189 +#: templates/jobs/job_detail.html:256 +msgid "Job Description" +msgstr "وصف الوظيفة" + +#: templates/forms/application_detail.html:92 +#, fuzzy +#| msgid "Required Qualifications" +msgid "Qualifications" +msgstr "المؤهلات المطلوبة" + +#: templates/forms/application_detail.html:93 +#: templates/jobs/create_job.html:221 templates/jobs/edit_job.html:221 +#: templates/jobs/job_detail.html:268 +msgid "Benefits" +msgstr "المزايا" + +#: templates/forms/application_detail.html:94 +#: templates/jobs/create_job.html:229 templates/jobs/edit_job.html:229 +#: templates/jobs/job_detail.html:274 +msgid "Application Instructions" +msgstr "تعليمات التقديم" + +#: templates/forms/application_submit_form.html:489 +#: templates/forms/partials/candidate_facing_base.html:11 +msgid "Application Form" +msgstr "نموذج التقديم" + +#: templates/forms/application_submit_form.html:504 +msgid "Review Your Application" +msgstr "مراجعة تقديمك" + +#: templates/forms/application_submit_form.html:516 +msgid "Back" +msgstr "العودة" + +#: templates/forms/application_submit_form.html:520 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/pagination_infinite.html:9 +msgid "Next" +msgstr "التالي" + +#: templates/forms/application_submit_form.html:529 +msgid "Submit Application" +msgstr "إرسال التقديم" + +#: templates/forms/form_submission_details.html:160 +#, fuzzy +#| msgid "Job Details" +msgid "Submission Details" +msgstr "تفاصيل الوظيفة" + +#: templates/forms/form_submission_details.html:162 +#: templates/forms/form_template_all_submissions.html:249 +#: templates/forms/form_template_all_submissions.html:364 +#, fuzzy +#| msgid "Back to Jobs" +msgid "Back to Submissions" +msgstr "العودة إلى الوظائف" + +#: templates/forms/form_submission_details.html:170 +msgid "Submission Metadata" +msgstr "" + +#: templates/forms/form_submission_details.html:176 +msgid "Submission ID:" +msgstr "" + +#: templates/forms/form_submission_details.html:180 +#: templates/recruitment/agency_portal_submit_candidate.html:151 +#, fuzzy +#| msgid "Submit" +msgid "Submitted:" +msgstr "إرسال" + +#: templates/forms/form_submission_details.html:184 +msgid "Form:" +msgstr "" + +#: templates/forms/form_submission_details.html:192 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant Name:" +msgstr "المتقدمون" + +#: templates/forms/form_submission_details.html:198 +#, fuzzy +#| msgid "Email" +msgid "Email:" +msgstr "البريد الإلكتروني" + +#: templates/forms/form_submission_details.html:208 +#, fuzzy +#| msgid "Response" +msgid "Form Responses" +msgstr "الاستجابة" + +#: templates/forms/form_submission_details.html:219 +msgid "Field Property" +msgstr "" + +#: templates/forms/form_submission_details.html:227 +#, fuzzy +#| msgid "Response Data" +msgid "Response Value" +msgstr "بيانات الاستجابة" + +#: templates/forms/form_submission_details.html:233 +#, fuzzy +#| msgid "Download Resume" +msgid "Download File" +msgstr "تحميل السيرة الذاتية" + +#: templates/forms/form_submission_details.html:234 +#, fuzzy +#| msgid "Download Resume" +msgid "Download" +msgstr "تحميل السيرة الذاتية" + +#: templates/forms/form_submission_details.html:250 +#, fuzzy +#| msgid "No description provided" +msgid "Not provided" +msgstr "لم يتم تقديم وصف" + +#: templates/forms/form_submission_details.html:256 +#, fuzzy +#| msgid "Application Stage" +msgid "Associated Stage" +msgstr "مرحلة التقديم" + +#: templates/forms/form_submission_details.html:264 +msgid "Field Required" +msgstr "" + +#: templates/forms/form_submission_details.html:268 +#: templates/recruitment/candidate_detail.html:538 +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:867 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:88 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:96 +msgid "Yes" +msgstr "نعم" + +#: templates/forms/form_submission_details.html:270 +#: templates/recruitment/candidate_detail.html:540 +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:868 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:89 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:97 +msgid "No" +msgstr "لا" + +#: templates/forms/form_submission_details.html:281 +msgid "No response fields were found for this submission." +msgstr "" + +#: templates/forms/form_submission_details.html:282 +msgid "" +"This may occur if the form template was modified or responses were cleared." +msgstr "" + +#: templates/forms/form_template_all_submissions.html:232 +#: templates/forms/form_template_submissions_list.html:188 +#: templates/jobs/job_candidates_list.html:122 +#: templates/recruitment/agency_portal_submit_candidate.html:95 +msgid "Dashboard" +msgstr "لوحة التحكم" + +#: templates/forms/form_template_all_submissions.html:233 +#: templates/forms/form_template_submissions_list.html:189 +#: templates/forms/form_templates_list.html:152 +msgid "Form Templates" +msgstr "قوالب النماذج" + +#: templates/forms/form_template_all_submissions.html:234 +#: templates/forms/form_template_submissions_list.html:193 +#: templates/forms/form_templates_list.html:240 +#: templates/forms/form_templates_list.html:295 +#: templates/jobs/job_list.html:320 templates/jobs/job_list.html:373 +#: templates/recruitment/agency_access_link_detail.html:151 +msgid "Submissions" +msgstr "" + +#: templates/forms/form_template_all_submissions.html:235 +msgid "All Submissions Table" +msgstr "" + +#: templates/forms/form_template_all_submissions.html:244 +msgid "All Submissions for" +msgstr "" + +#: templates/forms/form_template_all_submissions.html:258 +#: templates/forms/form_template_submissions_list.html:227 +msgid "Submission ID" +msgstr "" + +#: templates/forms/form_template_all_submissions.html:259 +#: templates/forms/form_template_submissions_list.html:228 +#: templates/forms/form_template_submissions_list.html:265 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant Name" +msgstr "المتقدمون" + +#: templates/forms/form_template_all_submissions.html:260 +#: templates/forms/form_template_submissions_list.html:229 +#: templates/forms/form_template_submissions_list.html:266 +#, fuzzy +#| msgid "Applicants" +msgid "Applicant Email" +msgstr "المتقدمون" + +#: templates/forms/form_template_all_submissions.html:261 +#: templates/forms/form_template_submissions_list.html:230 +#: templates/forms/form_template_submissions_list.html:267 +#, fuzzy +#| msgid "Submitted by Agency" +msgid "Submitted At" +msgstr "مقدم من الوكالة" + +#: templates/forms/form_template_all_submissions.html:315 +#: templates/forms/form_template_submissions_list.html:286 +#, python-format +msgid "" +"\n" +" Showing %(start)s to %(end)s of %(total)s " +"results.\n" +" " +msgstr "" + +#: templates/forms/form_template_all_submissions.html:336 +#: templates/forms/form_template_submissions_list.html:307 +msgid "Page" +msgstr "" + +#: templates/forms/form_template_all_submissions.html:336 +#: templates/forms/form_template_submissions_list.html:307 +#: templates/includes/easy_logs.html:159 +msgid "of" +msgstr "" + +#: templates/forms/form_template_all_submissions.html:359 +#: templates/forms/form_template_submissions_list.html:330 +#, fuzzy +#| msgid "No meetings found." +msgid "No Submissions Found" +msgstr "لم يتم العثور على اجتماعات." + +#: templates/forms/form_template_all_submissions.html:361 +#: templates/forms/form_template_submissions_list.html:332 +msgid "There are no submissions for this form template yet." +msgstr "" + +#: templates/forms/form_template_submissions_list.html:202 +msgid "Submissions for" +msgstr "" + +#: templates/forms/form_template_submissions_list.html:208 +#, fuzzy +#| msgid "View All Applicants" +msgid "View All in Table" +msgstr "عرض جميع المتقدمين" + +#: templates/forms/form_template_submissions_list.html:211 +#: templates/forms/form_template_submissions_list.html:335 +#, fuzzy +#| msgid "Form Templates" +msgid "Back to Templates" +msgstr "قوالب النماذج" + +#: templates/forms/form_template_submissions_list.html:231 +#: templates/forms/form_templates_list.html:275 +#: templates/jobs/job_candidates_list.html:231 +#: templates/meetings/list_meetings.html:313 +#: templates/participants/participants_list.html:218 +#: templates/recruitment/agency_access_link_detail.html:168 +#: templates/recruitment/agency_assignment_detail.html:243 +#: templates/recruitment/agency_assignment_detail.html:339 +#: templates/recruitment/agency_assignment_list.html:117 +#: templates/recruitment/agency_list.html:191 +#: templates/recruitment/agency_portal_assignment_detail.html:245 +#: templates/recruitment/candidate_exam_view.html:255 +#: templates/recruitment/candidate_hired_view.html:237 +#: templates/recruitment/candidate_interview_view.html:269 +#: templates/recruitment/candidate_list.html:278 +#: templates/recruitment/candidate_offer_view.html:249 +#: templates/recruitment/candidate_screening_view.html:382 +#: templates/recruitment/notification_detail.html:126 +#: templates/recruitment/partials/_candidate_table.html:14 +#: templates/recruitment/training_list.html:207 +#: templates/user/admin_settings.html:179 +msgid "Actions" +msgstr "الإجراءات" + +#: templates/forms/form_template_submissions_list.html:243 +#: templates/forms/form_template_submissions_list.html:272 +#: templates/recruitment/agency_assignment_detail.html:268 +#: templates/recruitment/agency_assignment_list.html:160 +#: templates/recruitment/agency_portal_dashboard.html:209 +#, fuzzy +#| msgid "Core Details" +msgid "View Details" +msgstr "التفاصيل الأساسية" + +#: templates/forms/form_template_submissions_list.html:260 +#, fuzzy +#| msgid "Submit" +msgid "Submission" +msgstr "إرسال" + +#: templates/forms/form_templates_list.html:155 +#, fuzzy +#| msgid "Create Template" +msgid "Create New Template" +msgstr "إنشاء قالب" + +#: templates/forms/form_templates_list.html:165 +#, fuzzy +#| msgid "Template Name" +msgid "Search by Template Name" +msgstr "اسم القالب" + +#: templates/forms/form_templates_list.html:169 +msgid "Search templates by name..." +msgstr "البحث عن القوالب بالاسم..." + +#: templates/forms/form_templates_list.html:177 +#: templates/includes/search_form.html:16 +#: templates/recruitment/agency_assignment_list.html:77 +#: templates/recruitment/agency_assignment_list.html:98 +#: templates/recruitment/agency_list.html:167 +msgid "Search" +msgstr "بحث" + +#: templates/forms/form_templates_list.html:183 +#, fuzzy +#| msgid "Search" +msgid "Clear Search" +msgstr "بحث" + +#: templates/forms/form_templates_list.html:213 +#: templates/forms/form_templates_list.html:271 +msgid "Stages" +msgstr "المراحل" + +#: templates/forms/form_templates_list.html:217 +#: templates/forms/form_templates_list.html:272 +msgid "Fields" +msgstr "الحقول" + +#: templates/forms/form_templates_list.html:226 +msgid "No description provided" +msgstr "لم يتم تقديم وصف" + +#: templates/forms/form_templates_list.html:234 +#: templates/forms/form_templates_list.html:289 +#: templates/jobs/job_list.html:314 +msgid "Preview" +msgstr "معاينة" + +#: templates/forms/form_templates_list.html:237 +#: templates/forms/form_templates_list.html:292 +#: templates/jobs/job_candidates_list.html:257 +#: templates/jobs/job_candidates_list.html:353 templates/jobs/job_list.html:306 +#: templates/jobs/job_list.html:317 +#: templates/participants/participants_list.html:236 +#: templates/participants/participants_list.html:280 +#: templates/recruitment/agency_assignment_list.html:164 +#: templates/recruitment/agency_list.html:251 +#: templates/recruitment/agency_list.html:323 +#: templates/recruitment/candidate_list.html:329 +#: templates/recruitment/candidate_list.html:377 +#: templates/recruitment/training_list.html:181 +#: templates/recruitment/training_list.html:222 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:51 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:51 +msgid "Edit" +msgstr "تعديل" + +#: templates/forms/form_templates_list.html:243 +#: templates/forms/form_templates_list.html:298 +#: templates/jobs/job_candidates_list.html:260 +#: templates/jobs/job_candidates_list.html:356 +#: templates/meetings/list_meetings.html:284 +#: templates/meetings/list_meetings.html:372 +#: templates/participants/participants_detail.html:142 +#: templates/participants/participants_detail.html:255 +#: templates/participants/participants_list.html:239 +#: templates/participants/participants_list.html:282 +#: templates/participants/participants_list.html:337 +#: templates/recruitment/candidate_list.html:332 +#: templates/recruitment/candidate_list.html:379 +#: templates/recruitment/notification_detail.html:141 +#: templates/recruitment/training_list.html:183 +#: templates/recruitment/training_list.html:225 +#: templates/recruitment/training_update.html:184 +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:499 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_preview.html:26 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:60 +msgid "Delete" +msgstr "حذف" + +#: templates/forms/form_templates_list.html:254 +#: templates/recruitment/agency_detail.html:518 +#: templates/recruitment/agency_form.html:393 +#: templates/recruitment/notification_confirm_delete.html:23 +msgid "Created:" +msgstr "تم الإنشاء:" + +#: templates/forms/form_templates_list.html:255 +msgid "ago" +msgstr "منذ" + +#: templates/forms/form_templates_list.html:273 +#: templates/recruitment/agency_confirm_delete.html:264 +#: templates/recruitment/agency_list.html:190 +#: templates/recruitment/agency_list.html:328 +#: templates/recruitment/notification_detail.html:169 +#: templates/recruitment/training_list.html:206 +msgid "Created" +msgstr "تم الإنشاء" + +#: templates/forms/form_templates_list.html:274 +#: templates/participants/participants_detail.html:218 +#, fuzzy +#| msgid "Last Updated:" +msgid "Last Updated" +msgstr "آخر تحديث:" + +#: templates/forms/form_templates_list.html:346 +msgid "No Form Templates Found" +msgstr "لم يتم العثور على قوالب نماذج" + +#: templates/forms/form_templates_list.html:349 +#, python-format +msgid "No templates match your search \"%(query)s\"." +msgstr "لا توجد قوالب تطابق بحثك \"%(query)s\"." + +#: templates/forms/form_templates_list.html:351 +msgid "You haven't created any form templates yet." +msgstr "لم تقم بإنشاء أي قوالب نماذج بعد." + +#: templates/forms/form_templates_list.html:355 +msgid "Create Your First Template" +msgstr "إنشاء أول قالب لك" + +#: templates/forms/form_templates_list.html:370 +#: templates/jobs/job_detail.html:347 +#, fuzzy +#| msgid "Create New Form" +msgid "Create New Form Template" +msgstr "إنشاء نموذج جديد" + +#: templates/forms/partials/candidate_facing_base.html:244 +#: templates/jobs/application_success.html:126 +msgid "KAAUH IMAGE" +msgstr "صورة KAAUH" + +#: templates/forms/partials/candidate_facing_base.html:261 +#: templates/jobs/application_success.html:141 +msgid "Careers" +msgstr "" + +#: templates/includes/candidate_exam_status_form.html:6 +#: templates/meetings/list_meetings.html:369 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_preview.html:28 +msgid "Update" +msgstr "تحديث" + +#: templates/includes/candidate_modal_body.html:2 +#: templates/recruitment/candidate_exam_view.html:252 +#: templates/recruitment/candidate_screening_view.html:370 +#: templates/recruitment/partials/_candidate_table.html:11 +msgid "AI Score" +msgstr "" + +#: templates/includes/candidate_modal_body.html:8 +#, fuzzy +#| msgid "Job Title" +msgid "Job Fit" +msgstr "المسمى الوظيفي" + +#: templates/includes/candidate_modal_body.html:15 +#: templates/recruitment/candidate_detail.html:511 +msgid "Top Keywords" +msgstr "" + +#: templates/includes/candidate_modal_body.html:29 +msgid "Experience" +msgstr "" + +#: templates/includes/candidate_modal_body.html:31 +msgid "years" +msgstr "" + +#: templates/includes/candidate_modal_body.html:32 +msgid "Recent Role:" +msgstr "" + +#: templates/includes/candidate_modal_body.html:37 +msgid "Skills" +msgstr "" + +#: templates/includes/candidate_modal_body.html:39 +msgid "Soft Skills:" +msgstr "" + +#: templates/includes/candidate_modal_body.html:40 +msgid "Industry Match:" +msgstr "" + +#: templates/includes/candidate_modal_body.html:49 +#: templates/recruitment/candidate_detail.html:505 +msgid "Recommendation" +msgstr "" + +#: templates/includes/candidate_modal_body.html:54 +#: templates/recruitment/candidate_detail.html:496 +msgid "Strengths" +msgstr "" + +#: templates/includes/candidate_modal_body.html:59 +#: templates/recruitment/candidate_detail.html:499 +msgid "Weaknesses" +msgstr "" + +#: templates/includes/candidate_modal_body.html:64 +#: templates/recruitment/candidate_detail.html:551 +msgid "Criteria Assessment" +msgstr "" + +#: templates/includes/candidate_modal_body.html:69 +#: templates/recruitment/candidate_detail.html:556 +#, fuzzy +#| msgid "Create Material" +msgid "Criteria" +msgstr "إنشاء مادة" + +#: templates/includes/candidate_modal_body.html:79 +#: templates/includes/candidate_modal_body.html:100 +#: templates/recruitment/candidate_detail.html:566 +msgid "Met" +msgstr "" + +#: templates/includes/candidate_modal_body.html:81 +#: templates/includes/candidate_modal_body.html:102 +#: templates/recruitment/candidate_detail.html:568 +msgid "Not Met" +msgstr "" + +#: templates/includes/candidate_modal_body.html:97 +#, fuzzy +#| msgid "Description & Requirements" +msgid "Minimum Requirements" +msgstr "الوصف والمتطلبات" + +#: templates/includes/candidate_modal_body.html:108 +#: templates/recruitment/candidate_screening_view.html:275 +msgid "Screening Rating" +msgstr "" + +#: templates/includes/candidate_modal_body.html:116 +#: templates/recruitment/candidate_detail.html:583 +msgid "Language Fluency" +msgstr "" + +#: templates/includes/copy_to_clipboard.html:5 +#: templates/includes/easy_logs.html:269 +#: templates/recruitment/agency_assignment_detail.html:453 +msgid "Success" +msgstr "نجح" + +#: templates/includes/copy_to_clipboard.html:9 +#, python-format +msgid "Copied \"%(text)s\" to clipboard!" +msgstr "" + +#: templates/includes/easy_logs.html:5 +#, fuzzy +#| msgid "Dashboard" +msgid "Audit Dashboard" +msgstr "لوحة التحكم" + +#: templates/includes/easy_logs.html:153 +msgid "System Audit Logs" +msgstr "" + +#: templates/includes/easy_logs.html:157 +msgid "Viewing Logs" +msgstr "" + +#: templates/includes/easy_logs.html:159 +msgid "Displaying" +msgstr "" + +#: templates/includes/easy_logs.html:160 +msgid "total records." +msgstr "" + +#: templates/includes/easy_logs.html:197 templates/includes/easy_logs.html:206 +#: templates/includes/easy_logs.html:214 +#, fuzzy +#| msgid "Start Time" +msgid "Date/Time" +msgstr "وقت البدء" + +#: templates/includes/easy_logs.html:198 templates/includes/easy_logs.html:207 +#: templates/includes/easy_logs.html:215 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html:17 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:33 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:15 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:33 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:14 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:30 +msgid "User" +msgstr "" + +#: templates/includes/easy_logs.html:200 +msgid "Model" +msgstr "" + +#: templates/includes/easy_logs.html:201 +msgid "Object PK" +msgstr "" + +#: templates/includes/easy_logs.html:202 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:35 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:81 +#, fuzzy +#| msgid "Change Stage" +msgid "Changes" +msgstr "تغيير المرحلة" + +#: templates/includes/easy_logs.html:208 +#: templates/recruitment/notification_detail.html:161 +#: templates/recruitment/notification_list.html:46 +#, fuzzy +#| msgid "Job Type" +msgid "Type" +msgstr "نوع الوظيفة" + +#: templates/includes/easy_logs.html:216 +#, fuzzy +#| msgid "HTTP Method" +msgid "Method" +msgstr "طريقة HTTP" + +#: templates/includes/easy_logs.html:217 +msgid "Path" +msgstr "" + +#: templates/includes/easy_logs.html:234 +msgid "CREATE" +msgstr "" + +#: templates/includes/easy_logs.html:235 +msgid "UPDATE" +msgstr "" + +#: templates/includes/easy_logs.html:236 +msgid "DELETE" +msgstr "" + +#: templates/includes/easy_logs.html:260 +msgid "Login" +msgstr "تسجيل الدخول" + +#: templates/includes/easy_logs.html:262 +#, fuzzy +#| msgid "Failed" +msgid "Failed Login" +msgstr "رسب" + +#: templates/includes/easy_logs.html:288 +msgid "No logs found for this section or the database is empty." +msgstr "" + +#: templates/includes/email_compose_form.html:10 +#: templates/recruitment/candidate_interview_view.html:523 +#, fuzzy +#| msgid "Host Email" +msgid "Compose Email" +msgstr "بريد المضيف الإلكتروني" + +#: templates/includes/email_compose_form.html:103 +msgid "Email will be sent to all selected recipients" +msgstr "" + +#: templates/includes/email_compose_form.html:116 +#: templates/includes/email_compose_form.html:236 +#: templates/includes/email_compose_form.html:267 +#: templates/recruitment/agency_detail.html:501 +#, fuzzy +#| msgid "Email" +msgid "Send Email" +msgstr "البريد الإلكتروني" + +#: templates/includes/email_compose_form.html:131 +#: templates/recruitment/agency_portal_submit_candidate.html:381 +#: templates/recruitment/candidate_detail.html:599 +msgid "Loading..." +msgstr "" + +#: templates/includes/email_compose_form.html:134 +msgid "Sending email..." +msgstr "" + +#: templates/includes/email_compose_form.html:208 +msgid "Sending..." +msgstr "" + +#: templates/includes/meeting_form.html:15 +#, fuzzy +#| msgid "Start Time" +msgid "Start Time and Date" +msgstr "وقت البدء" + +#: templates/includes/meeting_form.html:20 +#: templates/meetings/create_meeting.html:170 +#: templates/meetings/reschedule_meeting.html:51 +#: templates/meetings/schedule_meeting_form.html:62 +#: templates/meetings/update_meeting.html:223 +#: templates/recruitment/schedule_meeting_form.html:72 +msgid "Duration (minutes)" +msgstr "المدة (دقائق)" + +#: templates/includes/meeting_form.html:25 +msgid "Meeting Details (will appear after scheduling):" +msgstr "" + +#: templates/includes/meeting_form.html:26 +#, fuzzy +#| msgid "Join URL" +msgid "Join URL:" +msgstr "رابط الانضمام" + +#: templates/includes/meeting_form.html:27 +#, fuzzy +#| msgid "Meeting ID" +msgid "Meeting ID:" +msgstr "معرف الاجتماع" + +#: templates/includes/meeting_form.html:32 +msgid "Click here to join meeting" +msgstr "" + +#: templates/includes/meeting_form.html:42 +#, fuzzy +#| msgid "Delete Meeting" +msgid "Reschedule Meeting" +msgstr "حذف الاجتماع" + +#: templates/includes/meeting_form.html:42 +#: templates/includes/meeting_form.html:71 +#: templates/meetings/schedule_meeting_form.html:84 +#: templates/recruitment/schedule_meeting_form.html:4 +#: templates/recruitment/schedule_meeting_form.html:86 +#, fuzzy +#| msgid "Delete Meeting" +msgid "Schedule Meeting" +msgstr "حذف الاجتماع" + +#: templates/includes/meeting_form.html:67 +#: templates/meetings/schedule_meeting_form.html:9 +#: templates/recruitment/candidate_interview_view.html:407 +#: templates/recruitment/schedule_meeting_form.html:15 +#, fuzzy +#| msgid "Interview" +msgid "Schedule Interview" +msgstr "المقابلة" + +#: templates/includes/meeting_form.html:83 +msgid "Processing..." +msgstr "" + +#: templates/includes/meeting_form.html:129 +msgid "An unknown error occurred." +msgstr "" + +#: templates/includes/meeting_form.html:137 +msgid "An error occurred while processing your request." +msgstr "" + +#: templates/includes/search_form.html:14 +msgid "Search..." +msgstr "بحث..." + +#: templates/interviews/schedule_interviews.html:110 +msgid "Bulk Interview Scheduling" +msgstr "" + +#: templates/interviews/schedule_interviews.html:113 +msgid "Configure time slots for:" +msgstr "" + +#: templates/interviews/schedule_interviews.html:117 +#: templates/recruitment/candidate_exam_view.html:187 +#: templates/recruitment/candidate_hired_view.html:211 +#: templates/recruitment/candidate_interview_view.html:190 +#: templates/recruitment/candidate_offer_view.html:189 +#: templates/recruitment/candidate_screening_view.html:238 +#, fuzzy +#| msgid "Back to Jobs" +msgid "Back to Job" +msgstr "العودة إلى الوظائف" + +#: templates/interviews/schedule_interviews.html:127 +#, fuzzy +#| msgid "Delete Candidate" +msgid "Select Candidates" +msgstr "حذف المرشح" + +#: templates/interviews/schedule_interviews.html:131 +msgid "Candidates to Schedule (Hold Ctrl/Cmd to select multiple)" +msgstr "" + +#: templates/interviews/schedule_interviews.html:141 +#, fuzzy +#| msgid "Schedule" +msgid "Schedule Details" +msgstr "الجدول" + +#: templates/interviews/schedule_interviews.html:183 +#, fuzzy +#| msgid "Duration (minutes)" +msgid "Duration (min)" +msgstr "المدة (دقائق)" + +#: templates/interviews/schedule_interviews.html:190 +#, fuzzy +#| msgid "Buffer Time (minutes)" +msgid "Buffer (min)" +msgstr "وقت المخزن المؤقت (دقائق)" + +#: templates/interviews/schedule_interviews.html:197 +#, fuzzy +#| msgid "Break End Time" +msgid "Daily Break Times" +msgstr "وقت انتهاء الاستراحة" + +#: templates/interviews/schedule_interviews.html:218 +#, fuzzy +#| msgid "Schedule" +msgid "Preview Schedule" +msgstr "الجدول" + +#: templates/jobs/application_success.html:7 +#, fuzzy +#| msgid "Application Stage" +msgid "Application Submitted - Thank You" +msgstr "مرحلة التقديم" + +#: templates/jobs/application_success.html:135 +#: templates/recruitment/dashboard.html:263 +msgid "Applications" +msgstr "التقديمات" + +#: templates/jobs/application_success.html:138 +msgid "Profile" +msgstr "الملف الشخصي" + +#: templates/jobs/application_success.html:150 +#, fuzzy +#| msgid "Application Information" +msgid "Application Confirmation" +msgstr "معلومات التقديم" + +#: templates/jobs/application_success.html:168 +msgid "Thank You!" +msgstr "" + +#: templates/jobs/application_success.html:169 +msgid "Your application has been submitted successfully" +msgstr "" + +#: templates/jobs/application_success.html:183 +msgid "" +"We appreciate your interest in joining our team. Our hiring team will review " +"your application and contact you if there's a potential match for this " +"position." +msgstr "" + +#: templates/jobs/application_success.html:188 +msgid "Return to Job Listings" +msgstr "" + +#: templates/jobs/career.html:224 templates/jobs/career.html:237 +msgid "Job ID#" +msgstr "" + +#: templates/jobs/career.html:225 templates/jobs/career.html:239 +#: templates/jobs/create_job.html:122 templates/jobs/edit_job.html:122 +#: templates/meetings/meeting_details.html:251 +#: templates/recruitment/agency_portal_assignment_detail.html:143 +msgid "Job Title" +msgstr "المسمى الوظيفي" + +#: templates/jobs/career.html:226 templates/jobs/career.html:241 +#, fuzzy +#| msgid "Hiring Agency" +msgid "Hiring" +msgstr "وكالة التوظيف" + +#: templates/jobs/career.html:227 templates/jobs/career.html:244 +#, fuzzy +#| msgid "Join Date" +msgid "Posting Date" +msgstr "تاريخ الانضمام" + +#: templates/jobs/career.html:228 templates/jobs/career.html:247 +#, fuzzy +#| msgid "Applied for:" +msgid "Apply Before" +msgstr "تقدم لـ:" + +#: templates/jobs/career.html:229 templates/jobs/career.html:249 +#: templates/recruitment/candidate_interview_view.html:266 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:27 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:98 +msgid "Link" +msgstr "ربط" + +#: templates/jobs/career.html:253 +#: templates/participants/participants_list.html:186 +#: templates/recruitment/candidate_list.html:243 +#, fuzzy +#| msgid "Applied" +msgid "Apply" +msgstr "تم التقديم" + +#: templates/jobs/create_job.html:105 templates/jobs/edit_job.html:105 +#, fuzzy +#| msgid "Create New Job Posting" +msgid "Edit Job Posting" +msgstr "إنشاء إعلان وظيفة جديد" + +#: templates/jobs/create_job.html:105 templates/jobs/edit_job.html:105 +msgid "Create New Job Posting" +msgstr "إنشاء إعلان وظيفة جديد" + +#: templates/jobs/create_job.html:116 templates/jobs/edit_job.html:116 +#, fuzzy +#| msgid "Core Details" +msgid "Core Position Details" +msgstr "التفاصيل الأساسية" + +#: templates/jobs/create_job.html:129 templates/jobs/edit_job.html:129 +#: templates/meetings/meeting_details.html:254 +msgid "Job Type" +msgstr "نوع الوظيفة" + +#: templates/jobs/create_job.html:137 templates/jobs/edit_job.html:137 +msgid "Workplace Type" +msgstr "نوع مكان العمل" + +#: templates/jobs/create_job.html:144 templates/jobs/edit_job.html:144 +msgid "Application Deadline" +msgstr "موعد نهائي للتقديم" + +#: templates/jobs/create_job.html:151 templates/jobs/edit_job.html:151 +msgid "Department" +msgstr "القسم" + +#: templates/jobs/create_job.html:158 templates/jobs/edit_job.html:158 +#: templates/recruitment/partials/stats_cards.html:37 +msgid "Open Positions" +msgstr "المناصب المفتوحة" + +#: templates/jobs/create_job.html:165 templates/jobs/edit_job.html:165 +#, fuzzy +#| msgid "Applications" +msgid "Max Applications" +msgstr "التقديمات" + +#: templates/jobs/create_job.html:183 templates/jobs/edit_job.html:183 +#, fuzzy +#| msgid "Content" +msgid "Job Content" +msgstr "المحتوى" + +#: templates/jobs/create_job.html:197 templates/jobs/edit_job.html:197 +msgid "Qualifications and Requirements" +msgstr "المؤهلات والمتطلبات" + +#: templates/jobs/create_job.html:211 templates/jobs/edit_job.html:211 +#, fuzzy +#| msgid "Application Instructions" +msgid "Benefits & Application Instructions" +msgstr "تعليمات التقديم" + +#: templates/jobs/create_job.html:243 templates/jobs/edit_job.html:243 +#, fuzzy +#| msgid "Internal Information" +msgid "Internal & Promotion" +msgstr "المعلومات الداخلية" + +#: templates/jobs/create_job.html:249 templates/jobs/edit_job.html:249 +msgid "Position Number" +msgstr "رقم المنصب" + +#: templates/jobs/create_job.html:256 templates/jobs/edit_job.html:256 +msgid "Reports To" +msgstr "يقدم تقاريره إلى" + +#: templates/jobs/create_job.html:266 templates/jobs/edit_job.html:266 +msgid "Hashtags (For Promotion/Search on Linkedin)" +msgstr "" + +#: templates/jobs/create_job.html:269 templates/jobs/edit_job.html:269 +#, fuzzy +#| msgid "Comma-separated list of trusted IP addresses" +msgid "Comma-separated list of hashtags, e.g., #hiring, #professor" +msgstr "قائمة عناوين IP الموثوقة مفصولة بفواصل" + +#: templates/jobs/create_job.html:282 templates/jobs/edit_job.html:282 +#, fuzzy +#| msgid "Location" +msgid "Location & Salary" +msgstr "الموقع" + +#: templates/jobs/create_job.html:288 templates/jobs/edit_job.html:288 +#: templates/recruitment/agency_detail.html:351 +msgid "City" +msgstr "المدينة" + +#: templates/jobs/create_job.html:295 templates/jobs/edit_job.html:295 +msgid "State/Province" +msgstr "الولاية/المقاطعة" + +#: templates/jobs/create_job.html:312 templates/jobs/edit_job.html:312 +msgid "Salary Range" +msgstr "نطاق الراتب" + +#: templates/jobs/create_job.html:330 templates/jobs/edit_job.html:330 +#, fuzzy +#| msgid "Create Job" +msgid "Save Job" +msgstr "إنشاء وظيفة" + +#: templates/jobs/job_candidates_list.html:118 +#, fuzzy +#| msgid "Applicants" +msgid "Applicants for" +msgstr "المتقدمون" + +#: templates/jobs/job_candidates_list.html:131 +#, fuzzy +#| msgid "New Application Stage" +msgid "Add New Applicant" +msgstr "مرحلة تقديم جديدة" + +#: templates/jobs/job_candidates_list.html:162 +#: templates/jobs/job_detail.html:315 +#, fuzzy +#| msgid "Applicants" +msgid "Total Applicants" +msgstr "المتقدمون" + +#: templates/jobs/job_candidates_list.html:175 +#, fuzzy +#| msgid "Applicants" +msgid "Search Applicants" +msgstr "المتقدمون" + +#: templates/jobs/job_candidates_list.html:179 +msgid "Search by name, email, phone, or stage..." +msgstr "" + +#: templates/jobs/job_candidates_list.html:185 +msgid "Filter Results" +msgstr "" + +#: templates/jobs/job_candidates_list.html:190 +#: templates/recruitment/notification_list.html:200 +msgid "Clear Filters" +msgstr "" + +#: templates/jobs/job_candidates_list.html:210 +#: templates/recruitment/candidate_list.html:230 +#, fuzzy +#| msgid "Stages" +msgid "All Stages" +msgstr "المراحل" + +#: templates/jobs/job_candidates_list.html:226 +#: templates/meetings/meeting_details.html:303 +#: templates/participants/participants_list.html:212 +#: templates/recruitment/agency_assignment_detail.html:239 +#: templates/recruitment/agency_portal_assignment_detail.html:241 +#: templates/recruitment/candidate_exam_view.html:250 +#: templates/recruitment/candidate_hired_view.html:232 +#: templates/recruitment/candidate_interview_view.html:261 +#: templates/recruitment/candidate_list.html:269 +#: templates/recruitment/candidate_offer_view.html:246 +#: templates/recruitment/candidate_screening_view.html:364 +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:19 +msgid "Name" +msgstr "الاسم" + +#: templates/jobs/job_candidates_list.html:230 +#: templates/jobs/job_candidates_list.html:343 +#: templates/recruitment/candidate_detail.html:355 +msgid "Applied Date" +msgstr "تاريخ التقديم" + +#: templates/jobs/job_candidates_list.html:253 templates/jobs/job_list.html:303 +#: templates/jobs/job_list.html:365 templates/meetings/list_meetings.html:272 +#: templates/meetings/list_meetings.html:366 +#: templates/participants/participants_list.html:232 +#: templates/participants/participants_list.html:276 +#: templates/recruitment/agency_list.html:246 +#: templates/recruitment/agency_list.html:319 +#: templates/recruitment/candidate_list.html:325 +#: templates/recruitment/candidate_list.html:373 +#: templates/recruitment/candidate_update.html:104 +#: templates/recruitment/training_list.html:177 +#: templates/recruitment/training_list.html:218 +#: templates/recruitment/training_update.html:119 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/edit_inline/stacked.html:65 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:38 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_title.html:25 +msgid "View" +msgstr "عرض" + +#: templates/jobs/job_candidates_list.html:277 +#, fuzzy +#| msgid "Rejected" +msgid "Selected" +msgstr "رفض" + +#: templates/jobs/job_candidates_list.html:280 +#, fuzzy +#| msgid "Interview" +msgid "Mark Interview" +msgstr "المقابلة" + +#: templates/jobs/job_candidates_list.html:284 +#, fuzzy +#| msgid "Offer" +msgid "Mark Offer" +msgstr "العرض" + +#: templates/jobs/job_candidates_list.html:349 +#, fuzzy +#| msgid "Profile" +msgid "View Profile" +msgstr "الملف الشخصي" + +#: templates/jobs/job_candidates_list.html:376 +#, fuzzy +#| msgid "No applicants yet" +msgid "No applicants found" +msgstr "لا يوجد متقدمون بعد" + +#: templates/jobs/job_candidates_list.html:377 +#, fuzzy +#| msgid "Candidates will appear here once they apply for this position." +msgid "There are no candidates who have applied for this position yet." +msgstr "سيظهر المرشحون هنا بمجرد تقديمهم لهذا المنصب." + +#: templates/jobs/job_candidates_list.html:379 +#, fuzzy +#| msgid "Applicants" +msgid "Add First Applicant" +msgstr "المتقدمون" + +#: templates/jobs/job_detail.html:171 +msgid "JOB ID: " +msgstr "" + +#: templates/jobs/job_detail.html:208 +msgid "Share Public Link" +msgstr "" + +#: templates/jobs/job_detail.html:212 +#: templates/meetings/meeting_details.html:270 +#: templates/meetings/meeting_details.html:475 +msgid "Copied!" +msgstr "" + +#: templates/jobs/job_detail.html:220 +msgid "Administrative & Location" +msgstr "الإداري والموقع" + +#: templates/jobs/job_detail.html:221 +#, fuzzy +#| msgid "Edit Job" +msgid "Edit JOb" +msgstr "تعديل الوظيفة" + +#: templates/jobs/job_detail.html:228 +msgid "Position No:" +msgstr "رقم المنصب:" + +#: templates/jobs/job_detail.html:243 +msgid "Created By:" +msgstr "أنشأ بواسطة:" + +#: templates/jobs/job_detail.html:246 +#, fuzzy +#| msgid "Created at" +msgid "Created At:" +msgstr "تم الإنشاء في" + +#: templates/jobs/job_detail.html:249 +#, fuzzy +#| msgid "Updated at" +msgid "Updated At:" +msgstr "تم التحديث في" + +#: templates/jobs/job_detail.html:262 +msgid "Required Qualifications" +msgstr "المؤهلات المطلوبة" + +#: templates/jobs/job_detail.html:296 +msgid "Tracking" +msgstr "" + +#: templates/jobs/job_detail.html:301 +#, fuzzy +#| msgid "Form Templates" +msgid "Form Template" +msgstr "قوالب النماذج" + +#: templates/jobs/job_detail.html:306 +#, fuzzy +#| msgid "Connect LinkedIn" +msgid "LinkedIn" +msgstr "ربط LinkedIn" + +#: templates/jobs/job_detail.html:319 +#, fuzzy +#| msgid "Create Template" +msgid "Create Applicant" +msgstr "إنشاء قالب" + +#: templates/jobs/job_detail.html:322 +#, fuzzy +#| msgid "Applicants" +msgid "Manage Applicants" +msgstr "المتقدمون" + +#: templates/jobs/job_detail.html:329 +#, fuzzy +#| msgid "Application Stage" +msgid "Applicant Stages" +msgstr "مرحلة التقديم" + +#: templates/jobs/job_detail.html:332 +msgid "" +"The applicant tracking flow is defined by the attached Form Template. View " +"the Form Template tab to manage stages and fields." +msgstr "" + +#: templates/jobs/job_detail.html:339 +msgid "Form Management" +msgstr "إدارة النماذج" + +#: templates/jobs/job_detail.html:342 +msgid "Manage the custom application forms associated with this job posting." +msgstr "إدارة نماذج التقديم المخصصة المرتبطة بهذا الإعلان الوظيفي." + +#: templates/jobs/job_detail.html:352 +#, fuzzy +#| msgid "Form Templates" +msgid "View Form Template" +msgstr "قوالب النماذج" + +#: templates/jobs/job_detail.html:355 +msgid "" +"This job status is not active, the form will appear once the job is made " +"active" +msgstr "" + +#: templates/jobs/job_detail.html:368 +msgid "LinkedIn Integration" +msgstr "تكامل LinkedIn" + +#: templates/jobs/job_detail.html:372 +msgid "Posted successfully!" +msgstr "تم النشر بنجاح!" + +#: templates/jobs/job_detail.html:376 +msgid "View on LinkedIn" +msgstr "عرض على LinkedIn" + +#: templates/jobs/job_detail.html:380 +msgid "Posted on:" +msgstr "تم النشر في:" + +#: templates/jobs/job_detail.html:383 +msgid "This job has not been posted to LinkedIn yet." +msgstr "لم يتم نشر هذه الوظيفة على LinkedIn بعد." + +#: templates/jobs/job_detail.html:391 +msgid "Re-post to LinkedIn" +msgstr "إعادة النشر على LinkedIn" + +#: templates/jobs/job_detail.html:391 +msgid "Post to LinkedIn" +msgstr "النشر على LinkedIn" + +#: templates/jobs/job_detail.html:396 +msgid "Upload Image for Post" +msgstr "رفع صورة للمنشور" + +#: templates/jobs/job_detail.html:401 +msgid "You need to" +msgstr "تحتاج إلى" + +#: templates/jobs/job_detail.html:401 +msgid "authenticate with LinkedIn" +msgstr "المصادقة مع LinkedIn" + +#: templates/jobs/job_detail.html:401 +msgid "first." +msgstr "أولاً." + +#: templates/jobs/job_detail.html:408 +#: templates/recruitment/candidate_hired_view.html:466 +msgid "Error:" +msgstr "خطأ:" + +#: templates/jobs/job_detail.html:413 +#, fuzzy +#| msgid "LinkedIn Connected" +msgid "Update LinkedIn Content" +msgstr "LinkedIn متصل" + +#: templates/jobs/job_detail.html:426 +msgid "Candidate Categories & Scores" +msgstr "" + +#: templates/jobs/job_detail.html:441 +msgid "Key Performance Indicators" +msgstr "" + +#: templates/jobs/job_detail.html:454 +msgid "Avg. AI Score" +msgstr "" + +#: templates/jobs/job_detail.html:465 +#: templates/recruitment/partials/stats_cards.html:97 +msgid "High Potential" +msgstr "" + +#: templates/jobs/job_detail.html:476 +#, fuzzy +#| msgid "Interview" +msgid "Time to Interview" +msgstr "المقابلة" + +#: templates/jobs/job_detail.html:487 +msgid "Avg. Exam Review" +msgstr "" + +#: templates/jobs/job_detail.html:497 +msgid "Vacancy Fill Rate" +msgstr "" + +#: templates/jobs/job_detail.html:519 +#, fuzzy +#| msgid "Edit Job" +msgid "Edit Job Status" +msgstr "تعديل الوظيفة" + +#: templates/jobs/job_detail.html:525 +#, fuzzy +#| msgid "Sync Status" +msgid "Select New Status" +msgstr "حالة المزامنة" + +#: templates/jobs/job_detail.html:531 +msgid "Status form not available. Please check your view." +msgstr "" + +#: templates/jobs/job_detail.html:537 +#: templates/meetings/meeting_details.html:396 +#: templates/recruitment/agency_portal_assignment_detail.html:578 +#: templates/user/profile.html:147 +msgid "Save Changes" +msgstr "" + +#: templates/jobs/job_list.html:204 +#, fuzzy +#| msgid "Create New Job Posting" +msgid "Job Postings" +msgstr "إنشاء إعلان وظيفة جديد" + +#: templates/jobs/job_list.html:207 +#, fuzzy +#| msgid "Create Job" +msgid "Create New Job" +msgstr "إنشاء وظيفة" + +#: templates/jobs/job_list.html:216 +msgid "Search by Title or Department" +msgstr "" + +#: templates/jobs/job_list.html:226 templates/meetings/list_meetings.html:200 +#, fuzzy +#| msgid "Offer Status" +msgid "Filter by Status" +msgstr "حالة العرض" + +#: templates/jobs/job_list.html:228 templates/meetings/list_meetings.html:202 +#: templates/recruitment/agency_assignment_list.html:86 +#, fuzzy +#| msgid "Status" +msgid "All Statuses" +msgstr "الحالة" + +#: templates/jobs/job_list.html:229 +#, fuzzy +#| msgid "Draft Jobs" +msgid "Draft" +msgstr "مسودات الوظائف" + +#: templates/jobs/job_list.html:231 +#, fuzzy +#| msgid "Close" +msgid "Closed" +msgstr "إغلاق" + +#: templates/jobs/job_list.html:232 +msgid "Archived" +msgstr "" + +#: templates/jobs/job_list.html:238 templates/meetings/list_meetings.html:215 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/change_list_filter_actions.html:7 +#, fuzzy +#| msgid "Applicants" +msgid "Apply Filters" +msgstr "المتقدمون" + +#: templates/jobs/job_list.html:242 templates/meetings/list_meetings.html:219 +#: templates/participants/participants_list.html:190 +#: templates/recruitment/candidate_list.html:247 +#: templates/recruitment/notification_list.html:60 +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:527 +msgid "Clear" +msgstr "مسح" + +#: templates/jobs/job_list.html:268 +#, fuzzy +#| msgid "Job Title" +msgid "Job Title / ID" +msgstr "المسمى الوظيفي" + +#: templates/jobs/job_list.html:270 +msgid "Max Apps" +msgstr "" + +#: templates/jobs/job_list.html:271 +#: templates/recruitment/agency_assignment_detail.html:144 +#: templates/recruitment/agency_assignment_list.html:115 +#: templates/recruitment/agency_portal_assignment_detail.html:158 +#: templates/recruitment/agency_portal_dashboard.html:162 +#, fuzzy +#| msgid "Deadline:" +msgid "Deadline" +msgstr "الموعد النهائي:" + +#: templates/jobs/job_list.html:273 +#, fuzzy +#| msgid "Candidate Form" +msgid "Manage Forms" +msgstr "نموذج المرشح" + +#: templates/jobs/job_list.html:276 +#, fuzzy +#| msgid "Applicants" +msgid "Applicants Metrics" +msgstr "المتقدمون" + +#: templates/jobs/job_list.html:281 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:22 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:65 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:78 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/dropdown_filters.py:22 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/mixins.py:45 +msgid "All" +msgstr "الكل" + +#: templates/jobs/job_list.html:282 +#: templates/jobs/partials/applicant_tracking.html:112 +msgid "Screened" +msgstr "" + +#: templates/jobs/job_list.html:355 +#, fuzzy +#| msgid "Offer Date" +msgid "Offers Made" +msgstr "تاريخ العرض" + +#: templates/jobs/job_list.html:356 +msgid "Form" +msgstr "" + +#: templates/jobs/job_list.html:359 +msgid "N/A" +msgstr "" + +#: templates/jobs/job_list.html:366 +#, fuzzy +#| msgid "Job Details" +msgid "Details" +msgstr "تفاصيل الوظيفة" + +#: templates/jobs/job_list.html:369 +msgid "Edit Job" +msgstr "تعديل الوظيفة" + +#: templates/jobs/job_list.html:392 +#, fuzzy +#| msgid "No meetings found." +msgid "No job postings found" +msgstr "لم يتم العثور على اجتماعات." + +#: templates/jobs/job_list.html:393 +msgid "Create your first job posting to get started or adjust your filters." +msgstr "" + +#: templates/meetings/create_meeting.html:4 +msgid "Create Zoom Meeting" +msgstr "إنشاء اجتماع Zoom" + +#: templates/meetings/create_meeting.html:151 +msgid "Create New Zoom Meeting" +msgstr "إنشاء اجتماع Zoom جديد" + +#: templates/meetings/create_meeting.html:155 +#: templates/meetings/meeting_details.html:210 +msgid "Back to Meetings" +msgstr "العودة إلى الاجتماعات" + +#: templates/meetings/delete_meeting_form.html:4 +#, fuzzy +#| msgid "Are you sure you want to delete this material?" +msgid "" +"Are you sure you want to delete this meeting? This action is irreversible." +msgstr "هل أنت متأكد من رغبتك في حذف هذه المادة؟" + +#: templates/meetings/delete_meeting_form.html:7 +#: templates/meetings/meeting_details.html:222 +msgid "Delete Meeting" +msgstr "حذف الاجتماع" + +#: templates/meetings/list_meetings.html:4 +#: templates/meetings/list_meetings.html:176 +msgid "Zoom Meetings" +msgstr "اجتماعات Zoom" + +#: templates/meetings/list_meetings.html:187 +msgid "Search by Topic" +msgstr "" + +#: templates/meetings/list_meetings.html:209 +#: templates/meetings/meeting_details.html:252 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidate Name" +msgstr "نموذج المرشح" + +#: templates/meetings/list_meetings.html:210 +#, fuzzy +#| msgid "Search templates by name..." +msgid "Search by candidate..." +msgstr "البحث عن القوالب بالاسم..." + +#: templates/meetings/list_meetings.html:264 +#: templates/meetings/list_meetings.html:309 +#: templates/user/admin_settings.html:173 +msgid "ID" +msgstr "المعرف" + +#: templates/meetings/list_meetings.html:265 +#, fuzzy +#| msgid "Started" +msgid "Start" +msgstr "بدأ" + +#: templates/meetings/list_meetings.html:277 +#: templates/meetings/list_meetings.html:362 +#, fuzzy +#| msgid "Join URL" +msgid "Join" +msgstr "رابط الانضمام" + +#: templates/meetings/list_meetings.html:423 +#, fuzzy +#| msgid "No meetings found." +msgid "No Zoom meetings found" +msgstr "لم يتم العثور على اجتماعات." + +#: templates/meetings/list_meetings.html:424 +msgid "Create your first meeting or adjust your filters." +msgstr "" + +#: templates/meetings/list_meetings.html:426 +msgid "Create Your First Meeting" +msgstr "إنشاء أول اجتماع لك" + +#: templates/meetings/meeting_details.html:216 +#, fuzzy +#| msgid "Update Meeting" +msgid "Edit Meeting" +msgstr "تحديث الاجتماع" + +#: templates/meetings/meeting_details.html:221 +#, fuzzy +#| msgid "Are you sure you want to delete this material?" +msgid "Are you sure you want to delete this meeting? This action is permanent." +msgstr "هل أنت متأكد من رغبتك في حذف هذه المادة؟" + +#: templates/meetings/meeting_details.html:249 +#, fuzzy +#| msgid "Interview Date" +msgid "Interview Detail" +msgstr "تاريخ المقابلة" + +#: templates/meetings/meeting_details.html:253 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidate Email" +msgstr "نموذج المرشح" + +#: templates/meetings/meeting_details.html:262 +#, fuzzy +#| msgid "Core Details" +msgid "Connection Details" +msgstr "التفاصيل الأساسية" + +#: templates/meetings/meeting_details.html:264 +#, fuzzy +#| msgid "Start Time" +msgid "Date & Time" +msgstr "وقت البدء" + +#: templates/meetings/meeting_details.html:265 +#: templates/recruitment/notification_detail.html:71 +msgid "minutes" +msgstr "" + +#: templates/meetings/meeting_details.html:267 +msgid "Host Email" +msgstr "بريد المضيف الإلكتروني" + +#: templates/meetings/meeting_details.html:277 +#, fuzzy +#| msgid "Join URL" +msgid "Copy URL" +msgstr "رابط الانضمام" + +#: templates/meetings/meeting_details.html:298 +msgid "Assigned Participants" +msgstr "" + +#: templates/meetings/meeting_details.html:304 +#, fuzzy +#| msgid "Toggle navigation" +msgid "Role/Designation" +msgstr "تبديل التنقل" + +#: templates/meetings/meeting_details.html:317 +msgid "External Participants" +msgstr "" + +#: templates/meetings/meeting_details.html:326 +msgid "System User" +msgstr "" + +#: templates/meetings/meeting_details.html:346 +msgid "Comments" +msgstr "التعليقات" + +#: templates/meetings/meeting_details.html:369 +#: templates/meetings/meeting_details.html:391 +#, fuzzy +#| msgid "Edit Job" +msgid "Edit Comment" +msgstr "تعديل الوظيفة" + +#: templates/meetings/meeting_details.html:376 +#, fuzzy +#| msgid "Delete Candidate" +msgid "Delete Comment" +msgstr "حذف المرشح" + +#: templates/meetings/meeting_details.html:376 +#, fuzzy +#| msgid "Are you sure you want to delete this item?" +msgid "Are you sure you want to delete this comment?" +msgstr "هل أنت متأكد من رغبتك في حذف هذا العنصر؟" + +#: templates/meetings/meeting_details.html:407 +msgid "No comments yet. Be the first to comment!" +msgstr "" + +#: templates/meetings/meeting_details.html:414 +#, fuzzy +#| msgid "Add New Candidate" +msgid "Add a New Comment" +msgstr "إضافة مرشح جديد" + +#: templates/meetings/meeting_details.html:427 +#, fuzzy +#| msgid "Submit" +msgid "Submit Comment" +msgstr "إرسال" + +#: templates/meetings/meeting_details.html:431 +msgid "You must be logged in to add a comment." +msgstr "" + +#: templates/meetings/meeting_details.html:475 +#, fuzzy +#| msgid "Failed" +msgid "Copy Failed." +msgstr "رسب" + +#: templates/meetings/reschedule_meeting.html:9 +#: templates/meetings/schedule_meeting_form.html:7 +#: templates/recruitment/schedule_meeting_form.html:13 +#, fuzzy +#| msgid "Interview" +msgid "Update Interview" +msgstr "المقابلة" + +#: templates/meetings/reschedule_meeting.html:15 +msgid "You are updating the existing meeting schedule." +msgstr "" + +#: templates/meetings/reschedule_meeting.html:27 +#: templates/meetings/schedule_meeting_form.html:27 +#: templates/recruitment/schedule_meeting_form.html:38 +#, fuzzy +#| msgid "Meeting ID" +msgid "Meeting Topic" +msgstr "معرف الاجتماع" + +#: templates/meetings/reschedule_meeting.html:65 +#: templates/meetings/schedule_meeting_form.html:82 +#: templates/meetings/update_meeting.html:233 +msgid "Update Meeting" +msgstr "تحديث الاجتماع" + +#: templates/meetings/schedule_meeting_form.html:16 +msgid "Candidate has upcoming interviews. Updating existing schedule." +msgstr "" + +#: templates/meetings/schedule_meeting_form.html:38 +msgid "e.g., Technical Screening, HR Interview" +msgstr "" + +#: templates/meetings/set_candidate_form.html:5 +#: templates/recruitment/candidate_interview_view.html:509 +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/change_list.html:41 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html:23 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html:24 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/pagination.html:19 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:9 +msgid "Save" +msgstr "حفظ" + +#: templates/meetings/update_meeting.html:4 +#: templates/meetings/update_meeting.html:196 +msgid "Update Zoom Meeting" +msgstr "تحديث اجتماع Zoom" + +#: templates/meetings/update_meeting.html:198 +msgid "Modify the details of your scheduled meeting" +msgstr "تعديل تفاصيل اجتماعك المجدول" + +#: templates/meetings/update_meeting.html:207 +#, fuzzy +#| msgid "Back to Meetings" +msgid "Back to Details" +msgstr "العودة إلى الاجتماعات" + +#: templates/participants/participants_create.html:94 +#, fuzzy +#| msgid "Create New Candidate" +msgid "Create New Participant" +msgstr "إنشاء مرشح جديد" + +#: templates/participants/participants_create.html:96 +#, fuzzy +#| msgid "Enter details to create a new candidate record." +msgid "Enter details to create a new participant record." +msgstr "أدخل التفاصيل لإنشاء سجل مرشح جديد." + +#: templates/participants/participants_create.html:99 +#: templates/participants/participants_create.html:101 +#: templates/participants/participants_detail.html:135 +#: templates/participants/participants_detail.html:136 +#: templates/recruitment/candidate_create.html:99 +#: templates/recruitment/candidate_create.html:101 +#: templates/recruitment/candidate_detail.html:631 +#: templates/recruitment/candidate_update.html:97 +#: templates/recruitment/candidate_update.html:99 +#: templates/recruitment/training_create.html:112 +#: templates/recruitment/training_create.html:114 +#: templates/recruitment/training_update.html:112 +#: templates/recruitment/training_update.html:114 +msgid "Back to List" +msgstr "العودة إلى القائمة" + +#: templates/participants/participants_create.html:112 +#, fuzzy +#| msgid "Basic Information" +msgid "Participant Information" +msgstr "المعلومات الأساسية" + +#: templates/participants/participants_create.html:131 +#, fuzzy +#| msgid "Participant Video" +msgid "Save Participant" +msgstr "فيديو المشارك" + +#: templates/participants/participants_detail.html:131 +#, fuzzy +#| msgid "Participant Video" +msgid "Participant Details" +msgstr "فيديو المشارك" + +#: templates/participants/participants_detail.html:139 +#, fuzzy +#| msgid "Participant Video" +msgid "Edit Participant" +msgstr "فيديو المشارك" + +#: templates/participants/participants_detail.html:140 +#, fuzzy +#| msgid "Profile" +msgid "Edit Profile" +msgstr "الملف الشخصي" + +#: templates/participants/participants_detail.html:161 +#, fuzzy +#| msgid "Candidate Information" +msgid "Contact & Role Information" +msgstr "معلومات المرشح" + +#: templates/participants/participants_detail.html:166 +#: templates/user/admin_settings.html:174 +#, fuzzy +#| msgid "First Name" +msgid "Full Name" +msgstr "الاسم الأول" + +#: templates/participants/participants_detail.html:192 +#, fuzzy +#| msgid "Active Jobs" +msgid "Assigned Jobs" +msgstr "الوظائف النشطة" + +#: templates/participants/participants_detail.html:199 +msgid "This participant is not currently assigned to any job." +msgstr "" + +#: templates/participants/participants_detail.html:210 +msgid "Metadata" +msgstr "" + +#: templates/participants/participants_detail.html:213 +#, fuzzy +#| msgid "Created" +msgid "Record Created" +msgstr "تم الإنشاء" + +#: templates/participants/participants_detail.html:214 +#: templates/participants/participants_detail.html:219 +msgid "at" +msgstr "" + +#: templates/participants/participants_detail.html:225 +msgid "Total Assigned Jobs" +msgstr "" + +#: templates/participants/participants_detail.html:240 +#: templates/participants/participants_list.html:322 +#, fuzzy +#| msgid "Confirm Delete" +msgid "Confirm Deletion" +msgstr "تأكيد الحذف" + +#: templates/participants/participants_detail.html:244 +#: templates/participants/participants_list.html:326 +#, fuzzy +#| msgid "Are you sure you want to delete this item?" +msgid "Are you sure you want to delete" +msgstr "هل أنت متأكد من رغبتك في حذف هذا العنصر؟" + +#: templates/participants/participants_detail.html:248 +#: templates/participants/participants_list.html:330 +msgid "This action cannot be undone." +msgstr "" + +#: templates/participants/participants_list.html:143 +#, fuzzy +#| msgid "Participant Video" +msgid "Participants List" +msgstr "فيديو المشارك" + +#: templates/participants/participants_list.html:147 +#, fuzzy +#| msgid "Add New Material" +msgid "Add New Participant" +msgstr "إضافة مادة جديدة" + +#: templates/participants/participants_list.html:156 +#: templates/recruitment/candidate_list.html:206 +msgid "Search by Name or Email" +msgstr "" + +#: templates/participants/participants_list.html:172 +msgid "Filter by Assigned Job" +msgstr "" + +#: templates/participants/participants_list.html:174 +#: templates/recruitment/candidate_list.html:224 +#: templates/recruitment/dashboard.html:395 +#, fuzzy +#| msgid "Jobs" +msgid "All Jobs" +msgstr "الوظائف" + +#: templates/participants/participants_list.html:304 +#, fuzzy +#| msgid "No candidates found." +msgid "No participants found" +msgstr "لم يتم العثور على مرشحين." + +#: templates/participants/participants_list.html:305 +msgid "Create your first participant record or adjust your filters." +msgstr "" + +#: templates/participants/participants_list.html:308 +#, fuzzy +#| msgid "Participant Video" +msgid "Add Participant" +msgstr "فيديو المشارك" + +#: templates/recruitment/agency_access_link_detail.html:4 +#: templates/recruitment/agency_access_link_detail.html:12 +#, fuzzy +#| msgid "Meeting Details" +msgid "Access Link Details" +msgstr "تفاصيل الاجتماع" + +#: templates/recruitment/agency_access_link_detail.html:14 +msgid "Secure access link for agency candidate submissions" +msgstr "" + +#: templates/recruitment/agency_access_link_detail.html:17 +#: templates/recruitment/agency_portal_submit_candidate.html:122 +#: templates/recruitment/agency_portal_submit_candidate.html:365 +#, fuzzy +#| msgid "Back to List" +msgid "Back to Assignment" +msgstr "العودة إلى القائمة" + +#: templates/recruitment/agency_access_link_detail.html:28 +#, fuzzy +#| msgid "Basic Information" +msgid "Access Information" +msgstr "المعلومات الأساسية" + +#: templates/recruitment/agency_access_link_detail.html:31 +#: templates/user/admin_settings.html:194 +#, fuzzy +#| msgid "Active" +msgid "Inactive" +msgstr "نشط" + +#: templates/recruitment/agency_access_link_detail.html:66 +#, fuzzy +#| msgid "Candidates" +msgid "Max Candidates" +msgstr "المرشحون" + +#: templates/recruitment/agency_access_link_detail.html:83 +#: templates/recruitment/agency_assignment_detail.html:172 +msgid "Access Credentials" +msgstr "" + +#: templates/recruitment/agency_access_link_detail.html:87 +#: templates/recruitment/agency_assignment_detail.html:176 +#, fuzzy +#| msgid "Join URL" +msgid "Login URL" +msgstr "رابط الانضمام" + +#: templates/recruitment/agency_access_link_detail.html:121 +#: templates/recruitment/agency_assignment_detail.html:210 +msgid "" +"Share these credentials securely with the agency. They can use this " +"information to log in and submit candidates." +msgstr "" + +#: templates/recruitment/agency_access_link_detail.html:132 +msgid "Usage Statistics" +msgstr "" + +#: templates/recruitment/agency_access_link_detail.html:137 +msgid "Total Accesses" +msgstr "" + +#: templates/recruitment/agency_access_link_detail.html:146 +msgid "Never" +msgstr "" + +#: templates/recruitment/agency_access_link_detail.html:173 +msgid "View Assignment" +msgstr "" + +#: templates/recruitment/agency_access_link_detail.html:178 +#: templates/user/admin_settings.html:232 +#, fuzzy +#| msgid "Active" +msgid "Deactivate" +msgstr "نشط" + +#: templates/recruitment/agency_access_link_detail.html:184 +#, fuzzy +#| msgid "Active" +msgid "Reactivate" +msgstr "نشط" + +#: templates/recruitment/agency_access_link_detail.html:217 +#: templates/recruitment/agency_assignment_detail.html:489 +msgid "" +"Are you sure you want to deactivate this access link? Agencies will no " +"longer be able to use it." +msgstr "" + +#: templates/recruitment/agency_access_link_detail.html:224 +#: templates/recruitment/agency_assignment_detail.html:496 +#, fuzzy +#| msgid "Are you sure you want to delete this material?" +msgid "Are you sure you want to reactivate this access link?" +msgstr "هل أنت متأكد من رغبتك في حذف هذه المادة؟" + +#: templates/recruitment/agency_access_link_form.html:14 +msgid "Generate a secure access link for agency to submit candidates" +msgstr "" + +#: templates/recruitment/agency_access_link_form.html:17 +#: templates/recruitment/agency_assignment_detail.html:103 +#: templates/recruitment/agency_assignment_form.html:114 +#, fuzzy +#| msgid "Back to List" +msgid "Back to Assignments" +msgstr "العودة إلى القائمة" + +#: templates/recruitment/agency_access_link_form.html:47 +msgid "Select the agency job assignment" +msgstr "" + +#: templates/recruitment/agency_access_link_form.html:62 +msgid "When will this access link expire?" +msgstr "" + +#: templates/recruitment/agency_access_link_form.html:69 +msgid "Max Submissions" +msgstr "" + +#: templates/recruitment/agency_access_link_form.html:79 +msgid "" +"Maximum number of candidates agency can submit (leave blank for unlimited)" +msgstr "" + +#: templates/recruitment/agency_access_link_form.html:99 +msgid "Whether this access link is currently active" +msgstr "" + +#: templates/recruitment/agency_access_link_form.html:105 +msgid "Notes" +msgstr "ملاحظات" + +#: templates/recruitment/agency_access_link_form.html:115 +#, fuzzy +#| msgid "Internal notes about the agency" +msgid "Additional notes or instructions for the agency" +msgstr "ملاحظات داخلية حول الوكالة" + +#: templates/recruitment/agency_access_link_form.html:122 +msgid "" +"Access links will be generated with a secure token that agencies can use to " +"log in" +msgstr "" + +#: templates/recruitment/agency_assignment_detail.html:98 +msgid "Assignment Details and Management" +msgstr "" + +#: templates/recruitment/agency_assignment_detail.html:106 +#: templates/recruitment/agency_assignment_detail.html:357 +msgid "Edit Assignment" +msgstr "" + +#: templates/recruitment/agency_assignment_detail.html:118 +#: templates/recruitment/agency_portal_assignment_detail.html:110 +#: templates/recruitment/agency_portal_assignment_detail.html:137 +#: templates/recruitment/agency_portal_submit_candidate.html:133 +#, fuzzy +#| msgid "Edit Details" +msgid "Assignment Details" +msgstr "تعديل التفاصيل" + +#: templates/recruitment/agency_assignment_detail.html:215 +#, fuzzy +#| msgid "Meeting Details" +msgid "View Access Links Details" +msgstr "تفاصيل الاجتماع" + +#: templates/recruitment/agency_assignment_detail.html:225 +#: templates/recruitment/agency_portal_assignment_detail.html:231 +#, fuzzy +#| msgid "New Candidates" +msgid "Submitted Candidates" +msgstr "المرشحون الجدد" + +#: templates/recruitment/agency_assignment_detail.html:229 +#, fuzzy +#| msgid "Preview" +msgid "Preview Portal" +msgstr "معاينة" + +#: templates/recruitment/agency_assignment_detail.html:240 +#: templates/recruitment/agency_portal_assignment_detail.html:242 +#: templates/recruitment/candidate_exam_view.html:251 +#: templates/recruitment/candidate_hired_view.html:233 +#: templates/recruitment/candidate_interview_view.html:262 +#: templates/recruitment/candidate_offer_view.html:247 +#, fuzzy +#| msgid "Content" +msgid "Contact" +msgstr "المحتوى" + +#: templates/recruitment/agency_assignment_detail.html:242 +#: templates/recruitment/agency_portal_assignment_detail.html:244 +#, fuzzy +#| msgid "Submit" +msgid "Submitted" +msgstr "إرسال" + +#: templates/recruitment/agency_assignment_detail.html:280 +#: templates/recruitment/agency_portal_assignment_detail.html:319 +#, fuzzy +#| msgid "No candidates found." +msgid "No candidates submitted yet" +msgstr "لم يتم العثور على مرشحين." + +#: templates/recruitment/agency_assignment_detail.html:282 +#, fuzzy +#| msgid "Candidates will appear here once they apply for this position." +msgid "" +"Candidates will appear here once the agency submits them through their " +"portal." +msgstr "سيظهر المرشحون هنا بمجرد تقديمهم لهذا المنصب." + +#: templates/recruitment/agency_assignment_detail.html:294 +#: templates/recruitment/agency_portal_assignment_detail.html:333 +#: templates/recruitment/agency_portal_dashboard.html:181 +msgid "Submission Progress" +msgstr "" + +#: templates/recruitment/agency_assignment_detail.html:325 +#: templates/recruitment/agency_portal_assignment_detail.html:175 +#: templates/recruitment/agency_portal_assignment_detail.html:364 +#, fuzzy +#| msgid "Candidates" +msgid "candidates" +msgstr "المرشحون" + +#: templates/recruitment/agency_assignment_detail.html:345 +#: templates/recruitment/agency_detail.html:492 +#: templates/recruitment/agency_portal_assignment_detail.html:513 +#, fuzzy +#| msgid "Error Message" +msgid "Send Message" +msgstr "رسالة الخطأ" + +#: templates/recruitment/agency_assignment_detail.html:351 +#: templates/recruitment/agency_assignment_detail.html:433 +#, fuzzy +#| msgid "Deadline:" +msgid "Extend Deadline" +msgstr "الموعد النهائي:" + +#: templates/recruitment/agency_assignment_detail.html:369 +#: templates/recruitment/agency_portal_assignment_detail.html:437 +msgid "Recent Messages" +msgstr "" + +#: templates/recruitment/agency_assignment_detail.html:381 +#: templates/recruitment/agency_portal_assignment_detail.html:449 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:186 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:224 +msgid "From" +msgstr "" + +#: templates/recruitment/agency_assignment_detail.html:385 +#: templates/recruitment/agency_portal_assignment_detail.html:453 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_preview.html:22 +msgid "New" +msgstr "جديد" + +#: templates/recruitment/agency_assignment_detail.html:395 +#: templates/recruitment/agency_portal_assignment_detail.html:463 +#, fuzzy +#| msgid "View API Response" +msgid "View All Messages" +msgstr "عرض استجابة واجهة برمجة التطبيقات" + +#: templates/recruitment/agency_assignment_detail.html:410 +msgid "Extend Assignment Deadline" +msgstr "" + +#: templates/recruitment/agency_assignment_detail.html:419 +#, fuzzy +#| msgid "Deadline:" +msgid "New Deadline" +msgstr "الموعد النهائي:" + +#: templates/recruitment/agency_assignment_detail.html:424 +#, fuzzy +#| msgid "Deadline:" +msgid "Current deadline:" +msgstr "الموعد النهائي:" + +#: templates/recruitment/agency_assignment_detail.html:457 +msgid "Token copied to clipboard!" +msgstr "" + +#: templates/recruitment/agency_assignment_form.html:110 +msgid "Assign a job to an external hiring agency" +msgstr "" + +#: templates/recruitment/agency_assignment_form.html:170 +msgid "Maximum number of candidates the agency can submit" +msgstr "" + +#: templates/recruitment/agency_assignment_form.html:187 +msgid "Date and time when submission period ends" +msgstr "" + +#: templates/recruitment/agency_assignment_form.html:207 +#, fuzzy +#| msgid "Internal notes about the agency" +msgid "Internal notes about this assignment (not visible to agency)" +msgstr "ملاحظات داخلية حول الوكالة" + +#: templates/recruitment/agency_assignment_list.html:4 +#: templates/recruitment/agency_assignment_list.html:59 +#, fuzzy +#| msgid "Agency Name" +msgid "Agency Assignments" +msgstr "اسم الوكالة" + +#: templates/recruitment/agency_assignment_list.html:62 +msgid "Total Assignments:" +msgstr "" + +#: templates/recruitment/agency_assignment_list.html:67 +msgid "New Assignment" +msgstr "" + +#: templates/recruitment/agency_assignment_list.html:79 +msgid "Search by agency or job title..." +msgstr "" + +#: templates/recruitment/agency_assignment_list.html:169 +#, fuzzy +#| msgid "View on LinkedIn" +msgid "View Access Link" +msgstr "عرض على LinkedIn" + +#: templates/recruitment/agency_assignment_list.html:183 +msgid "Assignments pagination" +msgstr "" + +#: templates/recruitment/agency_assignment_list.html:228 +#, fuzzy +#| msgid "No meetings found." +msgid "No assignments found" +msgstr "لم يتم العثور على اجتماعات." + +#: templates/recruitment/agency_assignment_list.html:229 +msgid "Create your first agency assignment to get started." +msgstr "" + +#: templates/recruitment/agency_assignment_list.html:231 +msgid "Create Assignment" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:4 +#: templates/recruitment/agency_confirm_delete.html:179 +#, fuzzy +#| msgid "Delete Meeting" +msgid "Delete Agency" +msgstr "حذف الاجتماع" + +#: templates/recruitment/agency_confirm_delete.html:182 +msgid "You are about to delete a hiring agency. This action cannot be undone." +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:186 +#, fuzzy +#| msgid "Back to Meetings" +msgid "Back to Agency" +msgstr "العودة إلى الاجتماعات" + +#: templates/recruitment/agency_confirm_delete.html:197 +msgid "Warning: This action cannot be undone!" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:199 +msgid "" +"Deleting this agency will permanently remove all associated data. Please " +"review the information below carefully before proceeding." +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:208 +msgid "Agency to be Deleted" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:277 +#, fuzzy +#| msgid "No candidates found." +msgid "Associated Candidates Found" +msgstr "لم يتم العثور على مرشحين." + +#: templates/recruitment/agency_confirm_delete.html:280 +msgid "candidate(s) are associated with this agency." +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:283 +msgid "" +"Deleting this agency will affect these candidates. Their agency reference " +"will be removed, but the candidates themselves will not be deleted." +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:293 +msgid "What will happen when you delete this agency?" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:300 +msgid "The agency profile and all its information will be permanently deleted" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:304 +#, fuzzy +#| msgid "Edit candidate information and details" +msgid "All contact information and agency details will be removed" +msgstr "تعديل معلومات المرشح وتفاصيله" + +#: templates/recruitment/agency_confirm_delete.html:309 +msgid "Associated candidates will lose their agency reference" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:313 +msgid "Historical data linking candidates to this agency will be lost" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:318 +msgid "This action cannot be undone under any circumstances" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:332 +msgid "Type the agency name to confirm deletion:" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:341 +msgid "This is required to prevent accidental deletions." +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:349 +msgid "" +"I understand that this action cannot be undone and I want to permanently " +"delete this agency." +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:364 +msgid "Delete Agency Permanently" +msgstr "" + +#: templates/recruitment/agency_confirm_delete.html:402 +#, fuzzy +#| msgid "Are you sure you want to delete this candidate?" +msgid "" +"Are you absolutely sure you want to delete this agency? This action cannot " +"be undone." +msgstr "هل أنت متأكد من رغبتك في حذف هذا المرشح؟" + +#: templates/recruitment/agency_detail.html:4 +#, fuzzy +#| msgid "Core Details" +msgid "Agency Details" +msgstr "التفاصيل الأساسية" + +#: templates/recruitment/agency_detail.html:220 +msgid "Hiring Agency Details and Candidate Management" +msgstr "" + +#: templates/recruitment/agency_detail.html:225 +msgid "Assign job" +msgstr "" + +#: templates/recruitment/agency_detail.html:228 +#: templates/recruitment/agency_detail.html:486 +#, fuzzy +#| msgid "Hiring Agency" +msgid "Edit Agency" +msgstr "وكالة التوظيف" + +#: templates/recruitment/agency_detail.html:231 +#: templates/recruitment/agency_form.html:131 +#, fuzzy +#| msgid "Back to List" +msgid "Back to Agencies" +msgstr "العودة إلى القائمة" + +#: templates/recruitment/agency_detail.html:248 +#: templates/recruitment/agency_list.html:285 +#, fuzzy +#| msgid "Contact & Job" +msgid "Contact:" +msgstr "الاتصال والوظيفة" + +#: templates/recruitment/agency_detail.html:280 +#: templates/recruitment/agency_form.html:216 +#: templates/recruitment/agency_portal_submit_candidate.html:214 +#, fuzzy +#| msgid "Join Information" +msgid "Contact Information" +msgstr "معلومات الانضمام" + +#: templates/recruitment/agency_detail.html:330 +#: templates/recruitment/agency_form.html:273 +#, fuzzy +#| msgid "Application Information" +msgid "Location Information" +msgstr "معلومات التقديم" + +#: templates/recruitment/agency_detail.html:391 +#, fuzzy +#| msgid "Delete Candidate" +msgid "Recent Candidates" +msgstr "حذف المرشح" + +#: templates/recruitment/agency_detail.html:394 +#: templates/recruitment/agency_detail.html:489 +#, fuzzy +#| msgid "All Candidates" +msgid "View All Candidates" +msgstr "جميع المرشحين" + +#: templates/recruitment/agency_detail.html:427 +#, fuzzy +#| msgid "No candidates found." +msgid "No candidates yet" +msgstr "لم يتم العثور على مرشحين." + +#: templates/recruitment/agency_detail.html:428 +msgid "This agency hasn't submitted any candidates yet." +msgstr "" + +#: templates/recruitment/agency_detail.html:442 +#, fuzzy +#| msgid "Candidates" +msgid "Candidate Statistics" +msgstr "المرشحون" + +#: templates/recruitment/agency_detail.html:450 +msgid "Total" +msgstr "" + +#: templates/recruitment/agency_detail.html:480 +#, fuzzy +#| msgid "Actions" +msgid "Quick Actions" +msgstr "الإجراءات" + +#: templates/recruitment/agency_detail.html:496 +msgid "Visit Website" +msgstr "" + +#: templates/recruitment/agency_detail.html:513 +#: templates/recruitment/agency_form.html:390 +#, fuzzy +#| msgid "Join Information" +msgid "Agency Information" +msgstr "معلومات الانضمام" + +#: templates/recruitment/agency_detail.html:522 +#: templates/recruitment/agency_form.html:397 +msgid "Last Updated:" +msgstr "آخر تحديث:" + +#: templates/recruitment/agency_detail.html:526 +#, fuzzy +#| msgid "Agency Name" +msgid "Agency ID:" +msgstr "اسم الوكالة" + +#: templates/recruitment/agency_form.html:124 +msgid "Update the hiring agency information below." +msgstr "" + +#: templates/recruitment/agency_form.html:126 +msgid "Fill in the details to add a new hiring agency." +msgstr "" + +#: templates/recruitment/agency_form.html:144 +msgid "Please correct the errors below:" +msgstr "" + +#: templates/recruitment/agency_form.html:159 +msgid "Basic Information" +msgstr "المعلومات الأساسية" + +#: templates/recruitment/agency_form.html:313 +#, fuzzy +#| msgid "Application Information" +msgid "Additional Information" +msgstr "معلومات التقديم" + +#: templates/recruitment/agency_form.html:362 +msgid "Quick Tips" +msgstr "" + +#: templates/recruitment/agency_form.html:367 +msgid "Provide accurate contact information for better communication" +msgstr "" + +#: templates/recruitment/agency_form.html:371 +msgid "Include a valid website URL if available" +msgstr "" + +#: templates/recruitment/agency_form.html:375 +msgid "Add a detailed description to help identify the agency" +msgstr "" + +#: templates/recruitment/agency_form.html:379 +msgid "All fields marked with * are required" +msgstr "" + +#: templates/recruitment/agency_form.html:401 +#, fuzzy +#| msgid "Slug" +msgid "Slug:" +msgstr "الرابط المختصر" + +#: templates/recruitment/agency_list.html:134 +#, fuzzy +#| msgid "Hiring Agencies" +msgid "Total Agencies:" +msgstr "وكالات التوظيف" + +#: templates/recruitment/agency_list.html:141 +#, fuzzy +#| msgid "View All Applicants" +msgid "View All Job Assignments" +msgstr "عرض جميع المتقدمين" + +#: templates/recruitment/agency_list.html:145 +#, fuzzy +#| msgid "Add New Material" +msgid "Add New Agency" +msgstr "إضافة مادة جديدة" + +#: templates/recruitment/agency_list.html:161 +msgid "Search by name, contact person, email, or country..." +msgstr "" + +#: templates/recruitment/agency_list.html:341 +msgid "Agency pagination" +msgstr "" + +#: templates/recruitment/agency_list.html:385 +#, fuzzy +#| msgid "No templates match your search \"%(query)s\"." +msgid "No agencies found matching your search criteria." +msgstr "لا توجد قوالب تطابق بحثك \"%(query)s\"." + +#: templates/recruitment/agency_list.html:387 +msgid "No hiring agencies have been added yet." +msgstr "" + +#: templates/recruitment/agency_list.html:391 +msgid "" +"Start by adding your first hiring agency to manage your recruitment partners." +msgstr "" + +#: templates/recruitment/agency_list.html:394 +#, fuzzy +#| msgid "Add Your First Candidate" +msgid "Add Your First Agency" +msgstr "إضافة أول مرشح لك" + +#: templates/recruitment/agency_portal_assignment_detail.html:115 +#, fuzzy +#| msgid "Dashboard" +msgid "Back to Dashboard" +msgstr "لوحة التحكم" + +#: templates/recruitment/agency_portal_assignment_detail.html:118 +#: templates/recruitment/agency_portal_submit_candidate.html:114 +#, fuzzy +#| msgid "New Candidates" +msgid "Submit New Candidate" +msgstr "المرشحون الجدد" + +#: templates/recruitment/agency_portal_assignment_detail.html:169 +#, fuzzy +#| msgid "Training" +msgid "days remaining" +msgstr "التدريب" + +#: templates/recruitment/agency_portal_assignment_detail.html:182 +#, fuzzy +#| msgid "Job Description" +msgid "Job Description " +msgstr "وصف الوظيفة" + +#: templates/recruitment/agency_portal_assignment_detail.html:269 +#: templates/recruitment/agency_portal_assignment_detail.html:528 +#, fuzzy +#| msgid "Update Candidate" +msgid "Edit Candidate" +msgstr "تحديث المرشح" + +#: templates/recruitment/agency_portal_assignment_detail.html:272 +#: templates/recruitment/agency_portal_assignment_detail.html:593 +#: templates/recruitment/agency_portal_assignment_detail.html:612 +#, fuzzy +#| msgid "Create Candidate" +msgid "Remove Candidate" +msgstr "إنشاء مرشح" + +#: templates/recruitment/agency_portal_assignment_detail.html:321 +msgid "Submit candidates using the form above to get started." +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:374 +#: templates/recruitment/agency_portal_submit_candidate.html:156 +#, fuzzy +#| msgid "Submit" +msgid "Can Submit" +msgstr "إرسال" + +#: templates/recruitment/agency_portal_assignment_detail.html:376 +#: templates/recruitment/agency_portal_submit_candidate.html:158 +#, fuzzy +#| msgid "Submit" +msgid "Cannot Submit" +msgstr "إرسال" + +#: templates/recruitment/agency_portal_assignment_detail.html:406 +msgid "Assignment Info" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:415 +msgid "Days Remaining" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:417 +#: templates/recruitment/agency_portal_submit_candidate.html:147 +msgid "days" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:422 +msgid "Submission Rate" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:478 +msgid "Send Message to Admin" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:493 +msgid "Priority" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:495 +msgid "Low" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:496 +msgid "Medium" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:497 +msgid "High" +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:498 +#, fuzzy +#| msgid "User Agent" +msgid "Urgent" +msgstr "وكيل المستخدم" + +#: templates/recruitment/agency_portal_assignment_detail.html:603 +#, fuzzy +#| msgid "Are you sure you want to delete this candidate?" +msgid "" +"Are you sure you want to remove this candidate? This action cannot be undone." +msgstr "هل أنت متأكد من رغبتك في حذف هذا المرشح؟" + +#: templates/recruitment/agency_portal_assignment_detail.html:605 +#, fuzzy +#| msgid "Candidate" +msgid "Candidate:" +msgstr "المرشح" + +#: templates/recruitment/agency_portal_assignment_detail.html:644 +msgid "Error loading candidate data. Please try again." +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:679 +#: templates/recruitment/agency_portal_assignment_detail.html:684 +msgid "Error updating candidate. Please try again." +msgstr "" + +#: templates/recruitment/agency_portal_assignment_detail.html:706 +#: templates/recruitment/agency_portal_assignment_detail.html:711 +msgid "Error removing candidate. Please try again." +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:4 +#: templates/recruitment/agency_portal_dashboard.html:45 +#, fuzzy +#| msgid "Dashboard" +msgid "Agency Dashboard" +msgstr "لوحة التحكم" + +#: templates/recruitment/agency_portal_dashboard.html:48 +msgid "Welcome back" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:76 +msgid "Total Assignments" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:87 +msgid "Active Assignments" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:98 +#: templates/recruitment/partials/stats_cards.html:28 +#, fuzzy +#| msgid "All Candidates" +msgid "Total Candidates" +msgstr "جميع المرشحين" + +#: templates/recruitment/agency_portal_dashboard.html:109 +#, fuzzy +#| msgid "Error Message" +msgid "Unread Messages" +msgstr "رسالة الخطأ" + +#: templates/recruitment/agency_portal_dashboard.html:121 +msgid "Your Job Assignments" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:123 +msgid "assignments" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:166 +msgid "days left" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:168 +msgid "days overdue" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:202 +msgid "Submissions Closed" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:230 +msgid "No Job Assignments Found" +msgstr "" + +#: templates/recruitment/agency_portal_dashboard.html:232 +msgid "" +"You don't have any job assignments yet. Please contact the administrator if " +"you expect to have assignments." +msgstr "" + +#: templates/recruitment/agency_portal_login.html:4 +msgid "Agency Portal Login" +msgstr "" + +#: templates/recruitment/agency_portal_login.html:128 +#, fuzzy +#| msgid "Edit candidate information and details" +msgid "Submit candidates for job assignments" +msgstr "تعديل معلومات المرشح وتفاصيله" + +#: templates/recruitment/agency_portal_login.html:159 +msgid "Enter the access token provided by the hiring organization" +msgstr "" + +#: templates/recruitment/agency_portal_login.html:181 +msgid "Enter the password for this access token" +msgstr "" + +#: templates/recruitment/agency_portal_login.html:189 +msgid "Access Portal" +msgstr "" + +#: templates/recruitment/agency_portal_login.html:198 +msgid "Need Help?" +msgstr "" + +#: templates/recruitment/agency_portal_login.html:206 +#, fuzzy +#| msgid "Contact & Job" +msgid "Contact Support" +msgstr "الاتصال والوظيفة" + +#: templates/recruitment/agency_portal_login.html:208 +msgid "Reach out to your hiring contact" +msgstr "" + +#: templates/recruitment/agency_portal_login.html:215 +#, fuzzy +#| msgid "Duration" +msgid "Documentation" +msgstr "المدة" + +#: templates/recruitment/agency_portal_login.html:217 +msgid "View user guides and tutorials" +msgstr "" + +#: templates/recruitment/agency_portal_login.html:227 +msgid "Security Notice" +msgstr "" + +#: templates/recruitment/agency_portal_login.html:230 +msgid "" +"This portal is for authorized agency partners only. Access is monitored and " +"logged." +msgstr "" + +#: templates/recruitment/agency_portal_login.html:234 +msgid "" +"If you believe you've received this link in error, please contact the hiring " +"organization immediately." +msgstr "" + +#: templates/recruitment/agency_portal_login.html:295 +msgid "Please enter your access token." +msgstr "" + +#: templates/recruitment/agency_portal_login.html:302 +msgid "Please enter your password." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:117 +#, fuzzy +#| msgid "No candidates found." +msgid "Submit a candidate for" +msgstr "لم يتم العثور على مرشحين." + +#: templates/recruitment/agency_portal_submit_candidate.html:136 +#, fuzzy +#| msgid "Position No:" +msgid "Position:" +msgstr "رقم المنصب:" + +#: templates/recruitment/agency_portal_submit_candidate.html:145 +msgid "Days Remaining:" +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:154 +#, fuzzy +#| msgid "Status" +msgid "Status:" +msgstr "الحالة" + +#: templates/recruitment/agency_portal_submit_candidate.html:170 +#: templates/recruitment/candidate_create.html:112 +msgid "Candidate Information" +msgstr "معلومات المرشح" + +#: templates/recruitment/agency_portal_submit_candidate.html:226 +#, fuzzy +#| msgid "Enter email" +msgid "Enter email address" +msgstr "أدخل البريد الإلكتروني" + +#: templates/recruitment/agency_portal_submit_candidate.html:246 +#, fuzzy +#| msgid "Candidate Information" +msgid "Address Information" +msgstr "معلومات المرشح" + +#: templates/recruitment/agency_portal_submit_candidate.html:251 +#, fuzzy +#| msgid "IP Address" +msgid "Full Address" +msgstr "عنوان IP" + +#: templates/recruitment/agency_portal_submit_candidate.html:258 +#, fuzzy +#| msgid "Enter last name" +msgid "Enter full address" +msgstr "أدخل اسم العائلة" + +#: templates/recruitment/agency_portal_submit_candidate.html:267 +#, fuzzy +#| msgid "Resume" +msgid "Resume/CV" +msgstr "السيرة الذاتية" + +#: templates/recruitment/agency_portal_submit_candidate.html:272 +#, fuzzy +#| msgid "Download Resume" +msgid "Upload Resume" +msgstr "تحميل السيرة الذاتية" + +#: templates/recruitment/agency_portal_submit_candidate.html:284 +msgid "Click to upload or drag and drop" +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:286 +msgid "Accepted formats: PDF, DOC, DOCX (Maximum 5MB)" +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:294 +msgid "Remove File" +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:306 +msgid "Additional Notes" +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:311 +msgid "Notes (Optional)" +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:317 +msgid "Any additional information about the candidate" +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:328 +msgid "Submitted candidates will be reviewed by the hiring team." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:349 +#, fuzzy +#| msgid "Create Candidate" +msgid "Cannot Submit Candidates" +msgstr "إنشاء مرشح" + +#: templates/recruitment/agency_portal_submit_candidate.html:353 +msgid "This assignment has expired. Submissions are no longer accepted." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:356 +msgid "Maximum candidate limit reached for this assignment." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:359 +msgid "This assignment is not currently active." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:383 +msgid "Submitting candidate..." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:384 +msgid "Please wait while we process your submission." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:450 +msgid "Please upload a PDF, DOC, or DOCX file." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:457 +msgid "File size must be less than 5MB." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:478 +#, fuzzy +#| msgid "Submit" +msgid "Submitting..." +msgstr "إرسال" + +#: templates/recruitment/agency_portal_submit_candidate.html:500 +#, fuzzy +#| msgid "Posted successfully!" +msgid "Candidate submitted successfully!" +msgstr "تم النشر بنجاح!" + +#: templates/recruitment/agency_portal_submit_candidate.html:531 +msgid "Error submitting candidate. Please try again." +msgstr "" + +#: templates/recruitment/agency_portal_submit_candidate.html:553 +msgid "Network error. Please check your connection and try again." +msgstr "" + +#: templates/recruitment/candidate_create.html:94 +msgid "Create New Candidate" +msgstr "إنشاء مرشح جديد" + +#: templates/recruitment/candidate_create.html:96 +msgid "Enter details to create a new candidate record." +msgstr "أدخل التفاصيل لإنشاء سجل مرشح جديد." + +#: templates/recruitment/candidate_create.html:131 +msgid "Create Candidate" +msgstr "إنشاء مرشح" + +#: templates/recruitment/candidate_detail.html:292 +msgid "Stage:" +msgstr "المرحلة:" + +#: templates/recruitment/candidate_detail.html:297 +msgid "Applied for:" +msgstr "تقدم لـ:" + +#: templates/recruitment/candidate_detail.html:303 +#: templates/recruitment/candidate_exam_view.html:229 +#: templates/recruitment/candidate_interview_view.html:222 +#: templates/recruitment/candidate_offer_view.html:222 +#: templates/recruitment/candidate_screening_view.html:341 +msgid "Change Stage" +msgstr "تغيير المرحلة" + +#: templates/recruitment/candidate_detail.html:313 +msgid "Contact & Job" +msgstr "الاتصال والوظيفة" + +#: templates/recruitment/candidate_detail.html:320 +msgid "Journey Timeline" +msgstr "" + +#: templates/recruitment/candidate_detail.html:331 +msgid "Core Details" +msgstr "التفاصيل الأساسية" + +#: templates/recruitment/candidate_detail.html:346 +msgid "Position Applied" +msgstr "المنصب المتقدم له" + +#: templates/recruitment/candidate_detail.html:377 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidate Journey" +msgstr "نموذج المرشح" + +#: templates/recruitment/candidate_detail.html:381 +#, fuzzy +#| msgid "Change Stage" +msgid "Current Stage" +msgstr "تغيير المرحلة" + +#: templates/recruitment/candidate_detail.html:385 +#, fuzzy +#| msgid "Last Updated:" +msgid "Latest status update:" +msgstr "آخر تحديث:" + +#: templates/recruitment/candidate_detail.html:389 +#, fuzzy +#| msgid "Financial & Timeline" +msgid "Historical Timeline" +msgstr "المالي والجدول الزمني" + +#: templates/recruitment/candidate_detail.html:397 +#, fuzzy +#| msgid "Application Stage" +msgid "Application Submitted" +msgstr "مرحلة التقديم" + +#: templates/recruitment/candidate_detail.html:458 +msgid "AI Generated Summary" +msgstr "ملخص تم إنشاؤه بواسطة الذكاء الاصطناعي" + +#: templates/recruitment/candidate_detail.html:468 +msgid "AI Analysis Report" +msgstr "" + +#: templates/recruitment/candidate_detail.html:474 +msgid "Match Score" +msgstr "" + +#: templates/recruitment/candidate_detail.html:487 +msgid "Category" +msgstr "" + +#: templates/recruitment/candidate_detail.html:490 +msgid "Job Fit Narrative" +msgstr "" + +#: templates/recruitment/candidate_detail.html:521 +#, fuzzy +#| msgid "Material Details" +msgid "Professional Details" +msgstr "تفاصيل المادة" + +#: templates/recruitment/candidate_detail.html:522 +msgid "Years of Experience:" +msgstr "" + +#: templates/recruitment/candidate_detail.html:523 +msgid "Most Recent Job Title:" +msgstr "" + +#: templates/recruitment/candidate_detail.html:524 +msgid "Experience Industry Match:" +msgstr "" + +#: templates/recruitment/candidate_detail.html:529 +msgid "Soft Skills Score:" +msgstr "" + +#: templates/recruitment/candidate_detail.html:534 +#, fuzzy +#| msgid "Sync Status" +msgid "Screening Status" +msgstr "حالة المزامنة" + +#: templates/recruitment/candidate_detail.html:536 +msgid "Minimum Requirements Met:" +msgstr "" + +#: templates/recruitment/candidate_detail.html:544 +msgid "Screening Stage Rating:" +msgstr "" + +#: templates/recruitment/candidate_detail.html:601 +msgid "Resume is being parsed" +msgstr "" + +#: templates/recruitment/candidate_detail.html:602 +msgid "" +"Our AI is analyzing the candidate's resume to generate insights. This may " +"take a few moments." +msgstr "" + +#: templates/recruitment/candidate_detail.html:622 +msgid "Management Actions" +msgstr "إجراءات الإدارة" + +#: templates/recruitment/candidate_detail.html:625 +msgid "Edit Details" +msgstr "تعديل التفاصيل" + +#: templates/recruitment/candidate_detail.html:627 +msgid "Are you sure you want to delete this candidate?" +msgstr "هل أنت متأكد من رغبتك في حذف هذا المرشح؟" + +#: templates/recruitment/candidate_detail.html:628 +msgid "Delete Candidate" +msgstr "حذف المرشح" + +#: templates/recruitment/candidate_detail.html:637 +#, fuzzy +#| msgid "View API Response" +msgid "View Actual Resume" +msgstr "عرض استجابة واجهة برمجة التطبيقات" + +#: templates/recruitment/candidate_detail.html:641 +msgid "Download Resume" +msgstr "تحميل السيرة الذاتية" + +#: templates/recruitment/candidate_detail.html:646 +msgid "View Resume AI Overview" +msgstr "" + +#: templates/recruitment/candidate_detail.html:654 +msgid "Time to Hire: " +msgstr "" + +#: templates/recruitment/candidate_detail.html:679 +msgid "Unable to Parse Resume , click to retry" +msgstr "" + +#: templates/recruitment/candidate_exam_view.html:174 +#, fuzzy +#| msgid "Form Management" +msgid "Exam Management" +msgstr "إدارة النماذج" + +#: templates/recruitment/candidate_exam_view.html:177 +msgid "Candidates in Exam Stage:" +msgstr "" + +#: templates/recruitment/candidate_exam_view.html:183 +msgid "Export exam candidates to CSV" +msgstr "" + +#: templates/recruitment/candidate_exam_view.html:184 +#: templates/recruitment/candidate_hired_view.html:208 +#: templates/recruitment/candidate_interview_view.html:187 +#: templates/recruitment/candidate_offer_view.html:186 +#: templates/recruitment/candidate_screening_view.html:235 +msgid "Export CSV" +msgstr "" + +#: templates/recruitment/candidate_exam_view.html:197 +#: templates/recruitment/candidate_screening_view.html:312 +#, fuzzy +#| msgid "Candidates" +msgid "Candidate List" +msgstr "المرشحون" + +#: templates/recruitment/candidate_exam_view.html:219 +#, fuzzy +#| msgid "Interview Status" +msgid "Interview Stage" +msgstr "حالة المقابلة" + +#: templates/recruitment/candidate_exam_view.html:222 +msgid "Screening Stage" +msgstr "" + +#: templates/recruitment/candidate_exam_view.html:254 +#, fuzzy +#| msgid "Exam Status" +msgid "Exam Results" +msgstr "حالة الاختبار" + +#: templates/recruitment/candidate_exam_view.html:330 +msgid "No candidates are currently in the Exam stage for this job." +msgstr "" + +#: templates/recruitment/candidate_exam_view.html:343 +msgid "Candidate Details & Exam Update" +msgstr "" + +#: templates/recruitment/candidate_exam_view.html:350 +#: templates/recruitment/candidate_screening_view.html:490 +#, fuzzy +#| msgid "No candidates found." +msgid "Loading candidate data..." +msgstr "لم يتم العثور على مرشحين." + +#: templates/recruitment/candidate_hired_view.html:192 +#, fuzzy +#| msgid "New Candidates" +msgid "Hired Candidates" +msgstr "المرشحون الجدد" + +#: templates/recruitment/candidate_hired_view.html:195 +msgid "Successfully Hired:" +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:202 +msgid "Sync hired candidates to external sources" +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:203 +#: templates/recruitment/candidate_hired_view.html:407 +#, fuzzy +#| msgid "Sources" +msgid "Sync to Sources" +msgstr "المصادر" + +#: templates/recruitment/candidate_hired_view.html:207 +msgid "Export hired candidates to CSV" +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:219 +msgid "Congratulations!" +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:220 +msgid "" +"These candidates have successfully completed the hiring process and joined " +"your team." +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:234 +#, fuzzy +#| msgid "Open Positions" +msgid "Applied Position" +msgstr "المناصب المفتوحة" + +#: templates/recruitment/candidate_hired_view.html:300 +#, fuzzy +#| msgid "Candidates will appear here once they apply for this position." +msgid "No candidates have been hired for this position yet." +msgstr "سيظهر المرشحون هنا بمجرد تقديمهم لهذا المنصب." + +#: templates/recruitment/candidate_hired_view.html:313 +#, fuzzy +#| msgid "New Candidates" +msgid "Hired Candidate Details" +msgstr "المرشحون الجدد" + +#: templates/recruitment/candidate_hired_view.html:320 +#: templates/recruitment/candidate_interview_view.html:440 +#: templates/recruitment/candidate_interview_view.html:629 +#: templates/recruitment/candidate_offer_view.html:333 +msgid "Loading content..." +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:333 +#, fuzzy +#| msgid "Sync Status" +msgid "Sync Results" +msgstr "حالة المزامنة" + +#: templates/recruitment/candidate_hired_view.html:340 +msgid "Syncing candidates..." +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:369 +msgid "Syncing hired candidates..." +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:370 +msgid "Please wait while we sync candidates to external sources." +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:378 +msgid "Syncing..." +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:402 +msgid "An unexpected error occurred during sync." +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:419 +#, fuzzy +#| msgid "Summary" +msgid "Sync Summary" +msgstr "الملخص" + +#: templates/recruitment/candidate_hired_view.html:422 +#, fuzzy +#| msgid "Sources" +msgid "Total Sources:" +msgstr "المصادر" + +#: templates/recruitment/candidate_hired_view.html:425 +msgid "Successful:" +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:428 +#, fuzzy +#| msgid "Failed" +msgid "Failed:" +msgstr "رسب" + +#: templates/recruitment/candidate_hired_view.html:431 +#, fuzzy +#| msgid "Candidates" +msgid "Candidates Synced:" +msgstr "المرشحون" + +#: templates/recruitment/candidate_hired_view.html:439 +#, fuzzy +#| msgid "Core Details" +msgid "Source Details" +msgstr "التفاصيل الأساسية" + +#: templates/recruitment/candidate_hired_view.html:457 +#, fuzzy +#| msgid "Candidate Profiles" +msgid "Candidates Processed:" +msgstr "ملفات المرشحين الشخصية" + +#: templates/recruitment/candidate_hired_view.html:461 +#: templates/recruitment/notification_detail.html:71 +#, fuzzy +#| msgid "Duration" +msgid "Duration:" +msgstr "المدة" + +#: templates/recruitment/candidate_hired_view.html:465 +#: templates/recruitment/notification_confirm_delete.html:21 +#, fuzzy +#| msgid "Error Message" +msgid "Message:" +msgstr "رسالة الخطأ" + +#: templates/recruitment/candidate_hired_view.html:494 +msgid "Sync task failed" +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:503 +msgid "Failed to check sync status" +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:510 +msgid "Sync timed out after 5 minutes" +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:521 +msgid "Sync in progress..." +msgstr "" + +#: templates/recruitment/candidate_hired_view.html:532 +#, fuzzy +#| msgid "Failed" +msgid "Sync Failed" +msgstr "رسب" + +#: templates/recruitment/candidate_interview_view.html:177 +#, fuzzy +#| msgid "Interview Date" +msgid "Interview Management" +msgstr "تاريخ المقابلة" + +#: templates/recruitment/candidate_interview_view.html:180 +msgid "Candidates in Interview Stage:" +msgstr "" + +#: templates/recruitment/candidate_interview_view.html:186 +msgid "Export interview candidates to CSV" +msgstr "" + +#: templates/recruitment/candidate_interview_view.html:215 +#, fuzzy +#| msgid "Offer" +msgid "To Offer" +msgstr "العرض" + +#: templates/recruitment/candidate_interview_view.html:218 +#, fuzzy +#| msgid "Exam" +msgid "To Exam" +msgstr "الاختبار" + +#: templates/recruitment/candidate_interview_view.html:232 +#, fuzzy +#| msgid "Interview" +msgid "Schedule Interviews" +msgstr "المقابلة" + +#: templates/recruitment/candidate_interview_view.html:240 +#, fuzzy +#| msgid "Participant Video" +msgid "Manage Participants" +msgstr "فيديو المشارك" + +#: templates/recruitment/candidate_interview_view.html:265 +#, fuzzy +#| msgid "Meeting ID" +msgid "Meeting Date" +msgstr "معرف الاجتماع" + +#: templates/recruitment/candidate_interview_view.html:267 +#, fuzzy +#| msgid "Meeting Details" +msgid "Meeting Status" +msgstr "تفاصيل الاجتماع" + +#: templates/recruitment/candidate_interview_view.html:268 +#, fuzzy +#| msgid "Interview Date" +msgid "Interview Result" +msgstr "تاريخ المقابلة" + +#: templates/recruitment/candidate_interview_view.html:300 +msgid "Minutes" +msgstr "" + +#: templates/recruitment/candidate_interview_view.html:421 +msgid "No candidates are currently in the Interview stage for this job." +msgstr "" + +#: templates/recruitment/candidate_interview_view.html:433 +#: templates/recruitment/candidate_interview_view.html:633 +#: templates/recruitment/candidate_offer_view.html:326 +msgid "Candidate Details / Bulk Action Form" +msgstr "" + +#: templates/recruitment/candidate_interview_view.html:453 +msgid "Manage all participants" +msgstr "" + +#: templates/recruitment/candidate_interview_view.html:476 +#: templates/recruitment/candidate_interview_view.html:501 +msgid "Users" +msgstr "المستخدمون" + +#: templates/recruitment/candidate_interview_view.html:530 +msgid "Loading email form..." +msgstr "" + +#: templates/recruitment/candidate_list.html:193 +msgid "Candidate Profiles" +msgstr "ملفات المرشحين الشخصية" + +#: templates/recruitment/candidate_list.html:197 +msgid "Add New Candidate" +msgstr "إضافة مرشح جديد" + +#: templates/recruitment/candidate_list.html:221 +msgid "Filter by Job" +msgstr "" + +#: templates/recruitment/candidate_list.html:274 +msgid "Major" +msgstr "" + +#: templates/recruitment/candidate_list.html:277 +#, fuzzy +#| msgid "Created at" +msgid "created At" +msgstr "تم الإنشاء في" + +#: templates/recruitment/candidate_list.html:401 +#, fuzzy +#| msgid "No candidates found." +msgid "No candidate profiles found" +msgstr "لم يتم العثور على مرشحين." + +#: templates/recruitment/candidate_list.html:402 +#, fuzzy +#| msgid "Start by adding a new profile or adjusting your search filters." +msgid "Create your first candidate profile or adjust your filters." +msgstr "ابدأ بإضافة ملف شخصي جديد أو تعديل عوامل التصفية الخاصة بك." + +#: templates/recruitment/candidate_list.html:405 +#, fuzzy +#| msgid "Add New Candidate" +msgid "Add Candidate" +msgstr "إضافة مرشح جديد" + +#: templates/recruitment/candidate_offer_view.html:176 +#, fuzzy +#| msgid "Form Management" +msgid "Offer Management" +msgstr "إدارة النماذج" + +#: templates/recruitment/candidate_offer_view.html:179 +msgid "Candidates in Offer Stage:" +msgstr "" + +#: templates/recruitment/candidate_offer_view.html:185 +msgid "Export offer candidates to CSV" +msgstr "" + +#: templates/recruitment/candidate_offer_view.html:213 +msgid "To Hired" +msgstr "" + +#: templates/recruitment/candidate_offer_view.html:216 +#, fuzzy +#| msgid "Rejected" +msgid "To Rejected" +msgstr "رفض" + +#: templates/recruitment/candidate_offer_view.html:313 +msgid "No candidates are currently in the Offer stage for this job." +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:224 +#, fuzzy +#| msgid "Application" +msgid "Applicant Screening" +msgstr "التقديم" + +#: templates/recruitment/candidate_screening_view.html:227 +msgid "Job:" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:234 +msgid "Export screening candidates to CSV" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:249 +msgid "AI Scoring & Top Candidate Filter" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:257 +msgid "Min AI Score" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:266 +msgid "Min Years Exp" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:278 +msgid "Any Rating" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:296 +#, fuzzy +#| msgid "New Candidates" +msgid "Top N Candidates" +msgstr "المرشحون الجدد" + +#: templates/recruitment/candidate_screening_view.html:304 +#, fuzzy +#| msgid "Update Material" +msgid "Update Filters" +msgstr "تحديث المادة" + +#: templates/recruitment/candidate_screening_view.html:333 +#, fuzzy +#| msgid "Exam Status" +msgid "Exam Stage" +msgstr "حالة الاختبار" + +#: templates/recruitment/candidate_screening_view.html:367 +#, fuzzy +#| msgid "Contact & Job" +msgid "Contact Info" +msgstr "الاتصال والوظيفة" + +#: templates/recruitment/candidate_screening_view.html:373 +msgid "Is Qualified?" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:376 +msgid "Professional Category" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:379 +msgid "Top 3 Skills" +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:422 +msgid "AI scoring.." +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:469 +msgid "No candidates match the current stage and filter criteria." +msgstr "" + +#: templates/recruitment/candidate_screening_view.html:483 +#, fuzzy +#| msgid "Candidate Profiles" +msgid "Candidate Criteria Review" +msgstr "ملفات المرشحين الشخصية" + +#: templates/recruitment/candidate_update.html:92 +msgid "Update Candidate:" +msgstr "تحديث المرشح:" + +#: templates/recruitment/candidate_update.html:94 +msgid "Edit candidate information and details" +msgstr "تعديل معلومات المرشح وتفاصيله" + +#: templates/recruitment/candidate_update.html:102 +msgid "View Candidate" +msgstr "عرض المرشح" + +#: templates/recruitment/candidate_update.html:116 +msgid "Candidate Form" +msgstr "نموذج المرشح" + +#: templates/recruitment/candidate_update.html:135 +msgid "Update Candidate" +msgstr "تحديث المرشح" + +#: templates/recruitment/dashboard.html:4 +#, fuzzy +#| msgid "Dashboard" +msgid "Recruitment Dashboard" +msgstr "لوحة التحكم" + +#: templates/recruitment/dashboard.html:123 +msgid "Recruitment Analytics" +msgstr "" + +#: templates/recruitment/dashboard.html:133 +msgid "Data Scope: " +msgstr "" + +#: templates/recruitment/dashboard.html:135 +msgid "Data Scope: All Jobs" +msgstr "" + +#: templates/recruitment/dashboard.html:140 +#, fuzzy +#| msgid "Edit Job" +msgid "Filter Job:" +msgstr "تعديل الوظيفة" + +#: templates/recruitment/dashboard.html:142 +msgid "All Jobs (Default View)" +msgstr "" + +#: templates/recruitment/dashboard.html:170 +msgid "Daily Candidate Applications Trend" +msgstr "" + +#: templates/recruitment/dashboard.html:184 +#, fuzzy +#| msgid "Application Form" +msgid "Top 5 Application Volume" +msgstr "نموذج التقديم" + +#: templates/recruitment/dashboard.html:200 +msgid "Pipeline Funnel: " +msgstr "" + +#: templates/recruitment/dashboard.html:202 +msgid "Total Pipeline Funnel (All Jobs)" +msgstr "" + +#: templates/recruitment/dashboard.html:216 +msgid "Time-to-Hire Target Check" +msgstr "" + +#: templates/recruitment/dashboard.html:231 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidates From Each Sources" +msgstr "نموذج المرشح" + +#: templates/recruitment/dashboard.html:278 +msgid "Top 5 Most Applied Jobs" +msgstr "" + +#: templates/recruitment/dashboard.html:286 +#, fuzzy +#| msgid "Applications" +msgid "Total Applications" +msgstr "التقديمات" + +#: templates/recruitment/dashboard.html:338 +#, fuzzy +#| msgid "Candidate Form" +msgid "Candidate Count" +msgstr "نموذج المرشح" + +#: templates/recruitment/dashboard.html:406 +#, fuzzy +#| msgid "Create Job" +msgid "Current Job" +msgstr "إنشاء وظيفة" + +#: templates/recruitment/dashboard.html:431 +msgid "Daily Applications (Last 30 Days)" +msgstr "" + +#: templates/recruitment/dashboard.html:446 +#: venv/lib/python3.13/site-packages/unfold/widgets.py:598 +#: venv/lib/python3.13/site-packages/unfold/widgets.py:639 +#, fuzzy +#| msgid "End Date" +msgid "Date" +msgstr "تاريخ الانتهاء" + +#: templates/recruitment/dashboard.html:451 +msgid "New Candidates" +msgstr "المرشحون الجدد" + +#: templates/recruitment/notification_confirm_all_read.html:4 +msgid "Mark All as Read" +msgstr "" + +#: templates/recruitment/notification_confirm_all_read.html:22 +msgid "What this will do" +msgstr "" + +#: templates/recruitment/notification_confirm_all_read.html:25 +#, python-format +msgid "" +"\n" +" This will mark %(count)s unread " +"notification as read.\n" +" " +msgid_plural "" +"\n" +" This will mark all %(count)s unread " +"notifications as read.\n" +" " +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: templates/recruitment/notification_confirm_all_read.html:32 +msgid "" +"You can still view all notifications in your notification list, but they " +"won't appear as unread." +msgstr "" + +#: templates/recruitment/notification_confirm_all_read.html:38 +msgid "All caught up!" +msgstr "" + +#: templates/recruitment/notification_confirm_all_read.html:41 +msgid "You don't have any unread notifications to mark as read." +msgstr "" + +#: templates/recruitment/notification_confirm_all_read.html:50 +msgid "Yes, Mark All as Read" +msgstr "" + +#: templates/recruitment/notification_confirm_all_read.html:58 +#: templates/recruitment/notification_detail.html:18 +#, fuzzy +#| msgid "Back to Meetings" +msgid "Back to Notifications" +msgstr "العودة إلى الاجتماعات" + +#: templates/recruitment/notification_confirm_delete.html:4 +#, fuzzy +#| msgid "Delete Meeting" +msgid "Delete Notification" +msgstr "حذف الاجتماع" + +#: templates/recruitment/notification_confirm_delete.html:20 +msgid "Notification Preview" +msgstr "" + +#: templates/recruitment/notification_confirm_delete.html:30 +#, fuzzy +#| msgid "Delete" +msgid "Yes, Delete" +msgstr "حذف" + +#: templates/recruitment/notification_detail.html:4 +#: templates/recruitment/notification_detail.html:12 +#, fuzzy +#| msgid "Meeting Details" +msgid "Notification Details" +msgstr "تفاصيل الاجتماع" + +#: templates/recruitment/notification_detail.html:14 +msgid "View notification details and manage your preferences" +msgstr "" + +#: templates/recruitment/notification_detail.html:47 +#: templates/recruitment/notification_detail.html:132 +msgid "Mark as Read" +msgstr "" + +#: templates/recruitment/notification_detail.html:51 +#: templates/recruitment/notification_detail.html:136 +msgid "Mark as Unread" +msgstr "" + +#: templates/recruitment/notification_detail.html:65 +msgid "Topic:" +msgstr "الموضوع:" + +#: templates/recruitment/notification_detail.html:68 +#, fuzzy +#| msgid "Start Time" +msgid "Start Time:" +msgstr "وقت البدء" + +#: templates/recruitment/notification_detail.html:75 +#, fuzzy +#| msgid "Join Meeting" +msgid "View Meeting" +msgstr "الانضمام إلى الاجتماع" + +#: templates/recruitment/notification_detail.html:84 +#: templates/recruitment/notification_detail.html:175 +#, fuzzy +#| msgid "Scheduled" +msgid "Scheduled For" +msgstr "مجدول" + +#: templates/recruitment/notification_detail.html:95 +#: templates/recruitment/notification_detail.html:182 +msgid "Delivery Attempts" +msgstr "" + +#: templates/recruitment/notification_detail.html:98 +#, python-format +msgid "" +"\n" +" This notification has been attempted " +"%(count)s time.\n" +" " +msgid_plural "" +"\n" +" This notification has been attempted " +"%(count)s times.\n" +" " +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: templates/recruitment/notification_detail.html:110 +#, fuzzy +#| msgid "Error" +msgid "Last Error" +msgstr "خطأ" + +#: templates/recruitment/notification_detail.html:150 +#, fuzzy +#| msgid "Join Information" +msgid "Information" +msgstr "معلومات الانضمام" + +#: templates/recruitment/notification_list.html:15 +#, python-format +msgid "" +"\n" +" %(count)s notification\n" +" " +msgid_plural "" +"\n" +" %(count)s notifications\n" +" " +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: templates/recruitment/notification_list.html:26 +msgid "Mark All Read" +msgstr "" + +#: templates/recruitment/notification_list.html:39 +#, fuzzy +#| msgid "Status" +msgid "All Status" +msgstr "الحالة" + +#: templates/recruitment/notification_list.html:40 +#: templates/recruitment/notification_list.html:82 +msgid "Unread" +msgstr "" + +#: templates/recruitment/notification_list.html:48 +msgid "All Types" +msgstr "" + +#: templates/recruitment/notification_list.html:57 +msgid "Filter" +msgstr "تصفية" + +#: templates/recruitment/notification_list.html:74 +msgid "Total Notifications" +msgstr "" + +#: templates/recruitment/notification_list.html:90 +msgid "Email Notifications" +msgstr "" + +#: templates/recruitment/notification_list.html:122 +#, fuzzy +#| msgid "Create Zoom Meeting" +msgid "Related to meeting:" +msgstr "إنشاء اجتماع Zoom" + +#: templates/recruitment/notification_list.html:130 +msgid "Mark as read" +msgstr "" + +#: templates/recruitment/notification_list.html:136 +msgid "Mark as unread" +msgstr "" + +#: templates/recruitment/notification_list.html:142 +#, fuzzy +#| msgid "Delete Candidate" +msgid "Delete notification" +msgstr "حذف المرشح" + +#: templates/recruitment/notification_list.html:155 +msgid "Notifications pagination" +msgstr "" + +#: templates/recruitment/notification_list.html:190 +#, fuzzy +#| msgid "No candidates found." +msgid "No notifications found" +msgstr "لم يتم العثور على مرشحين." + +#: templates/recruitment/notification_list.html:193 +msgid "Try adjusting your filters to see more notifications." +msgstr "" + +#: templates/recruitment/notification_list.html:195 +#, fuzzy +#| msgid "You haven't created any form templates yet." +msgid "You don't have any notifications yet." +msgstr "لم تقم بإنشاء أي قوالب نماذج بعد." + +#: templates/recruitment/partials/_candidate_table.html:10 +msgid "Name / Contact" +msgstr "" + +#: templates/recruitment/partials/_candidate_table.html:64 +msgid "View Details and Score Breakdown" +msgstr "" + +#: templates/recruitment/partials/_candidate_table.html:75 +#, fuzzy +#| msgid "Create Candidate" +msgid "Mark as Potential Candidate" +msgstr "إنشاء مرشح" + +#: templates/recruitment/partials/_candidate_table.html:83 +msgid "Move to Next Stage" +msgstr "" + +#: templates/recruitment/partials/_candidate_table.html:92 +msgid "Move to" +msgstr "" + +#: templates/recruitment/partials/_candidate_table.html:102 +#, fuzzy +#| msgid "Exam Status" +msgid "Update Exam Status" +msgstr "حالة الاختبار" + +#: templates/recruitment/partials/_candidate_table.html:120 +#, fuzzy +#| msgid "No candidates found." +msgid "No candidates found in this list." +msgstr "لم يتم العثور على مرشحين." + +#: templates/recruitment/partials/_candidate_table.html:122 +msgid "" +"Adjust your 'Top N' filter in the controls above or check the All Applicants " +"list." +msgstr "" + +#: templates/recruitment/partials/_guage_chart.html:36 +#: templates/recruitment/partials/_guage_chart.html:50 +msgid "Days" +msgstr "" + +#: templates/recruitment/partials/_guage_chart.html:50 +msgid "Target:" +msgstr "" + +#: templates/recruitment/partials/_guage_chart.html:50 +msgid "Max Scale:" +msgstr "" + +#: templates/recruitment/partials/ai_overview_breadcromb.html:25 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_icon.html:7 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_icon.html:9 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_icon.html:11 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_logo.html:5 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_logo.html:7 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_logo.html:9 +msgid "Home" +msgstr "الرئيسية" + +#: templates/recruitment/partials/ai_overview_breadcromb.html:113 +#, fuzzy +#| msgid "Job Overview" +msgid "AI Overview" +msgstr "نظرة عامة على الوظيفة" + +#: templates/recruitment/partials/stats_cards.html:10 +#, fuzzy +#| msgid "Contact & Job" +msgid "Total Jobs" +msgstr "الاتصال والوظيفة" + +#: templates/recruitment/partials/stats_cards.html:13 +msgid "All Active & Drafted Positions (Global)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:19 +msgid "Active Jobs" +msgstr "الوظائف النشطة" + +#: templates/recruitment/partials/stats_cards.html:22 +msgid "Currently Open Requisitions (Scoped)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:31 +msgid "Total Profiles in Current Scope" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:40 +msgid "Total Slots to be Filled (Scoped)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:46 +#, fuzzy +#| msgid "Participant Video" +msgid "Total Participants" +msgstr "فيديو المشارك" + +#: templates/recruitment/partials/stats_cards.html:49 +msgid "Total Recruiters/Interviewers (Global)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:55 +#, fuzzy +#| msgid "LinkedIn Connected" +msgid "LinkedIn Posts" +msgstr "LinkedIn متصل" + +#: templates/recruitment/partials/stats_cards.html:58 +msgid "Total Job Posts Sent to LinkedIn (Global)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:63 +msgid "New Apps (7 Days)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:66 +msgid "Incoming applications last week" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:71 +msgid "Avg. Apps per Job" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:74 +msgid "Average Applications per Job (Scoped)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:81 +#, fuzzy +#| msgid "Timezone" +msgid "Time-to-Hire" +msgstr "المنطقة الزمنية" + +#: templates/recruitment/partials/stats_cards.html:84 +msgid "Avg. Days (Application to Hired)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:89 +msgid "Avg. Match Score" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:92 +msgid "Average AI Score (Current Scope)" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:100 +#, python-format +msgid "Score ≥ 75%% Profiles" +msgstr "" + +#: templates/recruitment/partials/stats_cards.html:105 +#, fuzzy +#| msgid "Meetings" +msgid "Meetings This Week" +msgstr "الاجتماعات" + +#: templates/recruitment/partials/stats_cards.html:108 +msgid "Scheduled Interviews (Current Week)" +msgstr "" + +#: templates/recruitment/schedule_meeting_form.html:22 +msgid "" +"This candidate has upcoming interviews. You are updating an existing " +"schedule." +msgstr "" + +#: templates/recruitment/schedule_meeting_form.html:27 +#, fuzzy +#| msgid "Candidates" +msgid "Back to Candidates" +msgstr "المرشحون" + +#: templates/recruitment/schedule_meeting_form.html:49 +msgid "" +"Default topic will be 'Interview: [Job Title] with [Candidate Name]' if left " +"empty." +msgstr "" + +#: templates/recruitment/schedule_meeting_form.html:66 +msgid "Please select a date and time for the interview." +msgstr "" + +#: templates/recruitment/training_create.html:107 +msgid "Create New Training Material" +msgstr "إنشاء مادة تدريبية جديدة" + +#: templates/recruitment/training_create.html:109 +msgid "Upload a new document or guide for your team." +msgstr "رفع مستند أو دليل جديد لفريقك." + +#: templates/recruitment/training_create.html:125 +#: templates/recruitment/training_update.html:131 +msgid "Material Details" +msgstr "تفاصيل المادة" + +#: templates/recruitment/training_list.html:132 +msgid "Add New Material" +msgstr "إضافة مادة جديدة" + +#: templates/recruitment/training_list.html:141 +msgid "Search by Title or Creator" +msgstr "" + +#: templates/recruitment/training_list.html:170 +#: templates/recruitment/training_list.html:205 +msgid "Created By" +msgstr "أنشأ بواسطة" + +#: templates/recruitment/training_list.html:171 +#, fuzzy +#| msgid "Created" +msgid "Created On" +msgstr "تم الإنشاء" + +#: templates/recruitment/training_list.html:274 +#, fuzzy +#| msgid "No training materials found." +msgid "No training materials found" +msgstr "لم يتم العثور على مواد تدريبية." + +#: templates/recruitment/training_list.html:275 +msgid "It looks like there are no materials yet. Start by adding one!" +msgstr "يبدو أنه لا توجد مواد بعد. ابدأ بإضافة واحدة!" + +#: templates/recruitment/training_list.html:278 +msgid "Create Your First Material" +msgstr "إنشاء أول مادة لك" + +#: templates/recruitment/training_update.html:107 +msgid "Update Training Material:" +msgstr "تحديث المادة التدريبية:" + +#: templates/recruitment/training_update.html:109 +msgid "Edit the details of this training document or guide." +msgstr "تعديل تفاصيل هذا المستند التدريبي أو الدليل." + +#: templates/recruitment/training_update.html:117 +msgid "View Material" +msgstr "عرض المادة" + +#: templates/recruitment/training_update.html:180 +msgid "Update Material" +msgstr "تحديث المادة" + +#: templates/recruitment/training_update.html:182 +msgid "Are you sure you want to delete this material?" +msgstr "هل أنت متأكد من رغبتك في حذف هذه المادة؟" + +#: templates/unfold/components/table.html:43 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/components/table.html:89 +msgid "No data" +msgstr "لا توجد بيانات" + +#: templates/user/admin_settings.html:6 +#, fuzzy +#| msgid "Settings" +msgid "Admin Settings" +msgstr "الإعدادات" + +#: templates/user/admin_settings.html:149 +msgid "Admin Settings Dashboard" +msgstr "" + +#: templates/user/admin_settings.html:159 +msgid "Staff User List" +msgstr "" + +#: templates/user/admin_settings.html:163 +#, fuzzy +#| msgid "Create New Form" +msgid "Create New User" +msgstr "إنشاء نموذج جديد" + +#: templates/user/admin_settings.html:177 +#, fuzzy +#| msgid "First Name" +msgid "First Join" +msgstr "الاسم الأول" + +#: templates/user/admin_settings.html:178 templates/user/profile.html:178 +msgid "Last Login" +msgstr "" + +#: templates/user/admin_settings.html:231 +msgid "Deactivate User" +msgstr "" + +#: templates/user/admin_settings.html:238 +#, fuzzy +#| msgid "Active Jobs" +msgid "Activate User" +msgstr "الوظائف النشطة" + +#: templates/user/admin_settings.html:239 +#, fuzzy +#| msgid "Active" +msgid "Activate" +msgstr "نشط" + +#: templates/user/admin_settings.html:248 +#, fuzzy +#| msgid "No training materials found." +msgid "No staff users found." +msgstr "لم يتم العثور على مواد تدريبية." + +#: templates/user/create_staff.html:6 templates/user/create_staff.html:37 +#: templates/user/create_staff.html:69 +#, fuzzy +#| msgid "Create Material" +msgid "Create Staff User" +msgstr "إنشاء مادة" + +#: templates/user/create_staff.html:76 +#, fuzzy +#| msgid "Back to Meetings" +msgid "Back to Settings" +msgstr "العودة إلى الاجتماعات" + +#: templates/user/profile.html:5 +#, fuzzy +#| msgid "Profile" +msgid "User Profile" +msgstr "الملف الشخصي" + +#: templates/user/profile.html:142 +msgid "Manage email addresses" +msgstr "" + +#: templates/user/profile.html:157 +msgid "Security" +msgstr "" + +#: templates/user/profile.html:164 +#, fuzzy +#| msgid "Candidate Profiles" +msgid "Change Profile Image" +msgstr "ملفات المرشحين الشخصية" + +#: templates/user/profile.html:170 +#, fuzzy +#| msgid "Sync Status" +msgid "Account Status" +msgstr "حالة المزامنة" + +#: templates/user/profile.html:173 +msgid "Username" +msgstr "اسم المستخدم" + +#: templates/user/profile.html:185 +msgid "Date Joined" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/_termui_impl.py:608 +#, python-brace-format +msgid "{editor}: Editing failed" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/_termui_impl.py:612 +#, python-brace-format +msgid "{editor}: Editing failed: {e}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1104 +#: venv/lib/python3.13/site-packages/click/core.py:1141 +#, python-brace-format +msgid "{text} {deprecated_message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1160 +#: venv/lib/python3.13/site-packages/typer/core.py:633 +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:96 +#, fuzzy +#| msgid "Actions" +msgid "Options" +msgstr "الإجراءات" + +#: venv/lib/python3.13/site-packages/click/core.py:1222 +#, python-brace-format +msgid "Got unexpected extra argument ({args})" +msgid_plural "Got unexpected extra arguments ({args})" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/core.py:1241 +msgid "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1425 +#: venv/lib/python3.13/site-packages/typer/core.py:249 +msgid "Aborted!" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1799 +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:97 +msgid "Commands" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1830 +msgid "Missing command." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:1908 +msgid "No such command {name!r}." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:2332 +msgid "Value must be an iterable." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:2355 +#, python-brace-format +msgid "Takes {nargs} values but 1 was given." +msgid_plural "Takes {nargs} values but {len} were given." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/core.py:2505 +msgid "" +"DeprecationWarning: The {param_type} {name!r} is deprecated.{extra_message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:2956 +#: venv/lib/python3.13/site-packages/typer/core.py:553 +#, python-brace-format +msgid "env var: {var}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:2959 +#: venv/lib/python3.13/site-packages/typer/core.py:366 +#: venv/lib/python3.13/site-packages/typer/core.py:574 +#, python-brace-format +msgid "default: {default}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/core.py:3023 +#: venv/lib/python3.13/site-packages/typer/core.py:114 +msgid "(dynamic)" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/decorators.py:465 +#, python-format +msgid "%(prog)s, version %(version)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/decorators.py:522 +msgid "Show the version and exit." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/decorators.py:548 +msgid "Show this message and exit." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:50 +#: venv/lib/python3.13/site-packages/click/exceptions.py:89 +#, fuzzy, python-brace-format +#| msgid "Error Message" +msgid "Error: {message}" +msgstr "رسالة الخطأ" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:81 +#, python-brace-format +msgid "Try '{command} {option}' for help." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:130 +#, python-brace-format +msgid "Invalid value: {message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:132 +#, python-brace-format +msgid "Invalid value for {param_hint}: {message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:190 +msgid "Missing argument" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:192 +#, fuzzy +#| msgid "Meeting Information" +msgid "Missing option" +msgstr "معلومات الاجتماع" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:194 +msgid "Missing parameter" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:196 +#, python-brace-format +msgid "Missing {param_type}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:203 +#, python-brace-format +msgid "Missing parameter: {param_name}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:223 +#, python-brace-format +msgid "No such option: {name}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:235 +#, python-brace-format +msgid "Did you mean {possibility}?" +msgid_plural "(Possible options: {possibilities})" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:282 +msgid "unknown error" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/exceptions.py:289 +msgid "Could not open file {filename!r}: {message}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/formatting.py:156 +#, fuzzy +#| msgid "Stage:" +msgid "Usage:" +msgstr "المرحلة:" + +#: venv/lib/python3.13/site-packages/click/parser.py:199 +msgid "Argument {name!r} takes {nargs} values." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/parser.py:381 +msgid "Option {name!r} does not take a value." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/parser.py:444 +msgid "Option {name!r} requires an argument." +msgid_plural "Option {name!r} requires {nargs} arguments." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/shell_completion.py:332 +msgid "Shell completion is not supported for Bash versions older than 4.4." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/shell_completion.py:339 +msgid "Couldn't detect Bash version, shell completion is not supported." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/termui.py:162 +#, fuzzy +#| msgid "Meeting Information" +msgid "Repeat for confirmation" +msgstr "معلومات الاجتماع" + +#: venv/lib/python3.13/site-packages/click/termui.py:178 +msgid "Error: The value you entered was invalid." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/termui.py:180 +#, fuzzy, python-brace-format +#| msgid "Error Message" +msgid "Error: {e.message}" +msgstr "رسالة الخطأ" + +#: venv/lib/python3.13/site-packages/click/termui.py:191 +msgid "Error: The two entered values do not match." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/termui.py:247 +msgid "Error: invalid input" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/termui.py:866 +msgid "Press any key to continue..." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:332 +#, python-brace-format +msgid "" +"Choose from:\n" +"\t{choices}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:369 +msgid "{value!r} is not {choice}." +msgid_plural "{value!r} is not one of {choices}." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/types.py:460 +msgid "{value!r} does not match the format {format}." +msgid_plural "{value!r} does not match the formats {formats}." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/click/types.py:482 +msgid "{value!r} is not a valid {number_type}." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:538 +#, python-brace-format +msgid "{value} is not in the range {range}." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:719 +msgid "{value!r} is not a valid boolean. Recognized values: {states}" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:747 +msgid "{value!r} is not a valid UUID." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:937 +#, fuzzy +#| msgid "Profile" +msgid "file" +msgstr "الملف الشخصي" + +#: venv/lib/python3.13/site-packages/click/types.py:939 +msgid "directory" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:941 +msgid "path" +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:988 +msgid "{name} {filename!r} does not exist." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:997 +msgid "{name} {filename!r} is a file." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1005 +msgid "{name} {filename!r} is a directory." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1014 +msgid "{name} {filename!r} is not readable." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1023 +msgid "{name} {filename!r} is not writable." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1032 +msgid "{name} {filename!r} is not executable." +msgstr "" + +#: venv/lib/python3.13/site-packages/click/types.py:1099 +#, python-brace-format +msgid "{len_type} values are required, but {len_value} was given." +msgid_plural "{len_type} values are required, but {len_value} were given." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/constructive_geometries/geomatcher.py:240 +msgid "RoW" +msgstr "" + +#: venv/lib/python3.13/site-packages/constructive_geometries/geomatcher.py:240 +#: venv/lib/python3.13/site-packages/constructive_geometries/geomatcher.py:243 +msgid "GLO" +msgstr "" + +#: venv/lib/python3.13/site-packages/constructive_geometries/geomatcher.py:243 +msgid "RoE" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/contrib/messages/apps.py:16 +#, fuzzy +#| msgid "Error Message" +msgid "Messages" +msgstr "رسالة الخطأ" + +#: venv/lib/python3.13/site-packages/django/contrib/sitemaps/apps.py:8 +msgid "Site Maps" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/contrib/staticfiles/apps.py:9 +msgid "Static Files" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/contrib/syndication/apps.py:7 +#, fuzzy +#| msgid "Application" +msgid "Syndication" +msgstr "التقديم" + +#. Translators: String used to replace omitted page numbers in elided page +#. range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. +#: venv/lib/python3.13/site-packages/django/core/paginator.py:30 +msgid "…" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/paginator.py:32 +msgid "That page number is not an integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/paginator.py:33 +msgid "That page number is less than 1" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/paginator.py:34 +msgid "That page contains no results" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:22 +msgid "Enter a valid value." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:70 +#, fuzzy +#| msgid "Enter last name" +msgid "Enter a valid domain name." +msgstr "أدخل اسم العائلة" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:153 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:775 +msgid "Enter a valid URL." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:200 +msgid "Enter a valid integer." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:211 +msgid "Enter a valid email address." +msgstr "" + +#. Translators: "letters" means latin letters: a-z and A-Z. +#: venv/lib/python3.13/site-packages/django/core/validators.py:289 +msgid "" +"Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:297 +msgid "" +"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or " +"hyphens." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:309 +#: venv/lib/python3.13/site-packages/django/core/validators.py:318 +#: venv/lib/python3.13/site-packages/django/core/validators.py:332 +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2220 +#, python-format +msgid "Enter a valid %(protocol)s address." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:311 +msgid "IPv4" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:320 +#: venv/lib/python3.13/site-packages/django/utils/ipv6.py:43 +msgid "IPv6" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:334 +msgid "IPv4 or IPv6" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:375 +msgid "Enter only digits separated by commas." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:381 +#, python-format +msgid "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:416 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:425 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:434 +#, python-format +msgid "Ensure this value is a multiple of step size %(limit_value)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:441 +#, python-format +msgid "" +"Ensure this value is a multiple of step size %(limit_value)s, starting from " +"%(offset)s, e.g. %(offset)s, %(valid_value1)s, %(valid_value2)s, and so on." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:473 +#, python-format +msgid "" +"Ensure this value has at least %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at least %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:491 +#, python-format +msgid "" +"Ensure this value has at most %(limit_value)d character (it has " +"%(show_value)d)." +msgid_plural "" +"Ensure this value has at most %(limit_value)d characters (it has " +"%(show_value)d)." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:514 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:366 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:405 +#, fuzzy +#| msgid "Enter phone number" +msgid "Enter a number." +msgstr "أدخل رقم الهاتف" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:516 +#, python-format +msgid "Ensure that there are no more than %(max)s digit in total." +msgid_plural "Ensure that there are no more than %(max)s digits in total." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:521 +#, python-format +msgid "Ensure that there are no more than %(max)s decimal place." +msgid_plural "Ensure that there are no more than %(max)s decimal places." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:526 +#, python-format +msgid "" +"Ensure that there are no more than %(max)s digit before the decimal point." +msgid_plural "" +"Ensure that there are no more than %(max)s digits before the decimal point." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:597 +#, python-format +msgid "" +"File extension “%(extension)s” is not allowed. Allowed extensions are: " +"%(allowed_extensions)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/core/validators.py:659 +msgid "Null characters are not allowed." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/base.py:1600 +#: venv/lib/python3.13/site-packages/django/forms/models.py:908 +#: venv/lib/python3.13/site-packages/unfold/contrib/inlines/admin.py:108 +msgid "and" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/base.py:1602 +#, python-format +msgid "%(model_name)s with this %(field_labels)s already exists." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/constraints.py:22 +#, python-format +msgid "Constraint “%(name)s” is violated." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:134 +#, python-format +msgid "Value %(value)r is not a valid choice." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:135 +msgid "This field cannot be null." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:136 +msgid "This field cannot be blank." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:137 +#, python-format +msgid "%(model_name)s with this %(field_label)s already exists." +msgstr "" + +#. Translators: The 'lookup_type' is one of 'date', 'year' or +#. 'month'. Eg: "Title must be unique for pub_date year" +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:141 +#, python-format +msgid "" +"%(field_label)s must be unique for %(date_field_label)s %(lookup_type)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:180 +#, python-format +msgid "Field of type: %(field_type)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1162 +#, python-format +msgid "“%(value)s” value must be either True or False." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1163 +#, python-format +msgid "“%(value)s” value must be either True, False, or None." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1165 +msgid "Boolean (Either True or False)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1215 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1217 +msgid "String (unlimited)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1326 +#, fuzzy +#| msgid "Comma-separated list of trusted IP addresses" +msgid "Comma-separated integers" +msgstr "قائمة عناوين IP الموثوقة مفصولة بفواصل" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1427 +#, python-format +msgid "" +"“%(value)s” value has an invalid date format. It must be in YYYY-MM-DD " +"format." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1431 +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1566 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD) but it is an invalid " +"date." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1435 +msgid "Date (without time)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1562 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in YYYY-MM-DD " +"HH:MM[:ss[.uuuuuu]][TZ] format." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1570 +#, python-format +msgid "" +"“%(value)s” value has the correct format (YYYY-MM-DD HH:MM[:ss[.uuuuuu]]" +"[TZ]) but it is an invalid date/time." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1575 +msgid "Date (with time)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1702 +#, python-format +msgid "“%(value)s” value must be a decimal number." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1704 +msgid "Decimal number" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1864 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in [DD] " +"[[HH:]MM:]ss[.uuuuuu] format." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1920 +#, fuzzy +#| msgid "IP Address" +msgid "Email address" +msgstr "عنوان IP" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:1945 +#, fuzzy +#| msgid "File" +msgid "File path" +msgstr "ملف" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2023 +#, python-format +msgid "“%(value)s” value must be a float." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2025 +#, fuzzy +#| msgid "Enter phone number" +msgid "Floating point number" +msgstr "أدخل رقم الهاتف" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2065 +#, python-format +msgid "“%(value)s” value must be an integer." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2067 +#, fuzzy +#| msgid "Interview" +msgid "Integer" +msgstr "المقابلة" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2163 +msgid "Big (8 byte) integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2180 +msgid "Small integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2188 +#, fuzzy +#| msgid "IP Address" +msgid "IPv4 address" +msgstr "عنوان IP" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2219 +#, fuzzy +#| msgid "IP Address" +msgid "IP address" +msgstr "عنوان IP" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2310 +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2311 +#, python-format +msgid "“%(value)s” value must be either None, True or False." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2313 +msgid "Boolean (Either True, False or None)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2364 +msgid "Positive big integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2379 +msgid "Positive integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2394 +msgid "Positive small integer" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2410 +#, python-format +msgid "Slug (up to %(max_length)s)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2446 +msgid "Text" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2526 +#, python-format +msgid "" +"“%(value)s” value has an invalid format. It must be in HH:MM[:ss[.uuuuuu]] " +"format." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2530 +#, python-format +msgid "" +"“%(value)s” value has the correct format (HH:MM[:ss[.uuuuuu]]) but it is an " +"invalid time." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2534 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:263 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:279 +#: venv/lib/python3.13/site-packages/unfold/widgets.py:599 +#: venv/lib/python3.13/site-packages/unfold/widgets.py:644 +#, fuzzy +#| msgid "Timezone" +msgid "Time" +msgstr "المنطقة الزمنية" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2642 +msgid "URL" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2666 +msgid "Raw binary data" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2731 +#, python-format +msgid "“%(value)s” is not a valid UUID." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/__init__.py:2733 +msgid "Universally unique identifier" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/files.py:420 +msgid "Image" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/json.py:24 +msgid "A JSON object" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/json.py:26 +msgid "Value must be valid JSON." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:979 +#, python-format +msgid "%(model)s instance with %(field)s %(value)r is not a valid choice." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:982 +msgid "Foreign Key (type determined by related field)" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:1276 +msgid "One-to-one relationship" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:1333 +#, python-format +msgid "%(from)s-%(to)s relationship" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:1335 +#, python-format +msgid "%(from)s-%(to)s relationships" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/db/models/fields/related.py:1383 +msgid "Many-to-many relationship" +msgstr "" + +#. Translators: If found as last label character, these punctuation +#. characters will prevent the default label_suffix to be appended to the label +#: venv/lib/python3.13/site-packages/django/forms/boundfield.py:185 +msgid ":?.!" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:95 +msgid "This field is required." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:315 +#, fuzzy +#| msgid "Enter phone number" +msgid "Enter a whole number." +msgstr "أدخل رقم الهاتف" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:486 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1267 +#, fuzzy +#| msgid "Interview Date" +msgid "Enter a valid date." +msgstr "تاريخ المقابلة" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:509 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1268 +#, fuzzy +#| msgid "Enter last name" +msgid "Enter a valid time." +msgstr "أدخل اسم العائلة" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:536 +msgid "Enter a valid date/time." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:570 +#, fuzzy +#| msgid "Internal Information" +msgid "Enter a valid duration." +msgstr "المعلومات الداخلية" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:571 +#, python-brace-format +msgid "The number of days must be between {min_days} and {max_days}." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:640 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:641 +msgid "No file was submitted." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:642 +msgid "The submitted file is empty." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:644 +#, python-format +msgid "Ensure this filename has at most %(max)d character (it has %(length)d)." +msgid_plural "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:649 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:717 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:889 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:975 +#: venv/lib/python3.13/site-packages/django/forms/models.py:1592 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:977 +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1096 +#: venv/lib/python3.13/site-packages/django/forms/models.py:1590 +#, fuzzy +#| msgid "Enter last name" +msgid "Enter a list of values." +msgstr "أدخل اسم العائلة" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1097 +#, fuzzy +#| msgid "Enter template name" +msgid "Enter a complete value." +msgstr "أدخل اسم القالب" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1339 +msgid "Enter a valid UUID." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/fields.py:1369 +msgid "Enter a valid JSON." +msgstr "" + +#. Translators: This is the default suffix added to form field labels +#: venv/lib/python3.13/site-packages/django/forms/forms.py:97 +msgid ":" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/forms.py:239 +#, python-format +msgid "(Hidden field %(name)s) %(error)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:61 +#, python-format +msgid "" +"ManagementForm data is missing or has been tampered with. Missing fields: " +"%(field_names)s. You may need to file a bug report if the issue persists." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:65 +#, python-format +msgid "Please submit at most %(num)d form." +msgid_plural "Please submit at most %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:70 +#, python-format +msgid "Please submit at least %(num)d form." +msgid_plural "Please submit at least %(num)d forms." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:484 +#: venv/lib/python3.13/site-packages/django/forms/formsets.py:491 +msgid "Order" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:901 +#, python-format +msgid "Please correct the duplicate data for %(field)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:906 +#, python-format +msgid "Please correct the duplicate data for %(field)s, which must be unique." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:913 +#, python-format +msgid "" +"Please correct the duplicate data for %(field_name)s which must be unique " +"for the %(lookup)s in %(date_field)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:922 +msgid "Please correct the duplicate values below." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:1359 +msgid "The inline value did not match the parent instance." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:1450 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/models.py:1594 +#, python-format +msgid "“%(pk)s” is not a valid value." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/utils.py:229 +#, python-format +msgid "" +"%(datetime)s couldn’t be interpreted in time zone %(current_timezone)s; it " +"may be ambiguous or it may not exist." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:528 +msgid "Currently" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:529 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/edit_inline/stacked.html:63 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:42 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_title.html:23 +#, fuzzy +#| msgid "Change Stage" +msgid "Change" +msgstr "تغيير المرحلة" + +#: venv/lib/python3.13/site-packages/django/forms/widgets.py:866 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/boolean.html:4 +msgid "Unknown" +msgstr "" + +#. Translators: Please do not add spaces around commas. +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:873 +msgid "yes,no,maybe" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:903 +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:920 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:922 +#, python-format +msgid "%s KB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:924 +#, python-format +msgid "%s MB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:926 +#, python-format +msgid "%s GB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:928 +#, python-format +msgid "%s TB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/template/defaultfilters.py:930 +#, python-format +msgid "%s PB" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:74 +msgid "p.m." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:75 +msgid "a.m." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:80 +msgid "PM" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:81 +msgid "AM" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:153 +msgid "midnight" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dateformat.py:155 +msgid "noon" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:7 +msgid "Monday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:8 +msgid "Tuesday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:9 +msgid "Wednesday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:10 +msgid "Thursday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:11 +msgid "Friday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:12 +msgid "Saturday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:13 +msgid "Sunday" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:16 +msgid "Mon" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:17 +msgid "Tue" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:18 +msgid "Wed" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:19 +msgid "Thu" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:20 +msgid "Fri" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:21 +#, fuzzy +#| msgid "Status" +msgid "Sat" +msgstr "الحالة" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:22 +msgid "Sun" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:25 +msgid "January" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:26 +msgid "February" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:27 +#, fuzzy +#| msgid "Search" +msgid "March" +msgstr "بحث" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:28 +msgid "April" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:29 +msgid "May" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:30 +msgid "June" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:31 +msgid "July" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:32 +msgid "August" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:33 +msgid "September" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:34 +msgid "October" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:35 +msgid "November" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:36 +msgid "December" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:39 +msgid "jan" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:40 +msgid "feb" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:41 +#, fuzzy +#| msgid "Summary" +msgid "mar" +msgstr "الملخص" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:42 +msgid "apr" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:43 +#, fuzzy +#| msgid "Summary" +msgid "may" +msgstr "الملخص" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:44 +msgid "jun" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:45 +msgid "jul" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:46 +#, fuzzy +#| msgid "ago" +msgid "aug" +msgstr "منذ" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:47 +msgid "sep" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:48 +msgid "oct" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:49 +msgid "nov" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:50 +msgid "dec" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:53 +msgctxt "abbrev. month" +msgid "Jan." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:54 +msgctxt "abbrev. month" +msgid "Feb." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:55 +#, fuzzy +#| msgid "Search" +msgctxt "abbrev. month" +msgid "March" +msgstr "بحث" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:56 +msgctxt "abbrev. month" +msgid "April" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:57 +msgctxt "abbrev. month" +msgid "May" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:58 +msgctxt "abbrev. month" +msgid "June" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:59 +msgctxt "abbrev. month" +msgid "July" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:60 +msgctxt "abbrev. month" +msgid "Aug." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:61 +msgctxt "abbrev. month" +msgid "Sept." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:62 +msgctxt "abbrev. month" +msgid "Oct." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:63 +msgctxt "abbrev. month" +msgid "Nov." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:64 +msgctxt "abbrev. month" +msgid "Dec." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:67 +msgctxt "alt. month" +msgid "January" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:68 +msgctxt "alt. month" +msgid "February" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:69 +#, fuzzy +#| msgid "Search" +msgctxt "alt. month" +msgid "March" +msgstr "بحث" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:70 +msgctxt "alt. month" +msgid "April" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:71 +msgctxt "alt. month" +msgid "May" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:72 +msgctxt "alt. month" +msgid "June" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:73 +msgctxt "alt. month" +msgid "July" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:74 +msgctxt "alt. month" +msgid "August" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:75 +msgctxt "alt. month" +msgid "September" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:76 +msgctxt "alt. month" +msgid "October" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:77 +msgctxt "alt. month" +msgid "November" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/dates.py:78 +msgctxt "alt. month" +msgid "December" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/ipv6.py:20 +msgid "This is not a valid IPv6 address." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/text.py:76 +#, python-format +msgctxt "String to return when truncating text" +msgid "%(truncated_text)s…" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/text.py:287 +#, fuzzy +#| msgid "More" +msgid "or" +msgstr "المزيد" + +#. Translators: This string is used as a separator between list elements +#: venv/lib/python3.13/site-packages/django/utils/text.py:306 +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:135 +msgid ", " +msgstr "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:8 +#, python-format +msgid "%(num)d year" +msgid_plural "%(num)d years" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:9 +#, python-format +msgid "%(num)d month" +msgid_plural "%(num)d months" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:10 +#, python-format +msgid "%(num)d week" +msgid_plural "%(num)d weeks" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:11 +#, python-format +msgid "%(num)d day" +msgid_plural "%(num)d days" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:12 +#, python-format +msgid "%(num)d hour" +msgid_plural "%(num)d hours" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/utils/timesince.py:13 +#, python-format +msgid "%(num)d minute" +msgid_plural "%(num)d minutes" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:29 +msgid "Forbidden" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:30 +msgid "CSRF verification failed. Request aborted." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:34 +msgid "" +"You are seeing this message because this HTTPS site requires a “Referer " +"header” to be sent by your web browser, but none was sent. This header is " +"required for security reasons, to ensure that your browser is not being " +"hijacked by third parties." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:40 +msgid "" +"If you have configured your browser to disable “Referer” headers, please re-" +"enable them, at least for this site, or for HTTPS connections, or for “same-" +"origin” requests." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:45 +msgid "" +"If you are using the tag or " +"including the “Referrer-Policy: no-referrer” header, please remove them. The " +"CSRF protection requires the “Referer” header to do strict referer checking. " +"If you’re concerned about privacy, use alternatives like for links to third-party sites." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:54 +msgid "" +"You are seeing this message because this site requires a CSRF cookie when " +"submitting forms. This cookie is required for security reasons, to ensure " +"that your browser is not being hijacked by third parties." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:60 +msgid "" +"If you have configured your browser to disable cookies, please re-enable " +"them, at least for this site, or for “same-origin” requests." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/csrf.py:66 +msgid "More information is available with DEBUG=True." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:44 +msgid "No year specified" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:64 +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:115 +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:214 +msgid "Date out of range" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:94 +msgid "No month specified" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:147 +msgid "No day specified" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:194 +msgid "No week specified" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:353 +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:384 +#, python-format +msgid "No %(verbose_name_plural)s available" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:680 +#, python-format +msgid "" +"Future %(verbose_name_plural)s not available because " +"%(class_name)s.allow_future is False." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/dates.py:720 +#, python-format +msgid "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/detail.py:56 +#, python-format +msgid "No %(verbose_name)s found matching the query" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/list.py:70 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/list.py:77 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/generic/list.py:173 +#, python-format +msgid "Empty list and “%(class_name)s.allow_empty” is False." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/static.py:49 +msgid "Directory indexes are not allowed here." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/static.py:51 +#, python-format +msgid "“%(path)s” does not exist" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/static.py:68 +#: venv/lib/python3.13/site-packages/django/views/templates/directory_index.html:8 +#: venv/lib/python3.13/site-packages/django/views/templates/directory_index.html:11 +#, python-format +msgid "Index of %(directory)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:7 +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:204 +msgid "The install worked successfully! Congratulations!" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:206 +#, python-format +msgid "" +"View release notes for Django %(version)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:208 +#, python-format +msgid "" +"You are seeing this page because DEBUG=True is in your settings file and you have not " +"configured any URLs." +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:217 +msgid "Django Documentation" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:218 +msgid "Topics, references, & how-to’s" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:226 +msgid "Tutorial: A Polling App" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:227 +msgid "Get started with Django" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:235 +msgid "Django Community" +msgstr "" + +#: venv/lib/python3.13/site-packages/django/views/templates/default_urlconf.html:236 +msgid "Connect, get help, or contribute" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/permissions.py:18 +msgid "You do not have permission to upload files." +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/permissions.py:25 +msgid "You must be logged in to upload files." +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/validators.py:17 +#, python-format +msgid "File should be at most %(max_size)s MB." +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/views.py:41 +msgid "Invalid form data" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_ckeditor_5/widgets.py:43 +msgid "Check the correct settings.CKEDITOR_5_CONFIGS " +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:72 +msgid "Only POST method is allowed" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:86 +msgid "Attachment module is disabled" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:93 +msgid "Only authenticated users are allowed" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:99 +msgid "No files were requested" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:140 +msgid "File size exceeds the limit allowed and cannot be saved" +msgstr "" + +#: venv/lib/python3.13/site-packages/django_summernote/views.py:160 +msgid "Failed to save attachment" +msgstr "" + +#: venv/lib/python3.13/site-packages/kombu/transport/qpid.py:1311 +#, python-format +msgid "Attempting to connect to qpid with SASL mechanism %s" +msgstr "" + +#: venv/lib/python3.13/site-packages/kombu/transport/qpid.py:1316 +#, python-format +msgid "Connected to qpid with SASL mechanism %s" +msgstr "" + +#: venv/lib/python3.13/site-packages/kombu/transport/qpid.py:1334 +#, python-format +msgid "Unable to connect to qpid with SASL mechanism %s" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/core.py:368 +#: venv/lib/python3.13/site-packages/typer/core.py:583 +msgid "required" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/core.py:630 +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:95 +msgid "Arguments" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:89 +#, fuzzy +#| msgid "Created at" +msgid "(deprecated) " +msgstr "تم الإنشاء في" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:90 +msgid "[default: {}]" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:91 +msgid "[env var: {}]" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:93 +msgid "[required]" +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:99 +msgid "Aborted." +msgstr "" + +#: venv/lib/python3.13/site-packages/typer/rich_utils.py:100 +#, python-brace-format +msgid "Try [blue]'{command_path} {help_option}'[/] for help." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/admin.py:40 +#: venv/lib/python3.13/site-packages/unfold/templatetags/unfold_list.py:337 +#, fuzzy +#| msgid "Select country" +msgid "Select record" +msgstr "اختر الدولة" + +#: venv/lib/python3.13/site-packages/unfold/admin.py:166 +#, fuzzy +#| msgid "Select country" +msgid "Select action" +msgstr "اختر الدولة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:10 +msgid "Collapse" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:20 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:172 +msgid "Value" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:21 +msgid "Default" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:22 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:59 +msgid "Code" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:23 +msgid "Modified" +msgstr "تم التعديل" + +#: venv/lib/python3.13/site-packages/unfold/contrib/constance/templates/admin/constance/includes/results_list.html:65 +msgid "Reset to default" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:46 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:103 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:137 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/choice_filters.py:168 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/dropdown_filters.py:28 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/dropdown_filters.py:61 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/dropdown_filters.py:105 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/mixins.py:71 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/mixins.py:169 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/text_filters.py:29 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/admin/text_filters.py:60 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_date_range.html:5 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html:5 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_numeric_range.html:5 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_numeric_single.html:5 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html:7 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/filter.html:5 +#, python-format +msgid " By %(filter_title)s " +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:193 +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:234 +msgid "To" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:258 +msgid "Date from" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/forms.py:274 +msgid "Date to" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html:30 +#, fuzzy +#| msgid "No data" +msgid "Not enough data." +msgstr "لا توجد بيانات" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/array.html:30 +#, fuzzy +#| msgid "Add New Candidate" +msgid "Add new item" +msgstr "إضافة مرشح جديد" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:7 +msgid "Paragraph" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:11 +msgid "Underlined" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:15 +msgid "Bold" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:19 +msgid "Italic" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:23 +msgid "Strike" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:35 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:39 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:43 +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:47 +msgid "Heading" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:55 +msgid "Quote" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:63 +msgid "Unordered list" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:67 +msgid "Ordered list" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:71 +msgid "Indent increase" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:75 +msgid "Indent decrease" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:83 +msgid "Undo" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:87 +msgid "Redo" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:95 +#, fuzzy +#| msgid "Enter email" +msgid "Enter an URL" +msgstr "أدخل البريد الإلكتروني" + +#: venv/lib/python3.13/site-packages/unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html:102 +msgid "Unlink" +msgstr "فك الربط" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/change_form.html:8 +msgid "Object permissions" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html:13 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html:14 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:9 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:43 +msgid "Object" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html:16 +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:15 +msgid "Group" +msgstr "تجميع" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:7 +msgid "Group permissions" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/group_form.html:67 +#, fuzzy +#| msgid "Manage" +msgid "Manage group" +msgstr "إدارة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:7 +msgid "User permissions" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/guardian/templates/unfold/guardian/user_form.html:67 +#, fuzzy +#| msgid "Manage" +msgid "Manage user" +msgstr "إدارة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/change_form.html:8 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/change_list_export_item.html:4 +msgid "Export" +msgstr "تصدير" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/change_list_import_item.html:4 +msgid "Import" +msgstr "استيراد" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/export.html:18 +#, python-format +msgid "" +"\n" +" Export %(len)s selected item.\n" +" " +msgid_plural "" +"\n" +" Export %(len)s selected items.\n" +" " +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/export.html:37 +msgid "This exporter will export the following fields" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_confirm.html:10 +msgid "" +"Below is a preview of data to be imported. If you are satisfied with the " +"results, click 'Confirm import'" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_confirm.html:15 +#, fuzzy +#| msgid "Confirm Delete" +msgid "Confirm import" +msgstr "تأكيد الحذف" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_errors.html:20 +#, fuzzy +#| msgid "Enter phone number" +msgid "Line number" +msgstr "أدخل رقم الهاتف" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_preview.html:24 +msgid "Skipped" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:6 +msgid "Some rows failed to validate" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:10 +msgid "" +"Please correct these errors in your data where possible, then reupload it " +"using the form above." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:22 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:40 +msgid "Row" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:26 +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:44 +#, fuzzy +#| msgid "Error" +msgid "Errors" +msgstr "خطأ" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/import_validation.html:70 +msgid "Non field specific" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html:6 +msgid "This exporter will export the following fields: " +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html:8 +msgid "This importer will import the following fields: " +msgstr "" + +#. Translators: Model verbose name and instance representation, +#. suitable to be an item in a list. +#: venv/lib/python3.13/site-packages/unfold/contrib/inlines/admin.py:99 +#, python-format +msgid "%(class_name)s %(instance)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/inlines/admin.py:111 +#, python-format +msgid "" +"Deleting %(class_name)s %(instance)s would require deleting the following " +"protected related objects: %(related_objects)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history.html:8 +msgid "" +"Choose a date from the list below to revert to a previous version of this " +"object." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history.html:28 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:57 +#, fuzzy +#| msgid "Country" +msgid "entry" +msgid_plural "entries" +msgstr[0] "الدولة" +msgstr[1] "الدولة" +msgstr[2] "الدولة" +msgstr[3] "الدولة" +msgstr[4] "الدولة" +msgstr[5] "الدولة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history.html:32 +msgid "This object doesn't have a change history." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_form.html:16 +msgid "" +"Press the 'Revert' button below to revert to this version of the object." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_form.html:20 +msgid "Press the 'Change History' button below to edit the history." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:19 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:55 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:10 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:26 +msgid "Date/time" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:27 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:63 +#, fuzzy +#| msgid "Created by" +msgid "Changed by" +msgstr "أنشأ بواسطة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:31 +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:77 +#, fuzzy +#| msgid "Change Stage" +msgid "Change reason" +msgstr "تغيير المرحلة" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/object_history_list.html:73 +msgid "None" +msgstr "لا شيء" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/submit_line.html:8 +msgid "Revert" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/contrib/simple_history/templates/simple_history/submit_line.html:14 +#, fuzzy +#| msgid "Change Stage" +msgid "Change History" +msgstr "تغيير المرحلة" + +#: venv/lib/python3.13/site-packages/unfold/forms.py:71 +#, fuzzy +#| msgid "Select country" +msgid "Select action to run" +msgstr "اختر الدولة" + +#: venv/lib/python3.13/site-packages/unfold/forms.py:129 +msgid "" +"Raw passwords are not stored, so there is no way to see this user’s " +"password, but you can change the password using this form." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/mixins/base_model_admin.py:57 +#: venv/lib/python3.13/site-packages/unfold/mixins/base_model_admin.py:78 +#, fuzzy +#| msgid "Select country" +msgid "Select value" +msgstr "اختر الدولة" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:22 +msgid "Run the selected action" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:23 +msgid "Run" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:43 +msgid "Click here to select the objects across all pages" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:44 +#, python-format +msgid "Select all %(total_count)s %(module_name)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/actions.html:50 +msgid "Clear selection" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/app_list.html:12 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:9 +#, python-format +msgid "Models in the %(name)s application" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/app_list.html:48 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list.html:99 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:59 +msgid "You don’t have permission to view or edit anything." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/auth/user/add_form.html:6 +msgid "After you've created a user, you’ll be able to edit more user options." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/auth/user/change_password.html:19 +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/auth/user/change_password.html:30 +#: venv/lib/python3.13/site-packages/unfold/templates/registration/password_change_form.html:29 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/account_links.html:30 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "كلمة المرور" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_form_object_tools.html:6 +msgid "History" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_form_object_tools.html:12 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/edit_inline/stacked.html:77 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_title.html:33 +#, fuzzy +#| msgid "View on LinkedIn" +msgid "View on site" +msgstr "عرض على LinkedIn" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list.html:69 +msgid "Filters" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:32 +msgid "Select all rows" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:46 +#, fuzzy +#| msgid "Toggle user menu" +msgid "Toggle sorting" +msgstr "تبديل قائمة المستخدم" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:54 +msgid "Remove from sorting" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:60 +#, python-format +msgid "Sorting priority: %(priority_number)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/change_list_results.html:85 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/components/table.html:34 +msgid "Expand row" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_confirmation.html:16 +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_confirmation.html:32 +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the " +"following protected related objects:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_confirmation.html:48 +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_confirmation.html:55 +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_selected_confirmation.html:49 +msgid "Objects" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_selected_confirmation.html:15 +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would result in deleting related " +"objects, but your account doesn't have permission to delete the following " +"types of objects:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_selected_confirmation.html:28 +#, python-format +msgid "" +"Deleting the selected %(objects_name)s would require deleting the following " +"protected related objects:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/delete_selected_confirmation.html:42 +#, python-format +msgid "" +"Are you sure you want to delete the selected %(objects_name)s? All of the " +"following objects and their related items will be deleted:" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/includes/object_delete_summary.html:5 +msgid "Summary" +msgstr "الملخص" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:15 +msgid "Welcome back to" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:26 +#, python-format +msgid "" +"You are authenticated as %(username)s, but are not authorized to access this " +"page. Would you like to login to a different account?" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:47 +msgid "Log in" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/login.html:55 +msgid "Forgotten your password or username?" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/object_history.html:60 +msgid "" +"This object doesn’t have a change history. It probably wasn’t added via this " +"admin site." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/pagination.html:12 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/popup_header.html:14 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/welcomemsg.html:25 +msgid "Show all" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/search_form.html:18 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command.html:24 +msgid "Type to search" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:28 +msgid "Save and continue editing" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:30 +msgid "Save and view" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:37 +msgid "Save and add another" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/admin/submit_line.html:43 +msgid "Save as new" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/logged_out.html:14 +msgid "You have been successfully logged out from the administration" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/logged_out.html:18 +msgid "Thanks for spending some quality time with the web site today." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/logged_out.html:23 +msgid "Log in again" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/password_change_done.html:9 +msgid "Your password was changed." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/registration/password_change_form.html:18 +msgid "" +"Please enter your old password, for security’s sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/account_links.html:17 +#, fuzzy +#| msgid "View Candidate" +msgid "View site" +msgstr "عرض المرشح" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/account_links.html:40 +#, fuzzy +#| msgid "Sign out" +msgid "Log out" +msgstr "تسجيل الخروج" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/actions_row.html:4 +#, fuzzy +#| msgid "Actions" +msgid "More actions" +msgstr "الإجراءات" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/add_link.html:5 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/add_link.html:8 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/empty_results.html:4 +#, python-format +msgid "Add %(name)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list.html:68 +#, fuzzy +#| msgid "Applications" +msgid "All applications" +msgstr "التقديمات" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/app_list_default.html:31 +msgid "Add" +msgstr "إضافة" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/boolean.html:4 +msgid "True" +msgstr "صحيح" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/boolean.html:4 +msgid "False" +msgstr "خطأ" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/change_list_filter_actions.html:16 +msgid "Hide counts" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/change_list_filter_actions.html:20 +#, fuzzy +#| msgid "Your account" +msgid "Show counts" +msgstr "حسابك" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/change_list_filter_actions.html:27 +msgid "Clear all filters" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_history.html:7 +msgid "Recent searches" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_history.html:49 +msgid "No recent searches" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_results.html:44 +#, fuzzy +#| msgid "No templates match your search \"%(query)s\"." +msgid "No results matching your query" +msgstr "لا توجد قوالب تطابق بحثك \"%(query)s\"." + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_results.html:57 +msgid "Loading more results..." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/command_results.html:62 +#, python-format +msgid "" +"\n" +" Found %(counter)s result in %(time)s seconds\n" +" " +msgid_plural "" +"\n" +" Found %(counter)s results in %(time)s seconds\n" +" " +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/delete_submit_line.html:5 +msgid "No, take me back" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/delete_submit_line.html:9 +msgid "Yes, I’m sure" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/display_header.html:10 +msgid "Record picture" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/edit_inline/tabular_heading.html:21 +#, fuzzy +#| msgid "Delete" +msgid "Delete?" +msgstr "حذف" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/empty_results.html:12 +#, fuzzy +#| msgid "No meetings found." +msgid "No results found" +msgstr "لم يتم العثور على اجتماعات." + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/empty_results.html:16 +msgid "" +"This page yielded into no results. Create a new item or reset your filters." +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/empty_results.html:30 +msgid "Reset filters" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/header_back_button.html:7 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/header_back_button.html:15 +msgid "Go back" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/history.html:9 +#, fuzzy +#| msgid "Management Actions" +msgid "Recent actions" +msgstr "إجراءات الإدارة" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/history.html:28 +msgid "Unknown content" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/messages/errornote.html:5 +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/pagination_infinite.html:5 +#, fuzzy +#| msgid "Preview" +msgid "Previous" +msgstr "معاينة" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/popup_header.html:14 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/welcomemsg.html:25 +#, python-format +msgid "%(counter)s result" +msgid_plural "%(counter)s results" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/popup_header.html:14 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/welcomemsg.html:25 +#, python-format +msgid "%(full_result_count)s total" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/search.html:10 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/search.html:40 +#, fuzzy +#| msgid "Search templates by name..." +msgid "Search apps and models..." +msgstr "البحث عن القوالب بالاسم..." + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/search.html:41 +#, fuzzy +#| msgid "Toggle navigation" +msgid "Filter navigation items" +msgstr "تبديل التنقل" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_branding.html:3 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/site_branding.html:6 +msgid "Django administration" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/tab_items.html:15 +msgid "General" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/theme_switch.html:16 +msgid "Dark" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/theme_switch.html:23 +msgid "Light" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/theme_switch.html:30 +msgid "System" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/helpers/unauthenticated_header.html:6 +msgid "Return to site" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/clearable_file_input.html:6 +#, fuzzy +#| msgid "Interview" +msgid "Image preview" +msgstr "المقابلة" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/clearable_file_input.html:24 +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/clearable_file_input_small.html:17 +msgid "Choose file to upload" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/related_widget_wrapper.html:16 +#, python-format +msgid "Change selected %(model)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/related_widget_wrapper.html:26 +#, python-format +msgid "Add another %(model)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/related_widget_wrapper.html:35 +#, python-format +msgid "View selected %(model)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold/widgets/related_widget_wrapper.html:45 +#, python-format +msgid "Delete selected %(model)s" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templates/unfold_crispy/layout/table_inline_formset.html:65 +msgid "Add row" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templatetags/unfold.py:770 +msgid "Welcome" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/templatetags/unfold_list.py:118 +msgid "Select all objects on this page for an action" +msgstr "" + +#: venv/lib/python3.13/site-packages/unfold/widgets.py:821 +#, fuzzy +#| msgid "Select country" +msgid "Select currency" +msgstr "اختر الدولة" + +#, python-format +#~ msgid "" +#~ "Cannot transition from \"%(current)s\" to \"%(new)s\". Allowed " +#~ "transitions: %(allowed)s" +#~ msgstr "" +#~ "لا يمكن الانتقال من \"%(current)s\" إلى \"%(new)s\". الانتقالات المسموح " +#~ "بها: %(allowed)s" + +#~ msgid "Enter title" +#~ msgstr "أدخل العنوان" + +#~ msgid "Save Material" +#~ msgstr "حفظ المادة" + +#~ msgid "Help & Support" +#~ msgstr "المساعدة والدعم" + +#~ msgid "Application URL" +#~ msgstr "رابط التقديم" + +#~ msgid "Full URL where candidates will apply" +#~ msgstr "الرابط الكامل حيث سيقدم المرشحون" + +#~ msgid "Desired Start Date" +#~ msgstr "تاريخ البدء المطلوب" + +#~ msgid "Post Reach Field" +#~ msgstr "حقل وصول المنشور" + +#~ msgid "Hashtags" +#~ msgstr "الوسوم" + +#~ msgid "Start Date:" +#~ msgstr "تاريخ البدء:" + +#~ msgid "Info" +#~ msgstr "معلومات" + +#~ msgid "View All Existing Forms" +#~ msgstr "عرض جميع النماذج الموجودة" + +#~ msgid "Reports To:" +#~ msgstr "يقدم تقاريره إلى:" + +#~ msgid "Host Video" +#~ msgstr "فيديو المضيف" + +#~ msgid "Are you sure?" +#~ msgstr "هل أنت متأكد؟" + +#~ msgid "Zoom API Response" +#~ msgstr "استجابة واجهة برمجة تطبيقات Zoom" + +#~ msgid "Start Time (ISO 8601):" +#~ msgstr "وقت البدء (ISO 8601):" + +#~ msgid "Duration (minutes):" +#~ msgstr "المدة (دقائق):" + +#~ msgid "Resume Document" +#~ msgstr "وثيقة السيرة الذاتية" + +#~ msgid "Parsed Data" +#~ msgstr "البيانات المحللة" + +#~ msgid "Activity" +#~ msgstr "النشاط" + +#~ msgid "Structured Resume Data" +#~ msgstr "بيانات السيرة الذاتية المنظمة" + +#~ msgid "" +#~ "Activity feed (e.g., stage changes, notes, interview history) will appear " +#~ "here." +#~ msgstr "" +#~ "سجل النشاط (مثلاً، تغييرات المرحلة، ملاحظات، سجل المقابلات) سيظهر هنا." diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 25de3e8..af604ce 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -1,1432 +1,1008 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-08 13:38+0300\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: recruitment/forms.py:14 recruitment/models.py:200 + msgid "First Name" msgstr "" -#: recruitment/forms.py:15 recruitment/models.py:201 msgid "Last Name" msgstr "" -#: recruitment/forms.py:16 recruitment/models.py:203 -#: templates/recruitment/candidate_list.html:152 msgid "Phone" msgstr "" -#: recruitment/forms.py:17 recruitment/models.py:202 -#: templates/recruitment/candidate_detail.html:242 -#: templates/recruitment/candidate_list.html:151 msgid "Email" msgstr "" -#: recruitment/forms.py:18 recruitment/models.py:204 -#: templates/recruitment/candidate_detail.html:218 msgid "Resume" msgstr "" -#: recruitment/forms.py:19 msgid "Application Stage" msgstr "" -#: recruitment/forms.py:22 msgid "Enter first name" msgstr "" -#: recruitment/forms.py:23 msgid "Enter last name" msgstr "" -#: recruitment/forms.py:24 msgid "Enter phone number" msgstr "" -#: recruitment/forms.py:25 msgid "Enter email" msgstr "" -#: recruitment/forms.py:50 msgid "Submit" msgstr "" -#: recruitment/forms.py:60 msgid "New Application Stage" msgstr "" -#: recruitment/forms.py:85 recruitment/models.py:180 recruitment/models.py:206 -#: templates/jobs/job_detail.html:420 -#: templates/recruitment/candidate_list.html:154 msgid "Applied" msgstr "" -#: recruitment/forms.py:92 msgid "Please select a stage." msgstr "" -#: recruitment/forms.py:101 -#, python-format -msgid "" -"Cannot transition from \"%(current)s\" to \"%(new)s\". Allowed transitions: " -"%(allowed)s" -msgstr "" - -#: recruitment/forms.py:116 recruitment/models.py:306 -#: templates/meetings/create_meeting.html:162 msgid "Topic" msgstr "" -#: recruitment/forms.py:117 recruitment/models.py:308 recruitment/models.py:727 -#: templates/meetings/create_meeting.html:166 -#: templates/meetings/list_meetings.html:260 -#: templates/meetings/meeting_details.html:29 msgid "Start Time" msgstr "" -#: recruitment/forms.py:118 recruitment/models.py:309 -#: templates/meetings/list_meetings.html:265 -#: templates/meetings/meeting_details.html:33 msgid "Duration" msgstr "" -#: recruitment/forms.py:121 msgid "Enter meeting topic" msgstr "" -#: recruitment/forms.py:123 msgid "60" msgstr "" -#: recruitment/forms.py:137 templates/meetings/create_meeting.html:180 -#: templates/meetings/list_meetings.html:242 msgid "Create Meeting" msgstr "" -#: recruitment/forms.py:147 recruitment/models.py:290 -#: templates/recruitment/training_list.html:150 -#: templates/recruitment/training_update.html:144 msgid "Title" msgstr "" -#: recruitment/forms.py:148 recruitment/models.py:291 -#: templates/recruitment/training_update.html:158 msgid "Content" msgstr "" -#: recruitment/forms.py:149 recruitment/models.py:292 -#: templates/recruitment/training_update.html:150 msgid "Video Link" msgstr "" -#: recruitment/forms.py:150 recruitment/models.py:293 -#: templates/recruitment/training_update.html:166 msgid "File" msgstr "" -#: recruitment/forms.py:153 msgid "Enter title" msgstr "" -#: recruitment/forms.py:154 msgid "Enter material content" msgstr "" -#: recruitment/forms.py:155 msgid "https://www.youtube.com/watch?v=..." msgstr "" -#: recruitment/forms.py:174 msgid "Save Material" msgstr "" -#: recruitment/forms.py:377 recruitment/models.py:199 -#: templates/recruitment/candidate_list.html:153 msgid "Job" msgstr "" -#: recruitment/forms.py:378 msgid "Template Name" msgstr "" -#: recruitment/forms.py:379 recruitment/models.py:542 msgid "Description" msgstr "" -#: recruitment/forms.py:380 recruitment/models.py:576 msgid "Active" msgstr "" -#: recruitment/forms.py:385 msgid "Enter template name" msgstr "" -#: recruitment/forms.py:391 msgid "Enter template description (optional)" msgstr "" -#: recruitment/forms.py:410 msgid "Create Template" msgstr "" -#: recruitment/models.py:13 msgid "Created at" msgstr "" -#: recruitment/models.py:14 msgid "Updated at" msgstr "" -#: recruitment/models.py:15 msgid "Slug" msgstr "" -#: recruitment/models.py:120 recruitment/models.py:714 msgid "Hiring Agency" msgstr "" -#: recruitment/models.py:121 msgid "External agency responsible for sourcing candidates for this role" msgstr "" -#: recruitment/models.py:181 msgid "Exam" msgstr "" -#: recruitment/models.py:182 templates/jobs/job_detail.html:426 msgid "Interview" msgstr "" -#: recruitment/models.py:183 templates/jobs/job_detail.html:432 msgid "Offer" msgstr "" -#: recruitment/models.py:185 msgid "Passed" msgstr "" -#: recruitment/models.py:186 msgid "Failed" msgstr "" -#: recruitment/models.py:188 msgid "Accepted" msgstr "" -#: recruitment/models.py:189 msgid "Rejected" msgstr "" -#: recruitment/models.py:205 msgid "Parsed Summary" msgstr "" -#: recruitment/models.py:207 msgid "Stage" msgstr "" -#: recruitment/models.py:209 msgid "Exam Date" msgstr "" -#: recruitment/models.py:210 msgid "Exam Status" msgstr "" -#: recruitment/models.py:211 recruitment/models.py:744 msgid "Interview Date" msgstr "" -#: recruitment/models.py:212 msgid "Interview Status" msgstr "" -#: recruitment/models.py:213 msgid "Offer Date" msgstr "" -#: recruitment/models.py:214 msgid "Offer Status" msgstr "" -#: recruitment/models.py:215 msgid "Join Date" msgstr "" -#: recruitment/models.py:230 msgid "Submitted by Agency" msgstr "" -#: recruitment/models.py:234 msgid "Candidate" msgstr "" -#: recruitment/models.py:235 templates/base.html:298 -#: templates/jobs/job_detail.html:414 msgid "Candidates" msgstr "" -#: recruitment/models.py:294 msgid "Created by" msgstr "" -#: recruitment/models.py:297 msgid "Training Material" msgstr "" -#: recruitment/models.py:298 templates/recruitment/training_list.html:4 -#: templates/recruitment/training_list.html:126 msgid "Training Materials" msgstr "" -#: recruitment/models.py:307 templates/meetings/meeting_details.html:25 msgid "Meeting ID" msgstr "" -#: recruitment/models.py:310 msgid "Timezone" msgstr "" -#: recruitment/models.py:311 templates/meetings/list_meetings.html:286 msgid "Join URL" msgstr "" -#: recruitment/models.py:312 templates/meetings/meeting_details.html:62 msgid "Participant Video" msgstr "" -#: recruitment/models.py:313 templates/meetings/meeting_details.html:66 msgid "Join Before Host" msgstr "" -#: recruitment/models.py:314 templates/meetings/meeting_details.html:70 msgid "Mute Upon Entry" msgstr "" -#: recruitment/models.py:315 templates/meetings/meeting_details.html:74 msgid "Waiting Room" msgstr "" -#: recruitment/models.py:317 msgid "Zoom Gateway Response" msgstr "" -#: recruitment/models.py:532 msgid "Source Name" msgstr "" -#: recruitment/models.py:533 recruitment/models.py:538 msgid "e.g., ATS, ERP " msgstr "" -#: recruitment/models.py:537 msgid "Source Type" msgstr "" -#: recruitment/models.py:543 msgid "A description of the source" msgstr "" -#: recruitment/models.py:548 recruitment/models.py:667 msgid "IP Address" msgstr "" -#: recruitment/models.py:549 msgid "The IP address of the source" msgstr "" -#: recruitment/models.py:558 msgid "API Key" msgstr "" -#: recruitment/models.py:559 msgid "API key for authentication (will be encrypted)" msgstr "" -#: recruitment/models.py:565 msgid "API Secret" msgstr "" -#: recruitment/models.py:566 msgid "API secret for authentication (will be encrypted)" msgstr "" -#: recruitment/models.py:571 msgid "Trusted IP Addresses" msgstr "" -#: recruitment/models.py:572 msgid "Comma-separated list of trusted IP addresses" msgstr "" -#: recruitment/models.py:577 msgid "Whether this source is active for integration" msgstr "" -#: recruitment/models.py:582 msgid "Integration Version" msgstr "" -#: recruitment/models.py:583 msgid "Version of the integration protocol" msgstr "" -#: recruitment/models.py:588 msgid "Last Sync At" msgstr "" -#: recruitment/models.py:589 msgid "Timestamp of the last successful synchronization" msgstr "" -#: recruitment/models.py:601 msgid "Sync Status" msgstr "" -#: recruitment/models.py:610 recruitment/models.py:630 msgid "Source" msgstr "" -#: recruitment/models.py:611 msgid "Sources" msgstr "" -#: recruitment/models.py:619 msgid "Request" msgstr "" -#: recruitment/models.py:620 msgid "Response" msgstr "" -#: recruitment/models.py:621 msgid "Error" msgstr "" -#: recruitment/models.py:622 msgid "Sync" msgstr "" -#: recruitment/models.py:623 templates/jobs/create_job.html:392 msgid "Create Job" msgstr "" -#: recruitment/models.py:624 msgid "Update Job" msgstr "" -#: recruitment/models.py:635 msgid "Action" msgstr "" -#: recruitment/models.py:640 msgid "Endpoint" msgstr "" -#: recruitment/models.py:645 msgid "HTTP Method" msgstr "" -#: recruitment/models.py:650 msgid "Request Data" msgstr "" -#: recruitment/models.py:655 msgid "Response Data" msgstr "" -#: recruitment/models.py:660 msgid "Status Code" msgstr "" -#: recruitment/models.py:664 msgid "Error Message" msgstr "" -#: recruitment/models.py:672 msgid "User Agent" msgstr "" -#: recruitment/models.py:677 msgid "Processing Time (seconds)" msgstr "" -#: recruitment/models.py:685 msgid "Integration Log" msgstr "" -#: recruitment/models.py:686 msgid "Integration Logs" msgstr "" -#: recruitment/models.py:701 msgid "Agency Name" msgstr "" -#: recruitment/models.py:702 msgid "Contact Person" msgstr "" -#: recruitment/models.py:706 msgid "Internal notes about the agency" msgstr "" -#: recruitment/models.py:707 msgid "Select country" msgstr "" -#: recruitment/models.py:715 msgid "Hiring Agencies" msgstr "" -#: recruitment/models.py:724 msgid "Start Date" msgstr "" -#: recruitment/models.py:725 msgid "End Date" msgstr "" -#: recruitment/models.py:726 msgid "Working Days" msgstr "" -#: recruitment/models.py:728 msgid "End Time" msgstr "" -#: recruitment/models.py:729 msgid "Break Start Time" msgstr "" -#: recruitment/models.py:730 msgid "Break End Time" msgstr "" -#: recruitment/models.py:731 msgid "Interview Duration (minutes)" msgstr "" -#: recruitment/models.py:732 msgid "Buffer Time (minutes)" msgstr "" -#: recruitment/models.py:745 msgid "Interview Time" msgstr "" -#: recruitment/models.py:749 msgid "Scheduled" msgstr "" -#: recruitment/models.py:750 msgid "Confirmed" msgstr "" -#: recruitment/models.py:751 msgid "Cancelled" msgstr "" -#: recruitment/models.py:752 msgid "Completed" msgstr "" -#: templates/base.html:7 msgid "King Abdullah Academic University Hospital - Applicant Tracking System" msgstr "" -#: templates/base.html:8 msgid "University ATS" msgstr "" -#: templates/base.html:258 msgid "Saudi Vision 2030" msgstr "" -#: templates/base.html:259 msgid "King Abdullah Academic University Hospital" msgstr "" -#: templates/base.html:272 msgid "Toggle navigation" msgstr "" -#: templates/base.html:282 msgid "Dashboard" msgstr "" -#: templates/base.html:290 msgid "Jobs" msgstr "" -#: templates/base.html:309 msgid "Training" msgstr "" -#: templates/base.html:320 templates/base.html:340 msgid "Meetings" msgstr "" -#: templates/base.html:330 templates/forms/form_templates_list.html:190 msgid "Form Templates" msgstr "" -#: templates/base.html:337 msgid "More" msgstr "" -#: templates/base.html:341 msgid "Schedule" msgstr "" -#: templates/base.html:343 msgid "Active Jobs" msgstr "" -#: templates/base.html:344 msgid "Draft Jobs" msgstr "" -#: templates/base.html:346 msgid "All Candidates" msgstr "" -#: templates/base.html:347 msgid "New Candidates" msgstr "" -#: templates/base.html:356 msgid "Toggle language menu" msgstr "" -#: templates/base.html:392 msgid "Toggle user menu" msgstr "" -#: templates/base.html:396 msgid "Your account" msgstr "" -#: templates/base.html:419 msgid "My Profile" msgstr "" -#: templates/base.html:420 templates/meetings/meeting_details.html:56 msgid "Settings" msgstr "" -#: templates/base.html:421 msgid "Activity Log" msgstr "" -#: templates/base.html:422 msgid "Help & Support" msgstr "" -#: templates/base.html:428 msgid "Connect LinkedIn" msgstr "" -#: templates/base.html:435 msgid "LinkedIn Connected" msgstr "" -#: templates/base.html:447 msgid "Sign out" msgstr "" -#: templates/base.html:450 msgid "Sign Out" msgstr "" -#: templates/base.html:466 msgid "Close" msgstr "" -#: templates/base.html:478 msgid "King Abdullah Academic University Hospital (KAAUH)." msgstr "" -#: templates/base.html:479 msgid "All rights reserved." msgstr "" -#: templates/base.html:511 msgid "Are you sure you want to sign out?" msgstr "" -#: templates/forms/form_templates_list.html:208 msgid "Search templates by name..." msgstr "" -#: templates/forms/form_templates_list.html:243 msgid "ago" msgstr "" -#: templates/forms/form_templates_list.html:253 msgid "Stages" msgstr "" -#: templates/forms/form_templates_list.html:257 msgid "Fields" msgstr "" -#: templates/forms/form_templates_list.html:264 msgid "No description provided" msgstr "" -#: templates/forms/form_templates_list.html:274 msgid "Preview" msgstr "" -#: templates/forms/form_templates_list.html:277 -#: templates/recruitment/candidate_list.html:178 -#: templates/recruitment/training_list.html:168 msgid "Edit" msgstr "" -#: templates/forms/form_templates_list.html:282 -#: templates/includes/delete_modal.html:26 -#: templates/meetings/list_meetings.html:307 -#: templates/recruitment/candidate_list.html:181 -#: templates/recruitment/training_list.html:171 -#: templates/recruitment/training_update.html:184 msgid "Delete" msgstr "" -#: templates/forms/form_templates_list.html:326 msgid "No Form Templates Found" msgstr "" -#: templates/forms/form_templates_list.html:329 -#, python-format msgid "No templates match your search \"%(query)s\"." msgstr "" -#: templates/forms/form_templates_list.html:331 msgid "You haven't created any form templates yet." msgstr "" -#: templates/forms/form_templates_list.html:335 msgid "Create Your First Template" msgstr "" -#: templates/forms/form_wizard.html:7 templates/forms/form_wizard.html:470 -#: templates/jobs/job_detail_candidate.html:7 msgid "Application Form" msgstr "" -#: templates/forms/form_wizard.html:439 -#: templates/jobs/job_detail_candidate.html:121 msgid "KAAUH IMAGE" msgstr "" -#: templates/forms/form_wizard.html:449 -#: templates/jobs/job_detail_candidate.html:132 msgid "Applications" msgstr "" -#: templates/forms/form_wizard.html:452 -#: templates/jobs/job_detail_candidate.html:135 msgid "Profile" msgstr "" -#: templates/forms/form_wizard.html:480 msgid "Review Your Application" msgstr "" -#: templates/forms/form_wizard.html:487 msgid "Back" msgstr "" -#: templates/forms/form_wizard.html:491 msgid "Next" msgstr "" -#: templates/forms/form_wizard.html:495 msgid "Submit Application" msgstr "" -#: templates/includes/delete_modal.html:11 msgid "Confirm Delete" msgstr "" -#: templates/includes/delete_modal.html:16 msgid "Are you sure you want to delete this item?" msgstr "" -#: templates/includes/delete_modal.html:19 templates/jobs/create_job.html:389 -#: templates/meetings/create_meeting.html:184 -#: templates/meetings/update_meeting.html:222 msgid "Cancel" msgstr "" -#: templates/includes/search_form.html:11 msgid "Search..." msgstr "" -#: templates/includes/search_form.html:13 msgid "Search" msgstr "" -#: templates/jobs/create_job.html:113 msgid "Create New Job Posting" msgstr "" -#: templates/jobs/create_job.html:121 msgid "Basic Information" msgstr "" -#: templates/jobs/create_job.html:127 msgid "Job Title" msgstr "" -#: templates/jobs/create_job.html:137 msgid "Job Type" msgstr "" -#: templates/jobs/create_job.html:148 msgid "Department" msgstr "" -#: templates/jobs/create_job.html:158 msgid "Position Number" msgstr "" -#: templates/jobs/create_job.html:169 msgid "Workplace Type" msgstr "" -#: templates/jobs/create_job.html:179 -#: templates/recruitment/training_list.html:151 msgid "Created By" msgstr "" -#: templates/jobs/create_job.html:193 msgid "Location" msgstr "" -#: templates/jobs/create_job.html:199 msgid "City" msgstr "" -#: templates/jobs/create_job.html:209 msgid "State/Province" msgstr "" -#: templates/jobs/create_job.html:219 msgid "Country" msgstr "" -#: templates/jobs/create_job.html:233 msgid "Job Details" msgstr "" -#: templates/jobs/create_job.html:239 templates/jobs/job_detail.html:341 msgid "Job Description" msgstr "" -#: templates/jobs/create_job.html:250 msgid "Qualifications and Requirements" msgstr "" -#: templates/jobs/create_job.html:261 msgid "Salary Range" msgstr "" -#: templates/jobs/create_job.html:271 templates/jobs/job_detail.html:353 msgid "Benefits" msgstr "" -#: templates/jobs/create_job.html:285 msgid "Application Information" msgstr "" -#: templates/jobs/create_job.html:291 msgid "Application URL" msgstr "" -#: templates/jobs/create_job.html:297 msgid "Full URL where candidates will apply" msgstr "" -#: templates/jobs/create_job.html:303 msgid "Application Deadline" msgstr "" -#: templates/jobs/create_job.html:313 msgid "Desired Start Date" msgstr "" -#: templates/jobs/create_job.html:324 templates/jobs/job_detail.html:363 msgid "Application Instructions" msgstr "" -#: templates/jobs/create_job.html:338 msgid "Post Reach Field" msgstr "" -#: templates/jobs/create_job.html:344 msgid "Hashtags" msgstr "" -#: templates/jobs/create_job.html:358 templates/jobs/job_detail.html:545 msgid "Internal Information" msgstr "" -#: templates/jobs/create_job.html:364 msgid "Reports To" msgstr "" -#: templates/jobs/create_job.html:374 msgid "Open Positions" msgstr "" -#: templates/jobs/job_detail.html:262 -#: templates/recruitment/candidate_detail.html:236 msgid "Core Details" msgstr "" -#: templates/jobs/job_detail.html:267 msgid "Description & Requirements" msgstr "" -#: templates/jobs/job_detail.html:273 msgid "Application" msgstr "" -#: templates/jobs/job_detail.html:284 msgid "Administrative & Location" msgstr "" -#: templates/jobs/job_detail.html:287 msgid "Department:" msgstr "" -#: templates/jobs/job_detail.html:290 msgid "Position No:" msgstr "" -#: templates/jobs/job_detail.html:293 msgid "Job Type:" msgstr "" -#: templates/jobs/job_detail.html:296 msgid "Workplace:" msgstr "" -#: templates/jobs/job_detail.html:299 msgid "Location:" msgstr "" -#: templates/jobs/job_detail.html:302 msgid "Created By:" msgstr "" -#: templates/jobs/job_detail.html:305 msgid "Financial & Timeline" msgstr "" -#: templates/jobs/job_detail.html:311 msgid "Salary:" msgstr "" -#: templates/jobs/job_detail.html:319 msgid "Start Date:" msgstr "" -#: templates/jobs/job_detail.html:327 msgid "Deadline:" msgstr "" -#: templates/jobs/job_detail.html:329 msgid "EXPIRED" msgstr "" -#: templates/jobs/job_detail.html:347 msgid "Required Qualifications" msgstr "" -#: templates/jobs/job_detail.html:375 msgid "Edit Job" msgstr "" -#: templates/jobs/job_detail.html:380 msgid "Upload Image for Post" msgstr "" -#: templates/jobs/job_detail.html:395 msgid "Applicants" msgstr "" -#: templates/jobs/job_detail.html:400 msgid "Manage" msgstr "" -#: templates/jobs/job_detail.html:405 msgid "Info" msgstr "" -#: templates/jobs/job_detail.html:464 msgid "View All Applicants" msgstr "" -#: templates/jobs/job_detail.html:471 msgid "No applicants yet" msgstr "" -#: templates/jobs/job_detail.html:472 msgid "Candidates will appear here once they apply for this position." msgstr "" -#: templates/jobs/job_detail.html:481 msgid "LinkedIn Integration" msgstr "" -#: templates/jobs/job_detail.html:485 msgid "Posted successfully!" msgstr "" -#: templates/jobs/job_detail.html:489 msgid "View on LinkedIn" msgstr "" -#: templates/jobs/job_detail.html:493 msgid "Posted on:" msgstr "" -#: templates/jobs/job_detail.html:496 msgid "This job has not been posted to LinkedIn yet." msgstr "" -#: templates/jobs/job_detail.html:504 msgid "Re-post to LinkedIn" msgstr "" -#: templates/jobs/job_detail.html:504 msgid "Post to LinkedIn" msgstr "" -#: templates/jobs/job_detail.html:510 msgid "You need to" msgstr "" -#: templates/jobs/job_detail.html:510 msgid "authenticate with LinkedIn" msgstr "" -#: templates/jobs/job_detail.html:510 msgid "first." msgstr "" -#: templates/jobs/job_detail.html:517 msgid "Error:" msgstr "" -#: templates/jobs/job_detail.html:523 msgid "Form Management" msgstr "" -#: templates/jobs/job_detail.html:526 msgid "Manage the custom application forms associated with this job posting." msgstr "" -#: templates/jobs/job_detail.html:530 msgid "Create New Form" msgstr "" -#: templates/jobs/job_detail.html:534 msgid "View All Existing Forms" msgstr "" -#: templates/jobs/job_detail.html:538 -#: templates/recruitment/candidate_create.html:131 msgid "Create Candidate" msgstr "" -#: templates/jobs/job_detail.html:547 msgid "Internal Job ID:" msgstr "" -#: templates/jobs/job_detail.html:548 msgid "Created:" msgstr "" -#: templates/jobs/job_detail.html:549 msgid "Last Updated:" msgstr "" -#: templates/jobs/job_detail.html:551 msgid "Reports To:" msgstr "" -#: templates/jobs/job_detail.html:557 msgid "Back to Jobs" msgstr "" -#: templates/jobs/job_detail_candidate.html:144 msgid "Job Overview" msgstr "" -#: templates/meetings/create_meeting.html:4 msgid "Create Zoom Meeting" msgstr "" -#: templates/meetings/create_meeting.html:151 msgid "Create New Zoom Meeting" msgstr "" -#: templates/meetings/create_meeting.html:155 -#: templates/meetings/meeting_details.html:19 msgid "Back to Meetings" msgstr "" -#: templates/meetings/create_meeting.html:170 msgid "Duration (minutes)" msgstr "" -#: templates/meetings/list_meetings.html:4 -#: templates/meetings/list_meetings.html:233 msgid "Zoom Meetings" msgstr "" -#: templates/meetings/list_meetings.html:255 msgid "ID" msgstr "" -#: templates/meetings/list_meetings.html:270 msgid "Status" msgstr "" -#: templates/meetings/list_meetings.html:274 msgid "Waiting" msgstr "" -#: templates/meetings/list_meetings.html:276 msgid "Started" msgstr "" -#: templates/meetings/list_meetings.html:278 msgid "Ended" msgstr "" -#: templates/meetings/list_meetings.html:289 -#: templates/meetings/meeting_details.html:45 msgid "Join Meeting" msgstr "" -#: templates/meetings/list_meetings.html:296 -#: templates/recruitment/candidate_list.html:174 -#: templates/recruitment/candidate_update.html:104 -#: templates/recruitment/training_list.html:164 -#: templates/recruitment/training_update.html:119 msgid "View" msgstr "" -#: templates/meetings/list_meetings.html:302 msgid "Update" msgstr "" -#: templates/meetings/list_meetings.html:356 msgid "No meetings found." msgstr "" -#: templates/meetings/list_meetings.html:362 msgid "Create Your First Meeting" msgstr "" -#: templates/meetings/meeting_details.html:4 msgid "Meeting Details" msgstr "" -#: templates/meetings/meeting_details.html:23 -#: templates/meetings/update_meeting.html:191 msgid "Meeting Information" msgstr "" -#: templates/meetings/meeting_details.html:37 msgid "Host Email" msgstr "" -#: templates/meetings/meeting_details.html:44 msgid "Join Information" msgstr "" -#: templates/meetings/meeting_details.html:48 msgid "Password" msgstr "" -#: templates/meetings/meeting_details.html:58 msgid "Host Video" msgstr "" -#: templates/meetings/meeting_details.html:81 msgid "View API Response" msgstr "" -#: templates/meetings/meeting_details.html:83 -#: templates/meetings/update_meeting.html:218 msgid "Update Meeting" msgstr "" -#: templates/meetings/meeting_details.html:86 msgid "Are you sure?" msgstr "" -#: templates/meetings/meeting_details.html:86 msgid "Delete Meeting" msgstr "" -#: templates/meetings/meeting_details.html:93 msgid "Zoom API Response" msgstr "" -#: templates/meetings/update_meeting.html:3 -#: templates/meetings/update_meeting.html:174 msgid "Update Zoom Meeting" msgstr "" -#: templates/meetings/update_meeting.html:175 msgid "Modify the details of your scheduled meeting" msgstr "" -#: templates/meetings/update_meeting.html:197 msgid "Topic:" msgstr "" -#: templates/meetings/update_meeting.html:202 msgid "Start Time (ISO 8601):" msgstr "" -#: templates/meetings/update_meeting.html:209 msgid "Duration (minutes):" msgstr "" -#: templates/recruitment/candidate_create.html:94 msgid "Create New Candidate" msgstr "" -#: templates/recruitment/candidate_create.html:96 msgid "Enter details to create a new candidate record." msgstr "" -#: templates/recruitment/candidate_create.html:99 -#: templates/recruitment/candidate_create.html:101 -#: templates/recruitment/candidate_detail.html:326 -#: templates/recruitment/candidate_update.html:97 -#: templates/recruitment/candidate_update.html:99 -#: templates/recruitment/training_create.html:124 -#: templates/recruitment/training_create.html:126 -#: templates/recruitment/training_update.html:112 -#: templates/recruitment/training_update.html:114 msgid "Back to List" msgstr "" -#: templates/recruitment/candidate_create.html:112 msgid "Candidate Information" msgstr "" -#: templates/recruitment/candidate_detail.html:191 msgid "Stage:" msgstr "" -#: templates/recruitment/candidate_detail.html:196 msgid "Applied for:" msgstr "" -#: templates/recruitment/candidate_detail.html:202 msgid "Change Stage" msgstr "" -#: templates/recruitment/candidate_detail.html:212 msgid "Contact & Job" msgstr "" -#: templates/recruitment/candidate_detail.html:225 msgid "Summary" msgstr "" -#: templates/recruitment/candidate_detail.html:251 msgid "Position Applied" msgstr "" -#: templates/recruitment/candidate_detail.html:260 msgid "Applied Date" msgstr "" -#: templates/recruitment/candidate_detail.html:277 msgid "Resume Document" msgstr "" -#: templates/recruitment/candidate_detail.html:285 msgid "Download Resume" msgstr "" -#: templates/recruitment/candidate_detail.html:294 msgid "AI Generated Summary" msgstr "" -#: templates/recruitment/candidate_detail.html:312 msgid "Management Actions" msgstr "" -#: templates/recruitment/candidate_detail.html:320 msgid "Edit Details" msgstr "" -#: templates/recruitment/candidate_detail.html:322 msgid "Are you sure you want to delete this candidate?" msgstr "" -#: templates/recruitment/candidate_detail.html:323 msgid "Delete Candidate" msgstr "" -#: templates/recruitment/candidate_detail.html:338 msgid "Parsed Data" msgstr "" -#: templates/recruitment/candidate_detail.html:343 msgid "Activity" msgstr "" -#: templates/recruitment/candidate_detail.html:352 msgid "Structured Resume Data" msgstr "" -#: templates/recruitment/candidate_detail.html:372 -msgid "" -"Activity feed (e.g., stage changes, notes, interview history) will appear " -"here." -msgstr "" - -#: templates/recruitment/candidate_list.html:129 msgid "Candidate Profiles" msgstr "" -#: templates/recruitment/candidate_list.html:138 msgid "Add New Candidate" msgstr "" -#: templates/recruitment/candidate_list.html:150 msgid "Name" msgstr "" -#: templates/recruitment/candidate_list.html:155 -#: templates/recruitment/training_list.html:152 msgid "Created" msgstr "" -#: templates/recruitment/candidate_list.html:156 -#: templates/recruitment/training_list.html:153 msgid "Actions" msgstr "" -#: templates/recruitment/candidate_list.html:233 msgid "No candidates found." msgstr "" -#: templates/recruitment/candidate_list.html:234 msgid "Start by adding a new profile or adjusting your search filters." msgstr "" -#: templates/recruitment/candidate_list.html:238 msgid "Add Your First Candidate" msgstr "" -#: templates/recruitment/candidate_update.html:92 msgid "Update Candidate:" msgstr "" -#: templates/recruitment/candidate_update.html:94 msgid "Edit candidate information and details" msgstr "" -#: templates/recruitment/candidate_update.html:102 msgid "View Candidate" msgstr "" -#: templates/recruitment/candidate_update.html:116 msgid "Candidate Form" msgstr "" -#: templates/recruitment/candidate_update.html:135 msgid "Update Candidate" msgstr "" -#: templates/recruitment/training_create.html:119 msgid "Create New Training Material" msgstr "" -#: templates/recruitment/training_create.html:121 msgid "Upload a new document or guide for your team." msgstr "" -#: templates/recruitment/training_create.html:137 -#: templates/recruitment/training_update.html:131 msgid "Material Details" msgstr "" -#: templates/recruitment/training_create.html:158 msgid "Create Material" msgstr "" -#: templates/recruitment/training_list.html:135 -#: templates/recruitment/training_list.html:137 msgid "Add New Material" msgstr "" -#: templates/recruitment/training_list.html:223 msgid "No training materials found." msgstr "" -#: templates/recruitment/training_list.html:224 msgid "It looks like there are no materials yet. Start by adding one!" msgstr "" -#: templates/recruitment/training_list.html:228 msgid "Create Your First Material" msgstr "" -#: templates/recruitment/training_update.html:107 msgid "Update Training Material:" msgstr "" -#: templates/recruitment/training_update.html:109 msgid "Edit the details of this training document or guide." msgstr "" -#: templates/recruitment/training_update.html:117 msgid "View Material" msgstr "" -#: templates/recruitment/training_update.html:180 msgid "Update Material" msgstr "" -#: templates/recruitment/training_update.html:182 msgid "Are you sure you want to delete this material?" msgstr "" -#: templates/unfold/components/table.html:43 msgid "No data" msgstr "" -# Source Management msgid "Data Sources" msgstr "" @@ -1448,21 +1024,12 @@ msgstr "" msgid "View Details" msgstr "" -msgid "Edit" -msgstr "" - -msgid "Delete" -msgstr "" - msgid "First" msgstr "" msgid "Previous" msgstr "" -msgid "Next" -msgstr "" - msgid "Last" msgstr "" @@ -1475,9 +1042,6 @@ msgstr "" msgid "Network Configuration" msgstr "" -msgid "Settings" -msgstr "" - msgid "API Configuration" msgstr "" @@ -1487,63 +1051,33 @@ msgstr "" msgid "Generate secure API keys for external integrations" msgstr "" -msgid "Active" -msgstr "" - msgid "Inactive" msgstr "" msgid "Not generated" msgstr "" -msgid "Created By" -msgstr "" - msgid "Created At" msgstr "" msgid "Updated At" msgstr "" -msgid "IP Address" -msgstr "" - msgid "Trusted IPs" msgstr "" -msgid "Integration Version" -msgstr "" - -msgid "API Key" -msgstr "" - -msgid "API Secret" -msgstr "" - msgid "Recent Integration Logs" msgstr "" msgid "Time" msgstr "" -msgid "Action" -msgstr "" - -msgid "Endpoint" -msgstr "" - msgid "Method" msgstr "" -msgid "Status" -msgstr "" - msgid "Success" msgstr "" -msgid "Failed" -msgstr "" - msgid "No integration logs found" msgstr "" @@ -1571,12 +1105,6 @@ msgstr "" msgid "Any active integrations using this source will be disconnected." msgstr "" -msgid "Back to List" -msgstr "" - -msgid "Delete Source" -msgstr "" - msgid "Generate API Keys" msgstr "" @@ -1599,4 +1127,4 @@ msgid "Source created successfully." msgstr "" msgid "Source deleted successfully." -msgstr "" +msgstr "" \ No newline at end of file diff --git a/recruitment/__pycache__/urls.cpython-313.pyc b/recruitment/__pycache__/urls.cpython-313.pyc deleted file mode 100644 index 6968d48..0000000 Binary files a/recruitment/__pycache__/urls.cpython-313.pyc and /dev/null differ diff --git a/recruitment/admin.py b/recruitment/admin.py index cd2c8a1..245a14d 100644 --- a/recruitment/admin.py +++ b/recruitment/admin.py @@ -3,12 +3,14 @@ from django.utils.html import format_html from django.urls import reverse from django.utils import timezone from .models import ( - JobPosting, Candidate, TrainingMaterial, ZoomMeeting, + JobPosting, Application, TrainingMaterial, ZoomMeeting, FormTemplate, FormStage, FormField, FormSubmission, FieldResponse, SharedFormTemplate, Source, HiringAgency, IntegrationLog,InterviewSchedule,Profile,JobPostingImage,MeetingComment, AgencyAccessLink, AgencyJobAssignment ) +from django.contrib.auth import get_user_model +User = get_user_model() class FormFieldInline(admin.TabularInline): model = FormField extra = 1 @@ -82,17 +84,10 @@ class HiringAgencyAdmin(admin.ModelAdmin): readonly_fields = ['slug', 'created_at', 'updated_at'] fieldsets = ( ('Basic Information', { - 'fields': ('name', 'slug', 'contact_person', 'email', 'phone', 'website') - }), - ('Location Details', { - 'fields': ('country', 'city', 'address') - }), - ('Additional Information', { - 'fields': ('description', 'created_at', 'updated_at') + 'fields': ('name','contact_person', 'email', 'phone', 'website','user') }), ) save_on_top = True - prepopulated_fields = {'slug': ('name',)} @admin.register(JobPosting) @@ -143,43 +138,6 @@ class JobPostingAdmin(admin.ModelAdmin): mark_as_closed.short_description = 'Mark selected jobs as closed' -@admin.register(Candidate) -class CandidateAdmin(admin.ModelAdmin): - list_display = ['full_name', 'job', 'email', 'phone', 'stage', 'applied','is_resume_parsed', 'created_at'] - list_filter = ['stage', 'applied', 'created_at', 'job__department'] - search_fields = ['first_name', 'last_name', 'email', 'phone'] - readonly_fields = ['slug', 'created_at', 'updated_at'] - fieldsets = ( - ('Personal Information', { - 'fields': ('first_name', 'last_name', 'email', 'phone', 'resume') - }), - ('Application Details', { - 'fields': ('job', 'applied', 'stage','is_resume_parsed') - }), - ('Interview Process', { - 'fields': ('exam_date', 'exam_status', 'interview_date', 'interview_status', 'offer_date', 'offer_status', 'join_date') - }), - ('Scoring', { - 'fields': ('ai_analysis_data',) - }), - ('Additional Information', { - 'fields': ('submitted_by_agency', 'created_at', 'updated_at') - }), - ) - save_on_top = True - actions = ['mark_as_applied', 'mark_as_not_applied'] - - def mark_as_applied(self, request, queryset): - updated = queryset.update(applied=True) - self.message_user(request, f'{updated} candidates marked as applied.') - mark_as_applied.short_description = 'Mark selected candidates as applied' - - def mark_as_not_applied(self, request, queryset): - updated = queryset.update(applied=False) - self.message_user(request, f'{updated} candidates marked as not applied.') - mark_as_not_applied.short_description = 'Mark selected candidates as not applied' - - @admin.register(TrainingMaterial) class TrainingMaterialAdmin(admin.ModelAdmin): list_display = ['title', 'created_by', 'created_at'] @@ -280,6 +238,7 @@ class FormSubmissionAdmin(admin.ModelAdmin): # Register other models admin.site.register(FormStage) +admin.site.register(Application) admin.site.register(FormField) admin.site.register(FieldResponse) admin.site.register(InterviewSchedule) @@ -290,3 +249,4 @@ admin.site.register(AgencyJobAssignment) admin.site.register(JobPostingImage) +admin.site.register(User) diff --git a/recruitment/decorators.py b/recruitment/decorators.py index b929bf2..06e68b1 100644 --- a/recruitment/decorators.py +++ b/recruitment/decorators.py @@ -1,17 +1,163 @@ from functools import wraps from datetime import date from django.shortcuts import redirect, get_object_or_404 -from django.http import HttpResponseNotFound +from django.http import HttpResponseNotFound, HttpResponseForbidden +from django.contrib.auth.decorators import login_required +from django.contrib.auth.mixins import AccessMixin +from django.core.exceptions import PermissionDenied +from django.contrib import messages def job_not_expired(view_func): @wraps(view_func) def _wrapped_view(request, job_id, *args, **kwargs): - + from .models import JobPosting job = get_object_or_404(JobPosting, pk=job_id) if job.expiration_date and job.application_deadline< date.today(): return redirect('expired_job_page') - + return view_func(request, job_id, *args, **kwargs) - return _wrapped_view \ No newline at end of file + return _wrapped_view + + +def user_type_required(allowed_types=None, login_url=None): + """ + Decorator to restrict view access based on user type. + + Args: + allowed_types (list): List of allowed user types ['staff', 'agency', 'candidate'] + login_url (str): URL to redirect to if user is not authenticated + """ + if allowed_types is None: + allowed_types = ['staff'] + + def decorator(view_func): + @wraps(view_func) + @login_required(login_url=login_url) + def _wrapped_view(request, *args, **kwargs): + user = request.user + + # Check if user has user_type attribute + if not hasattr(user, 'user_type') or not user.user_type: + messages.error(request, "User type not specified. Please contact administrator.") + return redirect('portal_login') + + # Check if user type is allowed + if user.user_type not in allowed_types: + # Log unauthorized access attempt + messages.error( + request, + f"Access denied. This page is restricted to {', '.join(allowed_types)} users." + ) + + # Redirect based on user type + if user.user_type == 'agency': + return redirect('agency_portal_dashboard') + elif user.user_type == 'candidate': + return redirect('candidate_portal_dashboard') + else: + return redirect('dashboard') + + return view_func(request, *args, **kwargs) + return _wrapped_view + return decorator + + +class UserTypeRequiredMixin(AccessMixin): + """ + Mixin for class-based views to restrict access based on user type. + """ + allowed_user_types = ['staff'] # Default to staff only + login_url = '/login/' + + def dispatch(self, request, *args, **kwargs): + if not request.user.is_authenticated: + return self.handle_no_permission() + + # Check if user has user_type attribute + if not hasattr(request.user, 'user_type') or not request.user.user_type: + messages.error(request, "User type not specified. Please contact administrator.") + return redirect('portal_login') + + # Check if user type is allowed + if request.user.user_type not in self.allowed_user_types: + # Log unauthorized access attempt + messages.error( + request, + f"Access denied. This page is restricted to {', '.join(self.allowed_user_types)} users." + ) + + # Redirect based on user type + if request.user.user_type == 'agency': + return redirect('agency_portal_dashboard') + elif request.user.user_type == 'candidate': + return redirect('candidate_portal_dashboard') + else: + return redirect('dashboard') + + return super().dispatch(request, *args, **kwargs) + + def handle_no_permission(self): + if self.request.user.is_authenticated: + # User is authenticated but doesn't have permission + messages.error( + self.request, + f"Access denied. This page is restricted to {', '.join(self.allowed_user_types)} users." + ) + return redirect('dashboard') + else: + # User is not authenticated + return super().handle_no_permission() + + +class StaffRequiredMixin(UserTypeRequiredMixin): + """Mixin to restrict access to staff users only.""" + allowed_user_types = ['staff'] + + +class AgencyRequiredMixin(UserTypeRequiredMixin): + """Mixin to restrict access to agency users only.""" + allowed_user_types = ['agency'] + login_url = '/portal/login/' + + +class CandidateRequiredMixin(UserTypeRequiredMixin): + """Mixin to restrict access to candidate users only.""" + allowed_user_types = ['candidate'] + login_url = '/portal/login/' + + +class StaffOrAgencyRequiredMixin(UserTypeRequiredMixin): + """Mixin to restrict access to staff and agency users.""" + allowed_user_types = ['staff', 'agency'] + + +class StaffOrCandidateRequiredMixin(UserTypeRequiredMixin): + """Mixin to restrict access to staff and candidate users.""" + allowed_user_types = ['staff', 'candidate'] + + +def agency_user_required(view_func): + """Decorator to restrict view to agency users only.""" + return user_type_required(['agency'], login_url='/portal/login/')(view_func) + + +def candidate_user_required(view_func): + """Decorator to restrict view to candidate users only.""" + return user_type_required(['candidate'], login_url='/portal/login/')(view_func) + + +def staff_user_required(view_func): + """Decorator to restrict view to staff users only.""" + return user_type_required(['staff'])(view_func) + + +def staff_or_agency_required(view_func): + """Decorator to restrict view to staff and agency users.""" + return user_type_required(['staff', 'agency'], login_url='/portal/login/')(view_func) + + +def staff_or_candidate_required(view_func): + """Decorator to restrict view to staff and candidate users.""" + return user_type_required(['staff', 'candidate'], login_url='/portal/login/')(view_func) diff --git a/recruitment/forms.py b/recruitment/forms.py index 76bbfc9..9e23101 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -4,15 +4,32 @@ from django.forms.formsets import formset_factory from django.utils.translation import gettext_lazy as _ from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Submit, Row, Column, Field, Div -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model from django.contrib.auth.forms import UserCreationForm + +User = get_user_model() import re from .models import ( - ZoomMeeting, Candidate,TrainingMaterial,JobPosting, - FormTemplate,InterviewSchedule,BreakTime,JobPostingImage, - Profile,MeetingComment,ScheduledInterview,Source,HiringAgency, - AgencyJobAssignment, AgencyAccessLink,Participants,OnsiteMeeting + ZoomMeeting, + Application, + TrainingMaterial, + JobPosting, + FormTemplate, + InterviewSchedule, + BreakTime, + JobPostingImage, + Profile, + MeetingComment, + ScheduledInterview, + Source, + HiringAgency, + AgencyJobAssignment, + AgencyAccessLink, + Participants, + Message, + Person,OnsiteMeeting ) + # from django_summernote.widgets import SummernoteWidget from django_ckeditor_5.widgets import CKEditor5Widget import secrets @@ -20,79 +37,84 @@ import string from django.core.exceptions import ValidationError from django.utils import timezone + def generate_api_key(length=32): """Generate a secure API key""" alphabet = string.ascii_letters + string.digits - return ''.join(secrets.choice(alphabet) for _ in range(length)) + return "".join(secrets.choice(alphabet) for _ in range(length)) + def generate_api_secret(length=64): """Generate a secure API secret""" - alphabet = string.ascii_letters + string.digits + '-._~' - return ''.join(secrets.choice(alphabet) for _ in range(length)) + alphabet = string.ascii_letters + string.digits + "-._~" + return "".join(secrets.choice(alphabet) for _ in range(length)) + class SourceForm(forms.ModelForm): """Simple form for creating and editing sources""" class Meta: model = Source - fields = [ - 'name', 'source_type', 'description', 'ip_address', 'is_active' - ] + fields = ["name", "source_type", "description", "ip_address", "is_active"] widgets = { - 'name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'e.g., ATS System, ERP Integration', - 'required': True - }), - 'source_type': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'e.g., ATS, ERP, API', - 'required': True - }), - 'description': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 3, - 'placeholder': 'Brief description of the source system' - }), - 'ip_address': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': '192.168.1.100' - }), - 'is_active': forms.CheckboxInput(attrs={ - 'class': 'form-check-input' - }), + "name": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "e.g., ATS System, ERP Integration", + "required": True, + } + ), + "source_type": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "e.g., ATS, ERP, API", + "required": True, + } + ), + "description": forms.Textarea( + attrs={ + "class": "form-control", + "rows": 3, + "placeholder": "Brief description of the source system", + } + ), + "ip_address": forms.TextInput( + attrs={"class": "form-control", "placeholder": "192.168.1.100"} + ), + "is_active": forms.CheckboxInput(attrs={"class": "form-check-input"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" self.helper.layout = Layout( - Field('name', css_class='form-control'), - Field('source_type', css_class='form-control'), - Field('ip_address', css_class='form-control'), - Field('is_active', css_class='form-check-input'), - Submit('submit', 'Save Source', css_class='btn btn-primary mt-3') + Field("name", css_class="form-control"), + Field("source_type", css_class="form-control"), + Field("ip_address", css_class="form-control"), + Field("is_active", css_class="form-check-input"), + Submit("submit", "Save Source", css_class="btn btn-primary mt-3"), ) def clean_name(self): """Ensure source name is unique""" - name = self.cleaned_data.get('name') + name = self.cleaned_data.get("name") if name: # Check for duplicates excluding current instance if editing instance = self.instance if not instance.pk: # Creating new instance if Source.objects.filter(name=name).exists(): - raise ValidationError('A source with this name already exists.') + raise ValidationError("A source with this name already exists.") else: # Editing existing instance if Source.objects.filter(name=name).exclude(pk=instance.pk).exists(): - raise ValidationError('A source with this name already exists.') + raise ValidationError("A source with this name already exists.") return name + class SourceAdvancedForm(forms.ModelForm): """Advanced form for creating and editing sources with API key generation""" @@ -100,118 +122,126 @@ class SourceAdvancedForm(forms.ModelForm): generate_keys = forms.CharField( widget=forms.HiddenInput(), required=False, - help_text="Set to 'true' to generate new API keys" + help_text="Set to 'true' to generate new API keys", ) # Display fields for generated keys (read-only) api_key_generated = forms.CharField( label="Generated API Key", required=False, - widget=forms.TextInput(attrs={'readonly': True, 'class': 'form-control'}) + widget=forms.TextInput(attrs={"readonly": True, "class": "form-control"}), ) api_secret_generated = forms.CharField( label="Generated API Secret", required=False, - widget=forms.TextInput(attrs={'readonly': True, 'class': 'form-control'}) + widget=forms.TextInput(attrs={"readonly": True, "class": "form-control"}), ) class Meta: model = Source fields = [ - 'name', 'source_type', 'description', 'ip_address', - 'trusted_ips', 'is_active', 'integration_version' + "name", + "source_type", + "description", + "ip_address", + "trusted_ips", + "is_active", + "integration_version", ] widgets = { - 'name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'e.g., ATS System, ERP Integration', - 'required': True - }), - 'source_type': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'e.g., ATS, ERP, API', - 'required': True - }), - 'description': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 3, - 'placeholder': 'Brief description of the source system' - }), - 'ip_address': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': '192.168.1.100' - }), - 'trusted_ips': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 2, - 'placeholder': 'Comma-separated IP addresses (e.g., 192.168.1.100, 10.0.0.1)' - }), - 'integration_version': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'v1.0, v2.1' - }), + "name": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "e.g., ATS System, ERP Integration", + "required": True, + } + ), + "source_type": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "e.g., ATS, ERP, API", + "required": True, + } + ), + "description": forms.Textarea( + attrs={ + "class": "form-control", + "rows": 3, + "placeholder": "Brief description of the source system", + } + ), + "ip_address": forms.TextInput( + attrs={"class": "form-control", "placeholder": "192.168.1.100"} + ), + "trusted_ips": forms.Textarea( + attrs={ + "class": "form-control", + "rows": 2, + "placeholder": "Comma-separated IP addresses (e.g., 192.168.1.100, 10.0.0.1)", + } + ), + "integration_version": forms.TextInput( + attrs={"class": "form-control", "placeholder": "v1.0, v2.1"} + ), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" # Add generate keys button self.helper.layout = Layout( - Field('name', css_class='form-control'), - Field('source_type', css_class='form-control'), - Field('description', css_class='form-control'), - Field('ip_address', css_class='form-control'), - Field('trusted_ips', css_class='form-control'), - Field('integration_version', css_class='form-control'), - Field('is_active', css_class='form-check-input'), - + Field("name", css_class="form-control"), + Field("source_type", css_class="form-control"), + Field("description", css_class="form-control"), + Field("ip_address", css_class="form-control"), + Field("trusted_ips", css_class="form-control"), + Field("integration_version", css_class="form-control"), + Field("is_active", css_class="form-check-input"), # Hidden field for key generation trigger - Field('generate_keys', type='hidden'), - + Field("generate_keys", type="hidden"), # Display fields for generated keys - Field('api_key_generated', css_class='form-control'), - Field('api_secret_generated', css_class='form-control'), - - Submit('submit', 'Save Source', css_class='btn btn-primary mt-3') + Field("api_key_generated", css_class="form-control"), + Field("api_secret_generated", css_class="form-control"), + Submit("submit", "Save Source", css_class="btn btn-primary mt-3"), ) def clean_name(self): """Ensure source name is unique""" - name = self.cleaned_data.get('name') + name = self.cleaned_data.get("name") if name: # Check for duplicates excluding current instance if editing instance = self.instance if not instance.pk: # Creating new instance if Source.objects.filter(name=name).exists(): - raise ValidationError('A source with this name already exists.') + raise ValidationError("A source with this name already exists.") else: # Editing existing instance if Source.objects.filter(name=name).exclude(pk=instance.pk).exists(): - raise ValidationError('A source with this name already exists.') + raise ValidationError("A source with this name already exists.") return name def clean_trusted_ips(self): """Validate and format trusted IP addresses""" - trusted_ips = self.cleaned_data.get('trusted_ips') + trusted_ips = self.cleaned_data.get("trusted_ips") if trusted_ips: # Split by comma and strip whitespace - ips = [ip.strip() for ip in trusted_ips.split(',') if ip.strip()] + ips = [ip.strip() for ip in trusted_ips.split(",") if ip.strip()] # Validate each IP address for ip in ips: try: # Basic IP validation (can be enhanced) - if not (ip.replace('.', '').isdigit() and len(ip.split('.')) == 4): - raise ValidationError(f'Invalid IP address: {ip}') + if not (ip.replace(".", "").isdigit() and len(ip.split(".")) == 4): + raise ValidationError(f"Invalid IP address: {ip}") except Exception: - raise ValidationError(f'Invalid IP address: {ip}') + raise ValidationError(f"Invalid IP address: {ip}") - return ', '.join(ips) + return ", ".join(ips) return trusted_ips def clean(self): @@ -219,147 +249,203 @@ class SourceAdvancedForm(forms.ModelForm): cleaned_data = super().clean() # Check if we need to generate API keys - generate_keys = cleaned_data.get('generate_keys') + generate_keys = cleaned_data.get("generate_keys") - if generate_keys == 'true': + if generate_keys == "true": # Generate new API key and secret - cleaned_data['api_key'] = generate_api_key() - cleaned_data['api_secret'] = generate_api_secret() + cleaned_data["api_key"] = generate_api_key() + cleaned_data["api_secret"] = generate_api_secret() # Set display fields for the frontend - cleaned_data['api_key_generated'] = cleaned_data['api_key'] - cleaned_data['api_secret_generated'] = cleaned_data['api_secret'] + cleaned_data["api_key_generated"] = cleaned_data["api_key"] + cleaned_data["api_secret_generated"] = cleaned_data["api_secret"] return cleaned_data -class CandidateForm(forms.ModelForm): + +class PersonForm(forms.ModelForm): class Meta: - model = Candidate - fields = ['job', 'first_name', 'last_name', 'phone', 'email','hiring_source','hiring_agency', 'resume',] + model = Person + fields = ["first_name","middle_name", "last_name", "email", "phone","date_of_birth","nationality","address","gender"] + widgets = { + "first_name": forms.TextInput(attrs={'class': 'form-control'}), + "middle_name": forms.TextInput(attrs={'class': 'form-control'}), + "last_name": forms.TextInput(attrs={'class': 'form-control'}), + "email": forms.EmailInput(attrs={'class': 'form-control'}), + "phone": forms.TextInput(attrs={'class': 'form-control'}), + "gender": forms.Select(attrs={'class': 'form-control'}), + "date_of_birth": forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), + "nationality": forms.Select(attrs={'class': 'form-control select2'}), + "address": forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), + } +class ApplicationForm(forms.ModelForm): + + class Meta: + model = Application + fields = [ + 'person', + "job", + "hiring_source", + "hiring_agency", + "resume", + ] labels = { - 'first_name': _('First Name'), - 'last_name': _('Last Name'), - 'phone': _('Phone'), - 'email': _('Email'), - 'resume': _('Resume'), - 'hiring_source': _('Hiring Type'), - 'hiring_agency': _('Hiring Agency'), + "resume": _("Resume"), + "hiring_source": _("Hiring Type"), + "hiring_agency": _("Hiring Agency"), } widgets = { - 'first_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter first name')}), - 'last_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter last name')}), - 'phone': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter phone number')}), - 'email': forms.EmailInput(attrs={'class': 'form-control', 'placeholder': _('Enter email')}), - 'stage': forms.Select(attrs={'class': 'form-select'}), - 'hiring_source': forms.Select(attrs={'class': 'form-select'}), - 'hiring_agency': forms.Select(attrs={'class': 'form-select'}), + "hiring_source": forms.Select(attrs={"class": "form-select"}), + "hiring_agency": forms.Select(attrs={"class": "form-select"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" # Make job field read-only if it's being pre-populated - job_value = self.initial.get('job') + job_value = self.initial.get("job") if job_value: - self.fields['job'].widget.attrs['readonly'] = True + self.fields["job"].widget.attrs["readonly"] = True self.helper.layout = Layout( - Field('job', css_class='form-control'), - Field('first_name', css_class='form-control'), - Field('last_name', css_class='form-control'), - Field('phone', css_class='form-control'), - Field('email', css_class='form-control'), - Field('stage', css_class='form-control'), - Field('hiring_source', css_class='form-control'), - Field('hiring_agency', css_class='form-control'), - Field('resume', css_class='form-control'), - Submit('submit', _('Submit'), css_class='btn btn-primary') - + Field("job", css_class="form-control"), + Field("hiring_source", css_class="form-control"), + Field("hiring_agency", css_class="form-control"), + Field("resume", css_class="form-control"), + Submit("submit", _("Submit"), css_class="btn btn-primary"), ) -class CandidateStageForm(forms.ModelForm): + # def save(self, commit=True): + # """Override save to handle person creation/update""" + # instance = super().save(commit=False) + + # # Get or create person + # if instance.person: + # person = instance.person + # else: + # # Create new person + # from .models import Person + # person = Person() + + # # Update person fields + # person.first_name = self.cleaned_data['first_name'] + # person.last_name = self.cleaned_data['last_name'] + # person.email = self.cleaned_data['email'] + # person.phone = self.cleaned_data['phone'] + + # if commit: + # person.save() + # instance.person = person + # instance.save() + + # return instance + + +class ApplicationStageForm(forms.ModelForm): """Form specifically for updating candidate stage with validation""" class Meta: - model = Candidate - fields = ['stage'] + model = Application + fields = ["stage"] labels = { - 'stage': _('New Application Stage'), + "stage": _("New Application Stage"), } widgets = { - 'stage': forms.Select(attrs={'class': 'form-select'}), + "stage": forms.Select(attrs={"class": "form-select"}), } + class ZoomMeetingForm(forms.ModelForm): class Meta: model = ZoomMeeting - fields = ['topic', 'start_time', 'duration'] + fields = ["topic", "start_time", "duration"] labels = { - 'topic': _('Topic'), - 'start_time': _('Start Time'), - 'duration': _('Duration'), + "topic": _("Topic"), + "start_time": _("Start Time"), + "duration": _("Duration"), } widgets = { - 'topic': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter meeting topic'),}), - 'start_time': forms.DateTimeInput(attrs={'class': 'form-control','type': 'datetime-local'}), - 'duration': forms.NumberInput(attrs={'class': 'form-control','min': 1, 'placeholder': _('60')}), + "topic": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": _("Enter meeting topic"), + } + ), + "start_time": forms.DateTimeInput( + attrs={"class": "form-control", "type": "datetime-local"} + ), + "duration": forms.NumberInput( + attrs={"class": "form-control", "min": 1, "placeholder": _("60")} + ), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" self.helper.layout = Layout( - Field('topic', css_class='form-control'), - Field('start_time', css_class='form-control'), - Field('duration', css_class='form-control'), - Submit('submit', _('Create Meeting'), css_class='btn btn-primary') + Field("topic", css_class="form-control"), + Field("start_time", css_class="form-control"), + Field("duration", css_class="form-control"), + Submit("submit", _("Create Meeting"), css_class="btn btn-primary"), ) + class TrainingMaterialForm(forms.ModelForm): class Meta: model = TrainingMaterial - fields = ['title', 'content', 'video_link', 'file'] + fields = ["title", "content", "video_link", "file"] labels = { - 'title': _('Title'), - 'content': _('Content'), - 'video_link': _('Video Link'), - 'file': _('File'), + "title": _("Title"), + "content": _("Content"), + "video_link": _("Video Link"), + "file": _("File"), } widgets = { - 'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Enter material title')}), - 'content': CKEditor5Widget(attrs={'placeholder': _('Enter material content')}), - 'video_link': forms.URLInput(attrs={'class': 'form-control', 'placeholder': _('https://www.youtube.com/watch?v=...')}), - 'file': forms.FileInput(attrs={'class': 'form-control'}), + "title": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": _("Enter material title"), + } + ), + "content": CKEditor5Widget( + attrs={"placeholder": _("Enter material content")} + ), + "video_link": forms.URLInput( + attrs={ + "class": "form-control", + "placeholder": _("https://www.youtube.com/watch?v=..."), + } + ), + "file": forms.FileInput(attrs={"class": "form-control"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'g-3' + self.helper.form_method = "post" + self.helper.form_class = "g-3" self.helper.layout = Layout( - 'title', - 'content', + "title", + "content", Row( - Column('video_link', css_class='col-md-6'), - Column('file', css_class='col-md-6'), - css_class='g-3 mb-4' + Column("video_link", css_class="col-md-6"), + Column("file", css_class="col-md-6"), + css_class="g-3 mb-4", ), Div( - Submit('submit', _('Create Material'), - css_class='btn btn-main-action'), - css_class='col-12 mt-4' - ) + Submit("submit", _("Create Material"), css_class="btn btn-main-action"), + css_class="col-12 mt-4", + ), ) @@ -369,117 +455,116 @@ class JobPostingForm(forms.ModelForm): class Meta: model = JobPosting fields = [ - 'title', 'department', 'job_type', 'workplace_type', - 'location_city', 'location_state', 'location_country', - 'description', 'qualifications', 'salary_range', 'benefits', - 'application_deadline', 'application_instructions', - 'position_number', 'reporting_to', - 'open_positions', 'hash_tags', 'max_applications' + "title", + "department", + "job_type", + "workplace_type", + "location_city", + "location_state", + "location_country", + "description", + "qualifications", + "salary_range", + "benefits", + "application_deadline", + "application_instructions", + "position_number", + "reporting_to", + "open_positions", + "hash_tags", + "max_applications", ] widgets = { # Basic Information - 'title': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Assistant Professor of Computer Science', - 'required': True - }), - 'department': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Computer Science, Human Resources, etc.' - }), - 'job_type': forms.Select(attrs={ - 'class': 'form-select', - 'required': True - }), - 'workplace_type': forms.Select(attrs={ - 'class': 'form-select', - 'required': True - }), - + "title": forms.TextInput( + attrs={"class": "form-control", "placeholder": "", "required": True} + ), + "department": forms.TextInput( + attrs={"class": "form-control", "placeholder": ""} + ), + "job_type": forms.Select(attrs={"class": "form-select", "required": True}), + "workplace_type": forms.Select( + attrs={"class": "form-select", "required": True} + ), # Location - 'location_city': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Boston' - }), - 'location_state': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'MA' - }), - 'location_country': forms.TextInput(attrs={ - 'class': 'form-control', - 'value': 'United States' - }), - - 'salary_range': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': '$60,000 - $80,000' - }), - + "location_city": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Boston"} + ), + "location_state": forms.TextInput( + attrs={"class": "form-control", "placeholder": "MA"} + ), + "location_country": forms.TextInput( + attrs={"class": "form-control", "value": "United States"} + ), + "salary_range": forms.TextInput( + attrs={"class": "form-control", "placeholder": "$60,000 - $80,000"} + ), # Application Information # 'application_url': forms.URLInput(attrs={ # 'class': 'form-control', # 'placeholder': 'https://university.edu/careers/job123', # 'required': True # }), - - 'application_deadline': forms.DateInput(attrs={ - 'class': 'form-control', - 'type': 'date', - 'required': True - }), - - 'open_positions': forms.NumberInput(attrs={ - 'class': 'form-control', - 'min': 1, - 'placeholder': 'Number of open positions' - }), - 'hash_tags': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': '#hiring,#jobopening', - # 'validators':validate_hash_tags, # Assuming this is available - }), - + "application_deadline": forms.DateInput( + attrs={"class": "form-control", "type": "date", "required": True} + ), + "open_positions": forms.NumberInput( + attrs={ + "class": "form-control", + "min": 1, + "placeholder": "Number of open positions", + } + ), + "hash_tags": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "#hiring,#jobopening", + # 'validators':validate_hash_tags, # Assuming this is available + } + ), # Internal Information - 'position_number': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'UNIV-2025-001' - }), - 'reporting_to': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Department Chair, Director, etc.' - }), - - 'max_applications': forms.NumberInput(attrs={ - 'class': 'form-control', - 'min': 1, - 'placeholder': 'Maximum number of applicants' - }), + "position_number": forms.TextInput( + attrs={"class": "form-control", "placeholder": "UNIV-2025-001"} + ), + "reporting_to": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Department Chair, Director, etc.", + } + ), + "max_applications": forms.NumberInput( + attrs={ + "class": "form-control", + "min": 1, + "placeholder": "Maximum number of applicants", + } + ), } def __init__(self, *args, **kwargs): - # Now call the parent __init__ with remaining args super().__init__(*args, **kwargs) if not self.instance.pk: # Creating new job posting # self.fields['status'].initial = 'Draft' - self.fields['location_city'].initial = 'Riyadh' - self.fields['location_state'].initial = 'Riyadh Province' - self.fields['location_country'].initial = 'Saudi Arabia' + self.fields["location_city"].initial = "Riyadh" + self.fields["location_state"].initial = "Riyadh Province" + self.fields["location_country"].initial = "Saudi Arabia" def clean_hash_tags(self): - hash_tags = self.cleaned_data.get('hash_tags') + hash_tags = self.cleaned_data.get("hash_tags") if hash_tags: - tags = [tag.strip() for tag in hash_tags.split(',') if tag.strip()] + tags = [tag.strip() for tag in hash_tags.split(",") if tag.strip()] for tag in tags: - if not tag.startswith('#'): + if not tag.startswith("#"): raise forms.ValidationError( - "Each hashtag must start with '#' symbol and must be comma(,) sepearted.") - return ','.join(tags) + "Each hashtag must start with '#' symbol and must be comma(,) sepearted." + ) + return ",".join(tags) return hash_tags # Allow blank def clean_title(self): - title = self.cleaned_data.get('title') + title = self.cleaned_data.get("title") if not title or len(title.strip()) < 3: raise forms.ValidationError("Job title must be at least 3 characters long.") if len(title) > 200: @@ -487,166 +572,211 @@ class JobPostingForm(forms.ModelForm): return title.strip() def clean_description(self): - description = self.cleaned_data.get('description') + description = self.cleaned_data.get("description") if not description or len(description.strip()) < 20: - raise forms.ValidationError("Job description must be at least 20 characters long.") + raise forms.ValidationError( + "Job description must be at least 20 characters long." + ) return description.strip() # to remove leading/trailing whitespace def clean_application_url(self): - url = self.cleaned_data.get('application_url') + url = self.cleaned_data.get("application_url") if url: validator = URLValidator() try: validator(url) except forms.ValidationError: - raise forms.ValidationError('Please enter a valid URL (e.g., https://example.com)') + raise forms.ValidationError( + "Please enter a valid URL (e.g., https://example.com)" + ) return url + class JobPostingImageForm(forms.ModelForm): class Meta: - model=JobPostingImage - fields=['post_image'] + model = JobPostingImage + fields = ["post_image"] + class FormTemplateForm(forms.ModelForm): """Form for creating form templates""" + class Meta: model = FormTemplate - fields = ['job','name', 'description', 'is_active'] + fields = ["job", "name", "description", "is_active"] labels = { - 'job': _('Job'), - 'name': _('Template Name'), - 'description': _('Description'), - 'is_active': _('Active'), + "job": _("Job"), + "name": _("Template Name"), + "description": _("Description"), + "is_active": _("Active"), } widgets = { - 'name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': _('Enter template name'), - 'required': True - }), - 'description': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 3, - 'placeholder': _('Enter template description (optional)') - }), - 'is_active': forms.CheckboxInput(attrs={ - 'class': 'form-check-input' - }) + "name": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": _("Enter template name"), + "required": True, + } + ), + "description": forms.Textarea( + attrs={ + "class": "form-control", + "rows": 3, + "placeholder": _("Enter template description (optional)"), + } + ), + "is_active": forms.CheckboxInput(attrs={"class": "form-check-input"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" self.helper.layout = Layout( - Field('job', css_class='form-control'), - Field('name', css_class='form-control'), - Field('description', css_class='form-control'), - Field('is_active', css_class='form-check-input'), - Submit('submit', _('Create Template'), css_class='btn btn-primary mt-3') + Field("job", css_class="form-control"), + Field("name", css_class="form-control"), + Field("description", css_class="form-control"), + Field("is_active", css_class="form-check-input"), + Submit("submit", _("Create Template"), css_class="btn btn-primary mt-3"), ) + class BreakTimeForm(forms.Form): """ A simple Form used for the BreakTimeFormSet. It is not a ModelForm because the data is stored directly in InterviewSchedule's JSONField, not in a separate BreakTime model instance. """ + start_time = forms.TimeField( - widget=forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), - label="Start Time" + widget=forms.TimeInput(attrs={"type": "time", "class": "form-control"}), + label="Start Time", ) end_time = forms.TimeField( - widget=forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), - label="End Time" + widget=forms.TimeInput(attrs={"type": "time", "class": "form-control"}), + label="End Time", ) + BreakTimeFormSet = formset_factory(BreakTimeForm, extra=1, can_delete=True) + class InterviewScheduleForm(forms.ModelForm): - candidates = forms.ModelMultipleChoiceField( - queryset=Candidate.objects.none(), + applications = forms.ModelMultipleChoiceField( + queryset=Application.objects.none(), widget=forms.CheckboxSelectMultiple, - required=True + required=True, ) working_days = forms.MultipleChoiceField( choices=[ - (0, 'Monday'), (1, 'Tuesday'), (2, 'Wednesday'), (3, 'Thursday'), - (4, 'Friday'), (5, 'Saturday'), (6, 'Sunday'), + (0, "Monday"), + (1, "Tuesday"), + (2, "Wednesday"), + (3, "Thursday"), + (4, "Friday"), + (5, "Saturday"), + (6, "Sunday"), ], widget=forms.CheckboxSelectMultiple, - required=True + required=True, ) class Meta: model = InterviewSchedule fields = [ - 'candidates', 'interview_type', 'start_date', 'end_date', 'working_days', - 'start_time', 'end_time', 'interview_duration', 'buffer_time', - 'break_start_time', 'break_end_time' + "applications", + "start_date", + "end_date", + "working_days", + "start_time", + "end_time", + "interview_duration", + "buffer_time", + "break_start_time", + "break_end_time", + "interview_type" ] widgets = { 'interview_type': forms.Select(attrs={'class': 'form-control'}), - 'start_date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}), - 'end_date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}), - 'start_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), - 'end_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), - 'interview_duration': forms.NumberInput(attrs={'class': 'form-control'}), - 'buffer_time': forms.NumberInput(attrs={'class': 'form-control'}), - 'break_start_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), - 'break_end_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), + "start_date": forms.DateInput( + attrs={"type": "date", "class": "form-control"} + ), + "end_date": forms.DateInput( + attrs={"type": "date", "class": "form-control"} + ), + "start_time": forms.TimeInput( + attrs={"type": "time", "class": "form-control"} + ), + "end_time": forms.TimeInput( + attrs={"type": "time", "class": "form-control"} + ), + "interview_duration": forms.NumberInput(attrs={"class": "form-control"}), + "buffer_time": forms.NumberInput(attrs={"class": "form-control"}), + "break_start_time": forms.TimeInput( + attrs={"type": "time", "class": "form-control"} + ), + "break_end_time": forms.TimeInput( + attrs={"type": "time", "class": "form-control"} + ), } def __init__(self, slug, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['candidates'].queryset = Candidate.objects.filter( - job__slug=slug, - stage='Interview' + self.fields["applications"].queryset = Application.objects.filter( + job__slug=slug, stage="Interview" ) def clean_working_days(self): - working_days = self.cleaned_data.get('working_days') + working_days = self.cleaned_data.get("working_days") return [int(day) for day in working_days] + class MeetingCommentForm(forms.ModelForm): """Form for creating and editing meeting comments""" + class Meta: model = MeetingComment - fields = ['content'] + fields = ["content"] widgets = { - 'content': CKEditor5Widget( - attrs={'class': 'form-control', 'placeholder': _('Enter your comment or note')}, - config_name='extends' + "content": CKEditor5Widget( + attrs={ + "class": "form-control", + "placeholder": _("Enter your comment or note"), + }, + config_name="extends", ), } labels = { - 'content': _('Comment'), + "content": _("Comment"), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" self.helper.layout = Layout( - Field('content', css_class='form-control'), - Submit('submit', _('Add Comment'), css_class='btn btn-primary mt-3') + Field("content", css_class="form-control"), + Submit("submit", _("Add Comment"), css_class="btn btn-primary mt-3"), ) + class InterviewForm(forms.ModelForm): class Meta: model = ScheduledInterview - fields = ['job','candidate'] + fields = ["job", "application"] + class ProfileImageUploadForm(forms.ModelForm): class Meta: - model=Profile - fields=['profile_image'] + model = Profile + fields = ["profile_image"] + class StaffUserCreationForm(UserCreationForm): email = forms.EmailField(required=True) @@ -665,10 +795,10 @@ class StaffUserCreationForm(UserCreationForm): def generate_username(self, email): """Generate a valid, unique username from email.""" - prefix = email.split('@')[0].lower() - username = re.sub(r'[^a-z0-9._]', '', prefix) + prefix = email.split("@")[0].lower() + username = re.sub(r"[^a-z0-9._]", "", prefix) if not username: - username = 'user' + username = "user" base = username counter = 1 while User.objects.filter(username=username).exists(): @@ -687,173 +817,200 @@ class StaffUserCreationForm(UserCreationForm): user.save() return user + class ToggleAccountForm(forms.Form): pass + class JobPostingCancelReasonForm(forms.ModelForm): class Meta: model = JobPosting - fields = ['cancel_reason'] + fields = ["cancel_reason"] + class JobPostingStatusForm(forms.ModelForm): class Meta: model = JobPosting - fields = ['status'] + fields = ["status"] widgets = { - 'status': forms.Select(attrs={'class': 'form-select'}), + "status": forms.Select(attrs={"class": "form-select"}), } + + class LinkedPostContentForm(forms.ModelForm): class Meta: model = JobPosting - fields = ['linkedin_post_formated_data'] + fields = ["linkedin_post_formated_data"] + class FormTemplateIsActiveForm(forms.ModelForm): class Meta: model = FormTemplate - fields = ['is_active'] + fields = ["is_active"] + class CandidateExamDateForm(forms.ModelForm): class Meta: - model = Candidate - fields = ['exam_date'] + model = Application + fields = ["exam_date"] widgets = { - 'exam_date': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}), + "exam_date": forms.DateTimeInput( + attrs={"type": "datetime-local", "class": "form-control"} + ), } + class HiringAgencyForm(forms.ModelForm): """Form for creating and editing hiring agencies""" class Meta: model = HiringAgency fields = [ - 'name', 'contact_person', 'email', 'phone', - 'website', 'country', 'address', 'notes' + "name", + "contact_person", + "email", + "phone", + "website", + "country", + "address", + "notes", ] widgets = { - 'name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter agency name', - 'required': True - }), - 'contact_person': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter contact person name' - }), - 'email': forms.EmailInput(attrs={ - 'class': 'form-control', - 'placeholder': 'agency@example.com' - }), - 'phone': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': '+966 50 123 4567' - }), - 'website': forms.URLInput(attrs={ - 'class': 'form-control', - 'placeholder': 'https://www.agency.com' - }), - 'country': forms.Select(attrs={ - 'class': 'form-select' - }), - 'address': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 3, - 'placeholder': 'Enter agency address' - }), - 'notes': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 3, - 'placeholder': 'Internal notes about the agency' - }), + "name": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Enter agency name", + "required": True, + } + ), + "contact_person": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Enter contact person name", + } + ), + "email": forms.EmailInput( + attrs={"class": "form-control", "placeholder": "agency@example.com"} + ), + "phone": forms.TextInput( + attrs={"class": "form-control", "placeholder": "+966 50 123 4567"} + ), + "website": forms.URLInput( + attrs={"class": "form-control", "placeholder": "https://www.agency.com"} + ), + "country": forms.Select(attrs={"class": "form-select"}), + "address": forms.Textarea( + attrs={ + "class": "form-control", + "rows": 3, + "placeholder": "Enter agency address", + } + ), + "notes": forms.Textarea( + attrs={ + "class": "form-control", + "rows": 3, + "placeholder": "Internal notes about the agency", + } + ), } labels = { - 'name': _('Agency Name'), - 'contact_person': _('Contact Person'), - 'email': _('Email Address'), - 'phone': _('Phone Number'), - 'website': _('Website'), - 'country': _('Country'), - 'address': _('Address'), - 'notes': _('Internal Notes'), + "name": _("Agency Name"), + "contact_person": _("Contact Person"), + "email": _("Email Address"), + "phone": _("Phone Number"), + "website": _("Website"), + "country": _("Country"), + "address": _("Address"), + "notes": _("Internal Notes"), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" self.helper.layout = Layout( - Field('name', css_class='form-control'), - Field('contact_person', css_class='form-control'), + Field("name", css_class="form-control"), + Field("contact_person", css_class="form-control"), Row( - Column('email', css_class='col-md-6'), - Column('phone', css_class='col-md-6'), - css_class='g-3 mb-3' + Column("email", css_class="col-md-6"), + Column("phone", css_class="col-md-6"), + css_class="g-3 mb-3", ), - Field('website', css_class='form-control'), - Field('country', css_class='form-control'), - Field('address', css_class='form-control'), - Field('notes', css_class='form-control'), + Field("website", css_class="form-control"), + Field("country", css_class="form-control"), + Field("address", css_class="form-control"), + Field("notes", css_class="form-control"), Div( - Submit('submit', _('Save Agency'), css_class='btn btn-main-action'), - css_class='col-12 mt-4' - ) + Submit("submit", _("Save Agency"), css_class="btn btn-main-action"), + css_class="col-12 mt-4", + ), ) def clean_name(self): """Ensure agency name is unique""" - name = self.cleaned_data.get('name') + name = self.cleaned_data.get("name") if name: instance = self.instance if not instance.pk: # Creating new instance if HiringAgency.objects.filter(name=name).exists(): - raise ValidationError('An agency with this name already exists.') + raise ValidationError("An agency with this name already exists.") else: # Editing existing instance - if HiringAgency.objects.filter(name=name).exclude(pk=instance.pk).exists(): - raise ValidationError('An agency with this name already exists.') + if ( + HiringAgency.objects.filter(name=name) + .exclude(pk=instance.pk) + .exists() + ): + raise ValidationError("An agency with this name already exists.") return name.strip() def clean_email(self): """Validate email format and uniqueness""" - email = self.cleaned_data.get('email') + email = self.cleaned_data.get("email") if email: # Check email format - if not '@' in email or '.' not in email.split('@')[1]: - raise ValidationError('Please enter a valid email address.') + if not "@" in email or "." not in email.split("@")[1]: + raise ValidationError("Please enter a valid email address.") # Check uniqueness (optional - remove if multiple agencies can have same email) instance = self.instance if not instance.pk: # Creating new instance if HiringAgency.objects.filter(email=email).exists(): - raise ValidationError('An agency with this email already exists.') + raise ValidationError("An agency with this email already exists.") else: # Editing existing instance - if HiringAgency.objects.filter(email=email).exclude(pk=instance.pk).exists(): - raise ValidationError('An agency with this email already exists.') + if ( + HiringAgency.objects.filter(email=email) + .exclude(pk=instance.pk) + .exists() + ): + raise ValidationError("An agency with this email already exists.") return email.lower().strip() if email else email def clean_phone(self): """Validate phone number format""" - phone = self.cleaned_data.get('phone') + phone = self.cleaned_data.get("phone") if phone: # Remove common formatting characters - clean_phone = ''.join(c for c in phone if c.isdigit() or c in '+') + clean_phone = "".join(c for c in phone if c.isdigit() or c in "+") if len(clean_phone) < 10: - raise ValidationError('Phone number must be at least 10 digits long.') + raise ValidationError("Phone number must be at least 10 digits long.") return phone.strip() if phone else phone def clean_website(self): """Validate website URL""" - website = self.cleaned_data.get('website') + website = self.cleaned_data.get("website") if website: - if not website.startswith(('http://', 'https://')): - website = 'https://' + website + if not website.startswith(("http://", "https://")): + website = "https://" + website validator = URLValidator() try: validator(website) except ValidationError: - raise ValidationError('Please enter a valid website URL.') + raise ValidationError("Please enter a valid website URL.") return website @@ -862,105 +1019,108 @@ class AgencyJobAssignmentForm(forms.ModelForm): class Meta: model = AgencyJobAssignment - fields = [ - 'agency', 'job', 'max_candidates', 'deadline_date','admin_notes' - ] + fields = ["agency", "job", "max_candidates", "deadline_date", "admin_notes"] widgets = { - 'agency': forms.Select(attrs={'class': 'form-select'}), - 'job': forms.Select(attrs={'class': 'form-select'}), - 'max_candidates': forms.NumberInput(attrs={ - 'class': 'form-control', - 'min': 1, - 'placeholder': 'Maximum number of candidates' - }), - 'deadline_date': forms.DateTimeInput(attrs={ - 'class': 'form-control', - 'type': 'datetime-local' - }), - 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), - 'status': forms.Select(attrs={'class': 'form-select'}), - 'admin_notes': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 3, - 'placeholder': 'Internal notes about this assignment' - }), + "agency": forms.Select(attrs={"class": "form-select"}), + "job": forms.Select(attrs={"class": "form-select"}), + "max_candidates": forms.NumberInput( + attrs={ + "class": "form-control", + "min": 1, + "placeholder": "Maximum number of candidates", + } + ), + "deadline_date": forms.DateTimeInput( + attrs={"class": "form-control", "type": "datetime-local"} + ), + "is_active": forms.CheckboxInput(attrs={"class": "form-check-input"}), + "status": forms.Select(attrs={"class": "form-select"}), + "admin_notes": forms.Textarea( + attrs={ + "class": "form-control", + "rows": 3, + "placeholder": "Internal notes about this assignment", + } + ), } labels = { - 'agency': _('Agency'), - 'job': _('Job Posting'), - 'max_candidates': _('Maximum Candidates'), - 'deadline_date': _('Deadline Date'), - 'is_active': _('Is Active'), - 'status': _('Status'), - 'admin_notes': _('Admin Notes'), + "agency": _("Agency"), + "job": _("Job Posting"), + "max_candidates": _("Maximum Candidates"), + "deadline_date": _("Deadline Date"), + "is_active": _("Is Active"), + "status": _("Status"), + "admin_notes": _("Admin Notes"), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" # Filter jobs to only show active jobs - self.fields['job'].queryset = JobPosting.objects.filter( - status='ACTIVE' - ).order_by('-created_at') + self.fields["job"].queryset = JobPosting.objects.filter( + status="ACTIVE" + ).order_by("-created_at") self.helper.layout = Layout( Row( - Column('agency', css_class='col-md-6'), - Column('job', css_class='col-md-6'), - css_class='g-3 mb-3' + Column("agency", css_class="col-md-6"), + Column("job", css_class="col-md-6"), + css_class="g-3 mb-3", ), Row( - Column('max_candidates', css_class='col-md-6'), - Column('deadline_date', css_class='col-md-6'), - css_class='g-3 mb-3' + Column("max_candidates", css_class="col-md-6"), + Column("deadline_date", css_class="col-md-6"), + css_class="g-3 mb-3", ), Row( - Column('is_active', css_class='col-md-6'), - Column('status', css_class='col-md-6'), - css_class='g-3 mb-3' + Column("is_active", css_class="col-md-6"), + Column("status", css_class="col-md-6"), + css_class="g-3 mb-3", ), - Field('admin_notes', css_class='form-control'), + Field("admin_notes", css_class="form-control"), Div( - Submit('submit', _('Save Assignment'), css_class='btn btn-main-action'), - css_class='col-12 mt-4' - ) + Submit("submit", _("Save Assignment"), css_class="btn btn-main-action"), + css_class="col-12 mt-4", + ), ) def clean_deadline_date(self): """Validate deadline date is in the future""" - deadline_date = self.cleaned_data.get('deadline_date') + deadline_date = self.cleaned_data.get("deadline_date") if deadline_date and deadline_date <= timezone.now(): - raise ValidationError('Deadline date must be in the future.') + raise ValidationError("Deadline date must be in the future.") return deadline_date def clean_max_candidates(self): """Validate maximum candidates is positive""" - max_candidates = self.cleaned_data.get('max_candidates') + max_candidates = self.cleaned_data.get("max_candidates") if max_candidates and max_candidates <= 0: - raise ValidationError('Maximum candidates must be greater than 0.') + raise ValidationError("Maximum candidates must be greater than 0.") return max_candidates def clean(self): """Check for duplicate assignments""" cleaned_data = super().clean() - agency = cleaned_data.get('agency') - job = cleaned_data.get('job') + agency = cleaned_data.get("agency") + job = cleaned_data.get("job") if agency and job: # Check if this assignment already exists - existing = AgencyJobAssignment.objects.filter( - agency=agency, job=job - ).exclude(pk=self.instance.pk).first() + existing = ( + AgencyJobAssignment.objects.filter(agency=agency, job=job) + .exclude(pk=self.instance.pk) + .first() + ) if existing: raise ValidationError( - f'This job is already assigned to {agency.name}. ' - f'Current status: {existing.get_status_display()}' + f"This job is already assigned to {agency.name}. " + f"Current status: {existing.get_status_display()}" ) return cleaned_data @@ -971,171 +1131,207 @@ class AgencyAccessLinkForm(forms.ModelForm): class Meta: model = AgencyAccessLink - fields = [ - 'assignment', 'expires_at', 'is_active' - ] + fields = ["assignment", "expires_at", "is_active"] widgets = { - 'assignment': forms.Select(attrs={'class': 'form-select'}), - 'expires_at': forms.DateTimeInput(attrs={ - 'class': 'form-control', - 'type': 'datetime-local' - }), - 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), + "assignment": forms.Select(attrs={"class": "form-select"}), + "expires_at": forms.DateTimeInput( + attrs={"class": "form-control", "type": "datetime-local"} + ), + "is_active": forms.CheckboxInput(attrs={"class": "form-check-input"}), } labels = { - 'assignment': _('Assignment'), - 'expires_at': _('Expires At'), - 'is_active': _('Is Active'), + "assignment": _("Assignment"), + "expires_at": _("Expires At"), + "is_active": _("Is Active"), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'form-horizontal' - self.helper.label_class = 'col-md-3' - self.helper.field_class = 'col-md-9' + self.helper.form_method = "post" + self.helper.form_class = "form-horizontal" + self.helper.label_class = "col-md-3" + self.helper.field_class = "col-md-9" # Filter assignments to only show active ones without existing links - self.fields['assignment'].queryset = AgencyJobAssignment.objects.filter( - is_active=True, - status='ACTIVE' - ).exclude( - access_link__isnull=False - ).order_by('-created_at') + self.fields["assignment"].queryset = ( + AgencyJobAssignment.objects.filter(is_active=True, status="ACTIVE") + .exclude(access_link__isnull=False) + .order_by("-created_at") + ) self.helper.layout = Layout( - Field('assignment', css_class='form-control'), - Field('expires_at', css_class='form-control'), - Field('is_active', css_class='form-check-input'), + Field("assignment", css_class="form-control"), + Field("expires_at", css_class="form-control"), + Field("is_active", css_class="form-check-input"), Div( - Submit('submit', _('Create Access Link'), css_class='btn btn-main-action'), - css_class='col-12 mt-4' - ) + Submit( + "submit", _("Create Access Link"), css_class="btn btn-main-action" + ), + css_class="col-12 mt-4", + ), ) def clean_expires_at(self): """Validate expiration date is in the future""" - expires_at = self.cleaned_data.get('expires_at') + expires_at = self.cleaned_data.get("expires_at") if expires_at and expires_at <= timezone.now(): - raise ValidationError('Expiration date must be in the future.') + raise ValidationError("Expiration date must be in the future.") return expires_at # Agency messaging forms removed - AgencyMessage model has been deleted -class AgencyCandidateSubmissionForm(forms.ModelForm): +class AgencyApplicationSubmissionForm(forms.ModelForm): """Form for agencies to submit candidates (simplified - resume + basic info)""" + # Person fields for creating/updating person + # first_name = forms.CharField( + # max_length=255, + # widget=forms.TextInput(attrs={ + # "class": "form-control", + # "placeholder": "First Name", + # "required": True, + # }), + # label=_("First Name") + # ) + # last_name = forms.CharField( + # max_length=255, + # widget=forms.TextInput(attrs={ + # "class": "form-control", + # "placeholder": "Last Name", + # "required": True, + # }), + # label=_("Last Name") + # ) + # email = forms.EmailField( + # widget=forms.EmailInput(attrs={ + # "class": "form-control", + # "placeholder": "email@example.com", + # "required": True, + # }), + # label=_("Email Address") + # ) + # phone = forms.CharField( + # max_length=20, + # widget=forms.TextInput(attrs={ + # "class": "form-control", + # "placeholder": "+966 50 123 4567", + # "required": True, + # }), + # label=_("Phone Number") + # ) + class Meta: - model = Candidate - fields = [ - 'first_name', 'last_name', 'email', 'phone', 'resume' - ] + model = Application + fields = ["person","resume"] widgets = { - 'first_name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'First Name', - 'required': True - }), - 'last_name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Last Name', - 'required': True - }), - 'email': forms.EmailInput(attrs={ - 'class': 'form-control', - 'placeholder': 'email@example.com', - 'required': True - }), - 'phone': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': '+966 50 123 4567', - 'required': True - }), - 'resume': forms.FileInput(attrs={ - 'class': 'form-control', - 'accept': '.pdf,.doc,.docx', - 'required': True - }), + "resume": forms.FileInput( + attrs={ + "class": "form-control", + "accept": ".pdf,.doc,.docx", + "required": True, + } + ), } labels = { - 'first_name': _('First Name'), - 'last_name': _('Last Name'), - 'email': _('Email Address'), - 'phone': _('Phone Number'), - 'resume': _('Resume'), + "resume": _("Resume"), } def __init__(self, assignment, *args, **kwargs): super().__init__(*args, **kwargs) self.assignment = assignment self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.form_class = 'g-3' - self.helper.enctype = 'multipart/form-data' + self.helper.form_method = "post" + self.helper.form_class = "g-3" + self.helper.enctype = "multipart/form-data" - self.helper.layout = Layout( - Row( - Column('first_name', css_class='col-md-6'), - Column('last_name', css_class='col-md-6'), - css_class='g-3 mb-3' - ), - Row( - Column('email', css_class='col-md-6'), - Column('phone', css_class='col-md-6'), - css_class='g-3 mb-3' - ), - Field('resume', css_class='form-control'), - Div( - Submit('submit', _('Submit Candidate'), css_class='btn btn-main-action'), - css_class='col-12 mt-4' - ) - ) + # self.helper.layout = Layout( + # Row( + # Column("first_name", css_class="col-md-6"), + # Column("last_name", css_class="col-md-6"), + # css_class="g-3 mb-3", + # ), + # Row( + # Column("email", css_class="col-md-6"), + # Column("phone", css_class="col-md-6"), + # css_class="g-3 mb-3", + # ), + # Field("resume", css_class="form-control"), + # Div( + # Submit( + # "submit", _("Submit Candidate"), css_class="btn btn-main-action" + # ), + # css_class="col-12 mt-4", + # ), + # ) def clean_email(self): """Validate email format and check for duplicates in the same job""" - email = self.cleaned_data.get('email') + email = self.cleaned_data.get("email") if email: - # Check if candidate with this email already exists for this job - existing_candidate = Candidate.objects.filter( - email=email.lower().strip(), - job=self.assignment.job + # Check if person with this email already exists for this job + from .models import Person + existing_person = Person.objects.filter( + email=email.lower().strip() ).first() - if existing_candidate: - raise ValidationError( - f'A candidate with this email has already applied for {self.assignment.job.title}.' - ) + if existing_person: + # Check if this person already has an application for this job + existing_application = Application.objects.filter( + person=existing_person, job=self.assignment.job + ).first() + + if existing_application: + raise ValidationError( + f"A candidate with this email has already applied for {self.assignment.job.title}." + ) return email.lower().strip() if email else email def clean_resume(self): """Validate resume file""" - resume = self.cleaned_data.get('resume') + resume = self.cleaned_data.get("resume") if resume: # Check file size (max 5MB) if resume.size > 5 * 1024 * 1024: - raise ValidationError('Resume file size must be less than 5MB.') + raise ValidationError("Resume file size must be less than 5MB.") # Check file extension - allowed_extensions = ['.pdf', '.doc', '.docx'] - file_extension = resume.name.lower().split('.')[-1] - if f'.{file_extension}' not in allowed_extensions: - raise ValidationError( - 'Resume must be in PDF, DOC, or DOCX format.' - ) + allowed_extensions = [".pdf", ".doc", ".docx"] + file_extension = resume.name.lower().split(".")[-1] + if f".{file_extension}" not in allowed_extensions: + raise ValidationError("Resume must be in PDF, DOC, or DOCX format.") return resume def save(self, commit=True): """Override save to set additional fields""" instance = super().save(commit=False) + # Create or get person + from .models import Person + person, created = Person.objects.get_or_create( + email=self.cleaned_data['email'].lower().strip(), + defaults={ + 'first_name': self.cleaned_data['first_name'], + 'last_name': self.cleaned_data['last_name'], + 'phone': self.cleaned_data['phone'], + } + ) + + if not created: + # Update existing person with new info + person.first_name = self.cleaned_data['first_name'] + person.last_name = self.cleaned_data['last_name'] + person.phone = self.cleaned_data['phone'] + person.save() + # Set required fields for agency submission + instance.person = person instance.job = self.assignment.job instance.hiring_agency = self.assignment.agency - instance.stage = Candidate.Stage.APPLIED - instance.applicant_status = Candidate.ApplicantType.CANDIDATE + instance.stage = Application.Stage.APPLIED + instance.applicant_status = Application.ApplicantType.CANDIDATE instance.applied = True if commit: @@ -1149,21 +1345,52 @@ class AgencyLoginForm(forms.Form): """Form for agencies to login with token and password""" token = forms.CharField( - widget=forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter your access token' - }), - label=_('Access Token'), - required=True + widget=forms.TextInput( + attrs={"class": "form-control", "placeholder": "Enter your access token"} + ), + label=_("Access Token"), + required=True, ) password = forms.CharField( - widget=forms.PasswordInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter your password' - }), - label=_('Password'), - required=True + widget=forms.PasswordInput( + attrs={"class": "form-control", "placeholder": "Enter your password"} + ), + label=_("Password"), + required=True, + ) + + +class PortalLoginForm(forms.Form): + """Unified login form for agency and candidate""" + + USER_TYPE_CHOICES = [ + ("", _("Select User Type")), + ("agency", _("Agency")), + ("candidate", _("Candidate")), + ] + + email = forms.EmailField( + widget=forms.EmailInput( + attrs={"class": "form-control", "placeholder": "Enter your email"} + ), + label=_("Email"), + required=True, + ) + + password = forms.CharField( + widget=forms.PasswordInput( + attrs={"class": "form-control", "placeholder": "Enter your password"} + ), + label=_("Password"), + required=True, + ) + + user_type = forms.ChoiceField( + choices=USER_TYPE_CHOICES, + widget=forms.Select(attrs={"class": "form-control"}), + label=_("User Type"), + required=True, ) # def __init__(self, *args, **kwargs): @@ -1184,62 +1411,60 @@ class AgencyLoginForm(forms.Form): def clean(self): """Validate token and password combination""" cleaned_data = super().clean() - token = cleaned_data.get('token') - password = cleaned_data.get('password') + token = cleaned_data.get("token") + password = cleaned_data.get("password") if token and password: try: access_link = AgencyAccessLink.objects.get( - unique_token=token, - is_active=True + unique_token=token, is_active=True ) if not access_link.is_valid: if access_link.is_expired: - raise ValidationError('This access link has expired.') + raise ValidationError("This access link has expired.") else: - raise ValidationError('This access link is no longer active.') + raise ValidationError("This access link is no longer active.") if access_link.access_password != password: - raise ValidationError('Invalid password.') + raise ValidationError("Invalid password.") # Store the access_link for use in the view self.validated_access_link = access_link except AgencyAccessLink.DoesNotExist: print("Access link does not exist") - raise ValidationError('Invalid access token.') + raise ValidationError("Invalid access token.") return cleaned_data - - - -#participants form +# participants form class ParticipantsForm(forms.ModelForm): """Form for creating and editing Participants""" class Meta: - model = Participants - fields = ['name', 'email', 'phone', 'designation'] + model = Participants + fields = ["name", "email", "phone", "designation"] widgets = { - 'name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter participant name', - 'required': True - }), - 'email': forms.EmailInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter email address', - 'required': True - }), - 'phone': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter phone number' - }), - 'designation': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter designation' - }), + "name": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Enter participant name", + "required": True, + } + ), + "email": forms.EmailInput( + attrs={ + "class": "form-control", + "placeholder": "Enter email address", + "required": True, + } + ), + "phone": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Enter phone number"} + ), + "designation": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Enter designation"} + ), # 'jobs': forms.CheckboxSelectMultiple(), } @@ -1247,21 +1472,23 @@ class ParticipantsForm(forms.ModelForm): # class ParticipantsSelectForm(forms.ModelForm): # """Form for selecting Participants""" -# participants=forms.ModelMultipleChoiceField( -# queryset=Participants.objects.all(), -# widget=forms.CheckboxSelectMultiple, -# required=False, -# label=_("Select Participants")) + participants = forms.ModelMultipleChoiceField( + queryset=Participants.objects.all(), + widget=forms.CheckboxSelectMultiple, + required=False, + label=_("Select Participants"), + ) -# users=forms.ModelMultipleChoiceField( -# queryset=User.objects.all(), -# widget=forms.CheckboxSelectMultiple, -# required=False, -# label=_("Select Users")) + users = forms.ModelMultipleChoiceField( + queryset=User.objects.all(), + widget=forms.CheckboxSelectMultiple, + required=False, + label=_("Select Users"), + ) -# class Meta: -# model = JobPosting -# fields = ['participants','users'] # No direct fields from Participants model + class Meta: + model = JobPosting + fields = ["participants", "users"] # No direct fields from Participants model class CandidateEmailForm(forms.Form): @@ -1273,61 +1500,79 @@ class CandidateEmailForm(forms.Form): label=_('Select Candidates'), # Use a descriptive label required=False ) - - subject = forms.CharField( max_length=200, - widget=forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter email subject', - 'required': True - }), - label=_('Subject'), - required=True + widget=forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Enter email subject", + "required": True, + } + ), + label=_("Subject"), + required=True, ) message = forms.CharField( - widget=forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 8, - 'placeholder': 'Enter your message here...', - 'required': True - }), - label=_('Message'), - required=True + widget=forms.Textarea( + attrs={ + "class": "form-control", + "rows": 8, + "placeholder": "Enter your message here...", + "required": True, + } + ), + label=_("Message"), + required=True, ) - + recipients = forms.MultipleChoiceField( + widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check"}), + label=_("Recipients"), + required=True, + ) + include_candidate_info = forms.BooleanField( + widget=forms.CheckboxInput(attrs={"class": "form-check-input"}), + label=_("Include candidate information"), + initial=True, + required=False, + ) + + include_meeting_details = forms.BooleanField( + widget=forms.CheckboxInput(attrs={"class": "form-check-input"}), + label=_("Include meeting details"), + initial=True, + required=False, + ) def __init__(self, job, candidates, *args, **kwargs): super().__init__(*args, **kwargs) self.job = job self.candidates=candidates - + candidate_choices=[] for candidate in candidates: candidate_choices.append( (f'candidate_{candidate.id}', f'{candidate.email}') ) - + self.fields['to'].choices =candidate_choices - self.fields['to'].initial = [choice[0] for choice in candidate_choices] - - + self.fields['to'].initial = [choice[0] for choice in candidate_choices] + + # Set initial message with candidate and meeting info initial_message = self._get_initial_message() - if initial_message: - self.fields['message'].initial = initial_message + self.fields["message"].initial = initial_message def _get_initial_message(self): """Generate initial message with candidate and meeting information""" candidate=self.candidates.first() message_parts=[] - + if candidate and candidate.stage == 'Applied': message_parts = [ f"Than you, for your interest in the {self.job.title} role.", @@ -1347,7 +1592,7 @@ class CandidateEmailForm(forms.Form): f"We look forward to reviewing your results.", f"Best regards, The KAAUH Hiring team" ] - + elif candidate and candidate.stage == 'Interview': message_parts = [ f"Than you, for your interest in the {self.job.title} role.", @@ -1358,7 +1603,7 @@ class CandidateEmailForm(forms.Form): f"We look forward to reviewing your results.", f"Best regards, The KAAUH Hiring team" ] - + elif candidate and candidate.stage == 'Offer': message_parts = [ f"Congratulations, ! We are delighted to inform you that we are extending a formal offer of employment for the {self.job.title} role.", @@ -1377,16 +1622,6 @@ class CandidateEmailForm(forms.Form): f"If you have any questions before your start date, please contact [Onboarding Contact].", f"Best regards, The KAAUH Hiring team" ] - - - - - # # Add candidate information - # if self.candidate: - # message_parts.append(f"Candidate Information:") - # message_parts.append(f"Name: {self.candidate.name}") - # message_parts.append(f"Email: {self.candidate.email}") - # message_parts.append(f"Phone: {self.candidate.phone}") # # Add latest meeting information if available # latest_meeting = self.candidate.get_latest_meeting @@ -1398,33 +1633,42 @@ class CandidateEmailForm(forms.Form): # if latest_meeting.join_url: # message_parts.append(f"Join URL: {latest_meeting.join_url}") - return '\n'.join(message_parts) + return "\n".join(message_parts) + def clean_recipients(self): + """Ensure at least one recipient is selected""" + recipients = self.cleaned_data.get('recipients') + if not recipients: + raise forms.ValidationError(_('Please select at least one recipient.')) + return recipients def get_email_addresses(self): """Extract email addresses from selected recipients""" email_addresses = [] - - candidates=self.cleaned_data.get('to',[]) - - if candidates: - for candidate in candidates: - if candidate.startswith('candidate_'): - print("candidadte: {candidate}") - candidate_id = candidate.split('_')[1] - try: - candidate = Candidate.objects.get(id=candidate_id) - email_addresses.append(candidate.email) - except Candidate.DoesNotExist: - continue + recipients = self.cleaned_data.get('recipients', []) + for recipient in recipients: + if recipient.startswith('participant_'): + participant_id = recipient.split('_')[1] + try: + participant = Participants.objects.get(id=participant_id) + email_addresses.append(participant.email) + except Participants.DoesNotExist: + continue + elif recipient.startswith('user_'): + user_id = recipient.split('_')[1] + try: + user = User.objects.get(id=user_id) + email_addresses.append(user.email) + except User.DoesNotExist: + continue return list(set(email_addresses)) # Remove duplicates - + def get_formatted_message(self): - """Get the formatted message with optional additional information""" - message = self.cleaned_data.get('message', '') + """Get formatted message with optional additional information""" + message = self.cleaned_data.get("message", "") return message @@ -1432,9 +1676,9 @@ class CandidateEmailForm(forms.Form): class InterviewParticpantsForm(forms.ModelForm): participants = forms.ModelMultipleChoiceField( queryset=Participants.objects.all(), - widget=forms.CheckboxSelectMultiple, + widget=forms.CheckboxSelectMultiple, required=False , - + ) system_users=forms.ModelMultipleChoiceField( queryset=User.objects.all(), @@ -1490,10 +1734,10 @@ class InterviewEmailForm(forms.Form): label=_('Message'), required=False ) - + def __init__(self, *args,candidate, external_participants, system_participants,meeting,job,**kwargs): super().__init__(*args, **kwargs) - + # --- Data Preparation --- # Note: Added error handling for agency name if it's missing (though it shouldn't be based on your check) formatted_date = meeting.start_time.strftime('%Y-%m-%d') @@ -1502,11 +1746,11 @@ class InterviewEmailForm(forms.Form): duration = meeting.duration job_title = job.title agency_name = candidate.hiring_agency.name if candidate.belong_to_an_agency and candidate.hiring_agency else "Hiring Agency" - + # --- Combined Participants List for Internal Email --- external_participants_names = ", ".join([p.name for p in external_participants ]) system_participants_names = ", ".join([p.first_name for p in system_participants ]) - + # Combine and ensure no leading/trailing commas if one list is empty participant_names = ", ".join(filter(None, [external_participants_names, system_participants_names])) @@ -1533,7 +1777,7 @@ We look forward to meeting you. Best regards, KAAUH Hiring Team """ - + # --- 2. Agency Message (Professional and clear details) --- agency_message = f""" @@ -1583,14 +1827,14 @@ Thank you for your participation. Best regards, KAAUH HIRING TEAM """ - + # Set initial data self.initial['subject'] = f"Interview Invitation: {job_title} at KAAUH - {candidate.full_name}" # .strip() removes the leading/trailing blank lines caused by the f""" format self.initial['message_for_candidate'] = candidate_message.strip() self.initial['message_for_agency'] = agency_message.strip() self.initial['message_for_participants'] = participants_message.strip() - + @@ -1602,7 +1846,7 @@ KAAUH HIRING TEAM # 'location': forms.TextInput(attrs={'placeholder': 'Enter Interview Location'}), # } - + class OnsiteMeetingForm(forms.ModelForm): class Meta: @@ -1617,6 +1861,6 @@ class OnsiteMeetingForm(forms.ModelForm): attrs={'min': 15, 'placeholder': 'Duration in minutes', 'class': 'form-control'} ), 'location': forms.TextInput(attrs={'placeholder': 'Physical location', 'class': 'form-control'}), - 'timezone': forms.TextInput(attrs={'class': 'form-control'}), + 'timezone': forms.TextInput(attrs={'class': 'form-control'}), 'status': forms.Select(attrs={'class': 'form-control'}), } \ No newline at end of file diff --git a/recruitment/management/commands/translate_po.py b/recruitment/management/commands/translate_po.py new file mode 100644 index 0000000..cb5692f --- /dev/null +++ b/recruitment/management/commands/translate_po.py @@ -0,0 +1,71 @@ +import os +from django.core.management.base import BaseCommand, CommandError +from django.conf import settings +from gpt_po_translator.main import translate_po_files + +class Command(BaseCommand): + help = 'Translates PO files using gpt-po-translator configured with OpenRouter.' + + def add_arguments(self, parser): + parser.add_argument( + '--folder', + type=str, + default=getattr(settings, 'LOCALE_PATHS', ['locale'])[0], + help='Path to the folder containing .po files (default is the first LOCALE_PATHS entry).', + ) + parser.add_argument( + '--lang', + type=str, + help='Comma-separated target language codes (e.g., de,fr,es).', + required=True, + ) + parser.add_argument( + '--model', + type=str, + default='mistralai/mistral-nemo', # Example OpenRouter model + help='The OpenRouter model to use (e.g., openai/gpt-4o, mistralai/mistral-nemo).', + ) + parser.add_argument( + '--bulk', + action='store_true', + help='Enable bulk translation mode for efficiency.', + ) + parser.add_argument( + '--bulksize', + type=int, + default=50, + help='Entries per batch in bulk mode (default: 50).', + ) + + def handle(self, *args, **options): + # --- OpenRouter Configuration --- + # 1. Get API Key from environment variable + api_key = os.environ.get('OPENROUTER_API_KEY') + if not api_key: + raise CommandError("The OPENROUTER_API_KEY environment variable is not set.") + + # 2. Set the base URL for OpenRouter + openrouter_base_url = "https://openrouter.ai/api/v1" + + # 3. Call the core translation function, passing OpenRouter specific config + try: + self.stdout.write(self.style.NOTICE(f"Starting translation with model: {options['model']} via OpenRouter...")) + + translate_po_files( + folder=options['folder'], + lang_codes=options['lang'].split(','), + provider='openai', # gpt-po-translator uses 'openai' provider for OpenAI-compatible APIs + api_key=api_key, + model_name=options['model'], + bulk=options['bulk'], + bulk_size=options['bulksize'], + # Set the base_url for the OpenAI client to point to OpenRouter + base_url=openrouter_base_url, + # OpenRouter often requires a referrer for API usage + extra_headers={"HTTP-Referer": "http://your-django-app.com"}, + ) + + self.stdout.write(self.style.SUCCESS(f"Successfully translated PO files for languages: {options['lang']}")) + + except Exception as e: + raise CommandError(f"An error occurred during translation: {e}") \ No newline at end of file diff --git a/recruitment/migrations/0001_initial.py b/recruitment/migrations/0001_initial.py index 6e83eef..68d8fc2 100644 --- a/recruitment/migrations/0001_initial.py +++ b/recruitment/migrations/0001_initial.py @@ -1,7 +1,10 @@ -# Generated by Django 5.2.7 on 2025-11-05 13:05 +# Generated by Django 5.2.6 on 2025-11-10 14:13 +import django.contrib.auth.models +import django.contrib.auth.validators import django.core.validators import django.db.models.deletion +import django.utils.timezone import django_ckeditor_5.fields import django_countries.fields import django_extensions.db.fields @@ -15,7 +18,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0012_alter_user_first_name_max_length'), ] operations = [ @@ -44,28 +47,6 @@ class Migration(migrations.Migration): 'ordering': ['order'], }, ), - migrations.CreateModel( - name='HiringAgency', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('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')), - ('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(blank=True, max_length=254)), - ('phone', models.CharField(blank=True, max_length=20)), - ('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)), - ('address', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Hiring Agency', - 'verbose_name_plural': 'Hiring Agencies', - 'ordering': ['name'], - }, - ), migrations.CreateModel( name='Participants', fields=[ @@ -137,6 +118,78 @@ class Migration(migrations.Migration): 'abstract': False, }, ), + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('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')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('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')), + ('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, max_length=20, null=True, verbose_name='Phone')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'User', + 'verbose_name_plural': 'Users', + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='Application', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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')), + ('resume', models.FileField(upload_to='resumes/', verbose_name='Resume')), + ('cover_letter', models.FileField(blank=True, null=True, upload_to='cover_letters/', verbose_name='Cover Letter')), + ('is_resume_parsed', models.BooleanField(default=False, verbose_name='Resume Parsed')), + ('parsed_summary', models.TextField(blank=True, verbose_name='Parsed Summary')), + ('applied', models.BooleanField(default=False, verbose_name='Applied')), + ('stage', models.CharField(choices=[('Applied', 'Applied'), ('Exam', 'Exam'), ('Interview', 'Interview'), ('Offer', 'Offer'), ('Hired', 'Hired'), ('Rejected', 'Rejected')], db_index=True, default='Applied', max_length=20, verbose_name='Stage')), + ('applicant_status', models.CharField(blank=True, choices=[('Applicant', 'Applicant'), ('Candidate', 'Candidate')], default='Applicant', max_length=20, null=True, verbose_name='Applicant Status')), + ('exam_date', models.DateTimeField(blank=True, null=True, verbose_name='Exam Date')), + ('exam_status', models.CharField(blank=True, choices=[('Passed', 'Passed'), ('Failed', 'Failed')], max_length=20, null=True, verbose_name='Exam Status')), + ('interview_date', models.DateTimeField(blank=True, null=True, verbose_name='Interview Date')), + ('interview_status', models.CharField(blank=True, choices=[('Passed', 'Passed'), ('Failed', 'Failed')], max_length=20, null=True, verbose_name='Interview Status')), + ('offer_date', models.DateField(blank=True, null=True, verbose_name='Offer Date')), + ('offer_status', models.CharField(blank=True, choices=[('Accepted', 'Accepted'), ('Rejected', 'Rejected'), ('Pending', 'Pending')], max_length=20, null=True, verbose_name='Offer Status')), + ('hired_date', models.DateField(blank=True, null=True, verbose_name='Hired Date')), + ('join_date', models.DateField(blank=True, null=True, verbose_name='Join Date')), + ('ai_analysis_data', models.JSONField(blank=True, default=dict, help_text='Full JSON output from the resume scoring model.', null=True, verbose_name='AI Analysis Data')), + ('retry', models.SmallIntegerField(default=3, verbose_name='Resume Parsing Retry')), + ('hiring_source', models.CharField(blank=True, choices=[('Public', 'Public'), ('Internal', 'Internal'), ('Agency', 'Agency')], default='Public', max_length=255, null=True, verbose_name='Hiring Source')), + ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='application_profile', to=settings.AUTH_USER_MODEL, verbose_name='User Account')), + ], + options={ + 'verbose_name': 'Application', + 'verbose_name_plural': 'Applications', + }, + ), + migrations.CreateModel( + name='Candidate', + fields=[ + ], + options={ + 'verbose_name': 'Candidate (Legacy)', + 'verbose_name_plural': 'Candidates (Legacy)', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('recruitment.application',), + ), migrations.CreateModel( name='FormField', fields=[ @@ -206,42 +259,33 @@ class Migration(migrations.Migration): field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stages', to='recruitment.formtemplate'), ), migrations.CreateModel( - name='Candidate', + name='HiringAgency', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('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')), - ('last_name', models.CharField(max_length=255, verbose_name='Last Name')), - ('email', models.EmailField(db_index=True, max_length=254, verbose_name='Email')), - ('phone', models.CharField(max_length=20, verbose_name='Phone')), - ('address', models.TextField(max_length=200, verbose_name='Address')), - ('resume', models.FileField(upload_to='resumes/', verbose_name='Resume')), - ('is_resume_parsed', models.BooleanField(default=False, verbose_name='Resume Parsed')), - ('is_potential_candidate', models.BooleanField(default=False, verbose_name='Potential Candidate')), - ('parsed_summary', models.TextField(blank=True, verbose_name='Parsed Summary')), - ('applied', models.BooleanField(default=False, verbose_name='Applied')), - ('stage', models.CharField(choices=[('Applied', 'Applied'), ('Exam', 'Exam'), ('Interview', 'Interview'), ('Offer', 'Offer'), ('Hired', 'Hired')], db_index=True, default='Applied', max_length=100, verbose_name='Stage')), - ('applicant_status', models.CharField(blank=True, choices=[('Applicant', 'Applicant'), ('Candidate', 'Candidate')], default='Applicant', max_length=100, null=True, verbose_name='Applicant Status')), - ('exam_date', models.DateTimeField(blank=True, null=True, verbose_name='Exam Date')), - ('exam_status', models.CharField(blank=True, choices=[('Passed', 'Passed'), ('Failed', 'Failed')], max_length=100, null=True, verbose_name='Exam Status')), - ('interview_date', models.DateTimeField(blank=True, null=True, verbose_name='Interview Date')), - ('interview_status', models.CharField(blank=True, choices=[('Passed', 'Passed'), ('Failed', 'Failed')], max_length=100, null=True, verbose_name='Interview Status')), - ('offer_date', models.DateField(blank=True, null=True, verbose_name='Offer Date')), - ('offer_status', models.CharField(blank=True, choices=[('Accepted', 'Accepted'), ('Rejected', 'Rejected')], max_length=100, null=True, verbose_name='Offer Status')), - ('hired_date', models.DateField(blank=True, null=True, verbose_name='Hired Date')), - ('join_date', models.DateField(blank=True, null=True, verbose_name='Join Date')), - ('ai_analysis_data', models.JSONField(default=dict, help_text='Full JSON output from the resume scoring model.', verbose_name='AI Analysis Data')), - ('retry', models.SmallIntegerField(default=3, verbose_name='Resume Parsing Retry')), - ('hiring_source', models.CharField(blank=True, choices=[('Public', 'Public'), ('Internal', 'Internal'), ('Agency', 'Agency')], default='Public', max_length=255, null=True, verbose_name='Hiring Source')), - ('hiring_agency', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='candidates', to='recruitment.hiringagency', verbose_name='Hiring Agency')), + ('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(blank=True, max_length=254)), + ('phone', models.CharField(blank=True, max_length=20)), + ('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)), + ('address', models.TextField(blank=True, null=True)), + ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='agency_profile', to=settings.AUTH_USER_MODEL, verbose_name='User')), ], options={ - 'verbose_name': 'Candidate', - 'verbose_name_plural': 'Candidates', + 'verbose_name': 'Hiring Agency', + 'verbose_name_plural': 'Hiring Agencies', + 'ordering': ['name'], }, ), + migrations.AddField( + model_name='application', + name='hiring_agency', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='applications', to='recruitment.hiringagency', verbose_name='Hiring Agency'), + ), migrations.CreateModel( name='JobPosting', fields=[ @@ -251,8 +295,8 @@ class Migration(migrations.Migration): ('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')), ('title', models.CharField(max_length=200)), ('department', models.CharField(blank=True, max_length=100)), - ('job_type', models.CharField(choices=[('FULL_TIME', 'Full-time'), ('PART_TIME', 'Part-time'), ('CONTRACT', 'Contract'), ('INTERNSHIP', 'Internship'), ('FACULTY', 'Faculty'), ('TEMPORARY', 'Temporary')], default='FULL_TIME', max_length=20)), - ('workplace_type', models.CharField(choices=[('ON_SITE', 'On-site'), ('REMOTE', 'Remote'), ('HYBRID', 'Hybrid')], default='ON_SITE', max_length=20)), + ('job_type', models.CharField(choices=[('Full-time', 'Full-time'), ('Part-time', 'Part-time'), ('Contract', 'Contract'), ('Internship', 'Internship'), ('Faculty', 'Faculty'), ('Temporary', 'Temporary')], default='FULL_TIME', max_length=20)), + ('workplace_type', models.CharField(choices=[('On-site', 'On-site'), ('Remote', 'Remote'), ('Hybrid', 'Hybrid')], default='ON_SITE', max_length=20)), ('location_city', models.CharField(blank=True, max_length=100)), ('location_state', models.CharField(blank=True, max_length=100)), ('location_country', models.CharField(default='Saudia Arabia', max_length=100)), @@ -281,6 +325,7 @@ class Migration(migrations.Migration): ('cancel_reason', models.TextField(blank=True, help_text='Reason for canceling the job posting', verbose_name='Cancel Reason')), ('cancelled_by', models.CharField(blank=True, help_text='Name of person who cancelled this job', max_length=100, verbose_name='Cancelled By')), ('cancelled_at', models.DateTimeField(blank=True, null=True)), + ('assigned_to', models.ForeignKey(blank=True, help_text='The user who has been assigned to this job', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_jobs', to=settings.AUTH_USER_MODEL, verbose_name='Assigned To')), ('hiring_agency', models.ManyToManyField(blank=True, help_text='External agency responsible for sourcing candidates for this role', related_name='jobs', to='recruitment.hiringagency', verbose_name='Hiring Agency')), ('users', models.ManyToManyField(blank=True, help_text='Internal staff involved in the recruitment process for this job', related_name='jobs_assigned', to=settings.AUTH_USER_MODEL, verbose_name='Internal Participant')), ('participants', models.ManyToManyField(blank=True, help_text='External participants involved in the recruitment process for this job', related_name='jobs_participating', to='recruitment.participants', verbose_name='External Participant')), @@ -308,7 +353,7 @@ class Migration(migrations.Migration): ('break_end_time', models.TimeField(blank=True, null=True, verbose_name='Break End Time')), ('interview_duration', models.PositiveIntegerField(verbose_name='Interview Duration (minutes)')), ('buffer_time', models.PositiveIntegerField(default=0, verbose_name='Buffer Time (minutes)')), - ('candidates', models.ManyToManyField(blank=True, null=True, related_name='interview_schedules', to='recruitment.candidate')), + ('candidates', models.ManyToManyField(blank=True, null=True, related_name='interview_schedules', to='recruitment.application')), ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interview_schedules', to='recruitment.jobposting')), ], @@ -319,9 +364,9 @@ class Migration(migrations.Migration): field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='form_template', to='recruitment.jobposting'), ), migrations.AddField( - model_name='candidate', + model_name='application', name='job', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='candidates', to='recruitment.jobposting', verbose_name='Job'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applications', to='recruitment.jobposting', verbose_name='Job'), ), migrations.CreateModel( name='AgencyJobAssignment', @@ -356,6 +401,77 @@ class Migration(migrations.Migration): ('job', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='post_images', to='recruitment.jobposting')), ], ), + migrations.CreateModel( + name='Message', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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')), + ('subject', models.CharField(max_length=200, verbose_name='Subject')), + ('content', models.TextField(verbose_name='Message Content')), + ('message_type', models.CharField(choices=[('direct', 'Direct Message'), ('job_related', 'Job Related'), ('system', 'System Notification')], default='direct', max_length=20, verbose_name='Message Type')), + ('is_read', models.BooleanField(default=False, verbose_name='Is Read')), + ('read_at', models.DateTimeField(blank=True, null=True, verbose_name='Read At')), + ('job', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='recruitment.jobposting', verbose_name='Related Job')), + ('recipient', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='received_messages', to=settings.AUTH_USER_MODEL, verbose_name='Recipient')), + ('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_messages', to=settings.AUTH_USER_MODEL, verbose_name='Sender')), + ], + options={ + 'verbose_name': 'Message', + 'verbose_name_plural': 'Messages', + 'ordering': ['-created_at'], + }, + ), + migrations.CreateModel( + name='Person', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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')), + ('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, help_text='Unique email address for the person', max_length=254, unique=True, verbose_name='Email')), + ('phone', models.CharField(blank=True, max_length=20, 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'), ('O', 'Other'), ('P', 'Prefer not to say')], max_length=1, null=True, verbose_name='Gender')), + ('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')), + ('linkedin_profile', models.URLField(blank=True, null=True, verbose_name='LinkedIn Profile URL')), + ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='person_profile', to=settings.AUTH_USER_MODEL, verbose_name='User Account')), + ], + options={ + 'verbose_name': 'Person', + 'verbose_name_plural': 'People', + }, + ), + migrations.CreateModel( + name='Document', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('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')), + ('file', models.FileField(upload_to='candidate_documents/%Y/%m/', validators=[recruitment.validators.validate_image_size], verbose_name='Document File')), + ('document_type', models.CharField(choices=[('resume', 'Resume'), ('cover_letter', 'Cover Letter'), ('certificate', 'Certificate'), ('id_document', 'ID Document'), ('passport', 'Passport'), ('education', 'Education Document'), ('experience', 'Experience Letter'), ('other', 'Other')], default='other', max_length=20, verbose_name='Document Type')), + ('description', models.CharField(blank=True, max_length=200, verbose_name='Description')), + ('uploaded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Uploaded By')), + ('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='recruitment.person', verbose_name='Person')), + ], + options={ + 'verbose_name': 'Document', + 'verbose_name_plural': 'Documents', + 'ordering': ['-created_at'], + }, + ), + migrations.AddField( + model_name='application', + name='person', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applications', to='recruitment.person', verbose_name='Person'), + ), migrations.CreateModel( name='Profile', fields=[ @@ -435,7 +551,7 @@ class Migration(migrations.Migration): ('status', models.CharField(choices=[('scheduled', 'Scheduled'), ('confirmed', 'Confirmed'), ('cancelled', 'Cancelled'), ('completed', 'Completed')], db_index=True, default='scheduled', max_length=20)), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), - ('candidate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scheduled_interviews', to='recruitment.candidate')), + ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scheduled_interviews', to='recruitment.application')), ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scheduled_interviews', to='recruitment.jobposting')), ('schedule', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interviews', to='recruitment.interviewschedule')), ('zoom_meeting', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='interview', to='recruitment.zoommeeting')), @@ -543,14 +659,6 @@ class Migration(migrations.Migration): model_name='formtemplate', index=models.Index(fields=['is_active'], name='recruitment_is_acti_ae5efb_idx'), ), - migrations.AddIndex( - model_name='candidate', - index=models.Index(fields=['stage'], name='recruitment_stage_f1c6eb_idx'), - ), - migrations.AddIndex( - model_name='candidate', - index=models.Index(fields=['created_at'], name='recruitment_created_73590f_idx'), - ), migrations.AddIndex( model_name='agencyjobassignment', index=models.Index(fields=['agency', 'status'], name='recruitment_agency__491a54_idx'), @@ -571,6 +679,58 @@ class Migration(migrations.Migration): name='agencyjobassignment', unique_together={('agency', 'job')}, ), + migrations.AddIndex( + model_name='message', + index=models.Index(fields=['sender', 'created_at'], name='recruitment_sender__49d984_idx'), + ), + migrations.AddIndex( + model_name='message', + index=models.Index(fields=['recipient', 'is_read', 'created_at'], name='recruitment_recipie_af0e6d_idx'), + ), + migrations.AddIndex( + model_name='message', + index=models.Index(fields=['job', 'created_at'], name='recruitment_job_id_18f813_idx'), + ), + migrations.AddIndex( + model_name='message', + index=models.Index(fields=['message_type', 'created_at'], name='recruitment_message_f25659_idx'), + ), + migrations.AddIndex( + model_name='person', + index=models.Index(fields=['email'], name='recruitment_email_0b1ab1_idx'), + ), + migrations.AddIndex( + model_name='person', + index=models.Index(fields=['first_name', 'last_name'], name='recruitment_first_n_739de5_idx'), + ), + migrations.AddIndex( + model_name='person', + index=models.Index(fields=['created_at'], name='recruitment_created_33495a_idx'), + ), + migrations.AddIndex( + model_name='document', + index=models.Index(fields=['person', 'document_type', 'created_at'], name='recruitment_person__0a6844_idx'), + ), + migrations.AddIndex( + model_name='application', + index=models.Index(fields=['person', 'job'], name='recruitment_person__34355c_idx'), + ), + migrations.AddIndex( + model_name='application', + index=models.Index(fields=['stage'], name='recruitment_stage_52c2d1_idx'), + ), + migrations.AddIndex( + model_name='application', + index=models.Index(fields=['created_at'], name='recruitment_created_80633f_idx'), + ), + migrations.AddIndex( + model_name='application', + index=models.Index(fields=['person', 'stage', 'created_at'], name='recruitment_person__8715ec_idx'), + ), + migrations.AlterUniqueTogether( + name='application', + unique_together={('person', 'job')}, + ), migrations.AddIndex( model_name='jobposting', index=models.Index(fields=['status', 'created_at', 'title'], name='recruitment_status_8b77aa_idx'), @@ -589,7 +749,7 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name='scheduledinterview', - index=models.Index(fields=['candidate', 'job'], name='recruitment_candida_43d5b0_idx'), + index=models.Index(fields=['application', 'job'], name='recruitment_applica_927561_idx'), ), migrations.AddIndex( model_name='notification', diff --git a/recruitment/migrations/0002_delete_candidate_and_more.py b/recruitment/migrations/0002_delete_candidate_and_more.py new file mode 100644 index 0000000..1d47a45 --- /dev/null +++ b/recruitment/migrations/0002_delete_candidate_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 5.2.6 on 2025-11-11 10:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('recruitment', '0001_initial'), + ] + + operations = [ + migrations.DeleteModel( + name='Candidate', + ), + migrations.RenameField( + model_name='interviewschedule', + old_name='candidates', + new_name='applications', + ), + migrations.RemoveField( + model_name='application', + name='user', + ), + ] diff --git a/recruitment/migrations/0003_convert_document_to_generic_fk.py b/recruitment/migrations/0003_convert_document_to_generic_fk.py new file mode 100644 index 0000000..2d3f90c --- /dev/null +++ b/recruitment/migrations/0003_convert_document_to_generic_fk.py @@ -0,0 +1,45 @@ +# Generated by Django 5.2.6 on 2025-11-11 12:13 + +import django.db.models.deletion +import recruitment.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('recruitment', '0002_delete_candidate_and_more'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='document', + name='recruitment_person__0a6844_idx', + ), + migrations.RemoveField( + model_name='document', + name='person', + ), + migrations.AddField( + model_name='document', + name='content_type', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name='Content Type'), + preserve_default=False, + ), + migrations.AddField( + model_name='document', + name='object_id', + field=models.PositiveIntegerField(default=1, verbose_name='Object ID'), + preserve_default=False, + ), + migrations.AlterField( + model_name='document', + name='file', + field=models.FileField(upload_to='documents/%Y/%m/', validators=[recruitment.validators.validate_image_size], verbose_name='Document File'), + ), + migrations.AddIndex( + model_name='document', + index=models.Index(fields=['content_type', 'object_id', 'document_type', 'created_at'], name='recruitment_content_547650_idx'), + ), + ] diff --git a/recruitment/migrations/0004_person_agency.py b/recruitment/migrations/0004_person_agency.py new file mode 100644 index 0000000..24bd305 --- /dev/null +++ b/recruitment/migrations/0004_person_agency.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.6 on 2025-11-12 20:22 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('recruitment', '0003_convert_document_to_generic_fk'), + ] + + operations = [ + migrations.AddField( + model_name='person', + name='agency', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='recruitment.hiringagency', verbose_name='Hiring Agency'), + ), + ] diff --git a/recruitment/models.py b/recruitment/models.py index bd25ec7..fab3793 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -1,11 +1,12 @@ from django.db import models from django.urls import reverse -from typing import List,Dict,Any +from typing import List, Dict, Any from django.utils import timezone -from django.db.models import FloatField,CharField,IntegerField -from django.db.models.functions import Cast,Coalesce +from django.db.models import FloatField, CharField, IntegerField +from django.db.models.functions import Cast, Coalesce from django.db.models import F -from django.contrib.auth.models import User +from django.contrib.auth.models import AbstractUser +from django.contrib.auth import get_user_model from django.core.validators import URLValidator from django_countries.fields import CountryField from django.core.exceptions import ValidationError @@ -14,6 +15,33 @@ from django.utils.html import strip_tags from django.utils.translation import gettext_lazy as _ from django_extensions.db.fields import RandomCharField from .validators import validate_hash_tags, validate_image_size +from django.contrib.auth.models import AbstractUser +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType + + +class CustomUser(AbstractUser): + """Custom user model extending AbstractUser""" + + USER_TYPES = [ + ("staff", _("Staff")), + ("agency", _("Agency")), + ("candidate", _("Candidate")), + ] + + user_type = models.CharField( + max_length=20, choices=USER_TYPES, default="staff", verbose_name=_("User Type") + ) + phone = models.CharField( + max_length=20, blank=True, null=True, verbose_name=_("Phone") + ) + + class Meta: + verbose_name = _("User") + verbose_name_plural = _("Users") + + +User = get_user_model() class Base(models.Model): @@ -26,10 +54,18 @@ class Base(models.Model): class Meta: abstract = True + class Profile(models.Model): - profile_image = models.ImageField(null=True, blank=True, upload_to="profile_pic/",validators=[validate_image_size]) - designation = models.CharField(max_length=100, blank=True,null=True) - phone=models.CharField(blank=True,null=True,verbose_name=_("Phone Number"),max_length=12) + profile_image = models.ImageField( + null=True, + blank=True, + upload_to="profile_pic/", + validators=[validate_image_size], + ) + designation = models.CharField(max_length=100, blank=True, null=True) + phone = models.CharField( + blank=True, null=True, verbose_name=_("Phone Number"), max_length=12 + ) user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile") def __str__(self): @@ -40,18 +76,18 @@ class JobPosting(Base): # Basic Job Information JOB_TYPES = [ - ("FULL_TIME", "Full-time"), - ("PART_TIME", "Part-time"), - ("CONTRACT", "Contract"), - ("INTERNSHIP", "Internship"), - ("FACULTY", "Faculty"), - ("TEMPORARY", "Temporary"), + (_("Full-time"), _("Full-time")), + (_("Part-time"), _("Part-time")), + (_("Contract"), _("Contract")), + (_("Internship"), _("Internship")), + (_("Faculty"), _("Faculty")), + (_("Temporary"), _("Temporary")), ] WORKPLACE_TYPES = [ - ("ON_SITE", "On-site"), - ("REMOTE", "Remote"), - ("HYBRID", "Hybrid"), + (_("On-site"), _("On-site")), + (_("Remote"), _("Remote")), + (_("Hybrid"), _("Hybrid")), ] # users=models.ManyToManyField( @@ -82,17 +118,15 @@ class JobPosting(Base): # Job Details description = CKEditor5Field( - 'Description', - config_name='extends' # Matches the config name you defined in settings.py + "Description", + config_name="extends", # Matches the config name you defined in settings.py ) - qualifications = CKEditor5Field(blank=True, null=True, - config_name='extends' - ) + qualifications = CKEditor5Field(blank=True, null=True, config_name="extends") salary_range = models.CharField( max_length=200, blank=True, help_text="e.g., $60,000 - $80,000" ) - benefits = CKEditor5Field(blank=True, null=True, config_name='extends') + benefits = CKEditor5Field(blank=True, null=True, config_name="extends") # Application Information ---job detail apply link for the candidates application_url = models.URLField( @@ -104,7 +138,7 @@ class JobPosting(Base): application_deadline = models.DateField(db_index=True) # Added index application_instructions = CKEditor5Field( - blank=True, null=True, config_name='extends' + blank=True, null=True, config_name="extends" ) # Internal Tracking @@ -123,7 +157,10 @@ class JobPosting(Base): ("ARCHIVED", "Archived"), ] status = models.CharField( - db_index=True, max_length=20, choices=STATUS_CHOICES, default="DRAFT" # Added index + db_index=True, + max_length=20, + choices=STATUS_CHOICES, + default="DRAFT", # Added index ) # hashtags for social media @@ -146,9 +183,11 @@ class JobPosting(Base): max_length=50, blank=True, help_text="Status of LinkedIn posting" ) linkedin_posted_at = models.DateTimeField(null=True, blank=True) - linkedin_post_formated_data=models.TextField(null=True,blank=True) + linkedin_post_formated_data = models.TextField(null=True, blank=True) - published_at = models.DateTimeField(db_index=True, null=True, blank=True) # Added index + published_at = models.DateTimeField( + db_index=True, null=True, blank=True + ) # Added index # University Specific Fields position_number = models.CharField( max_length=50, blank=True, help_text="University position number" @@ -168,10 +207,13 @@ class JobPosting(Base): null=True, blank=True, help_text="The system or channel from which this job posting originated or was first published.", - db_index=True # Explicitly index ForeignKey + db_index=True, # Explicitly index ForeignKey ) max_applications = models.PositiveIntegerField( - default=1000, help_text="Maximum number of applications allowed", null=True, blank=True + default=1000, + help_text="Maximum number of applications allowed", + null=True, + blank=True, ) hiring_agency = models.ManyToManyField( "HiringAgency", @@ -194,14 +236,23 @@ class JobPosting(Base): verbose_name=_("Cancelled By"), ) cancelled_at = models.DateTimeField(null=True, blank=True) + assigned_to = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="assigned_jobs", + help_text=_("The user who has been assigned to this job"), + verbose_name=_("Assigned To"), + ) class Meta: ordering = ["-created_at"] verbose_name = "Job Posting" verbose_name_plural = "Job Postings" indexes = [ - models.Index(fields=['status', 'created_at', 'title']), - models.Index(fields=['slug']), + models.Index(fields=["status", "created_at", "title"]), + models.Index(fields=["slug"]), ] def __str__(self): @@ -220,9 +271,8 @@ class JobPosting(Base): year = timezone.now().year # Get next sequential number last_job = ( - JobPosting.objects.select_for_update().filter( - internal_job_id__startswith=f"{prefix}-{year}-" - ) + JobPosting.objects.select_for_update() + .filter(internal_job_id__startswith=f"{prefix}-{year}-") .order_by("internal_job_id") .last() ) @@ -269,7 +319,7 @@ class JobPosting(Base): return False # 1. Replace the common HTML non-breaking space entity with a standard space. - content = field_value.replace(' ', ' ') + content = field_value.replace(" ", " ") # 2. Remove all HTML tags (leaving only text and remaining spaces). stripped = strip_tags(content) @@ -302,7 +352,7 @@ class JobPosting(Base): @property def current_applications_count(self): """Returns the current number of candidates associated with this job.""" - return self.candidates.count() + return self.applications.count() @property def is_application_limit_reached(self): @@ -314,9 +364,15 @@ class JobPosting(Base): @property def all_candidates(self): - return self.candidates.annotate( - sortable_score=Coalesce(Cast('ai_analysis_data__analysis_data__match_score', output_field=IntegerField()), - 0)).order_by('-sortable_score') + return self.applications.annotate( + sortable_score=Coalesce( + Cast( + "ai_analysis_data__analysis_data__match_score", + output_field=IntegerField(), + ), + 0, + ) + ).order_by("-sortable_score") @property def screening_candidates(self): @@ -333,9 +389,11 @@ class JobPosting(Base): @property def offer_candidates(self): return self.all_candidates.filter(stage="Offer") + @property def accepted_candidates(self): return self.all_candidates.filter(offer_status="Accepted") + @property def hired_candidates(self): return self.all_candidates.filter(stage="Hired") @@ -343,9 +401,16 @@ class JobPosting(Base): # counts @property def all_candidates_count(self): - return self.candidates.annotate( - sortable_score=Cast('ai_analysis_data__match_score', output_field=CharField())).order_by( - '-sortable_score').count() or 0 + return ( + self.candidates.annotate( + sortable_score=Cast( + "ai_analysis_data__match_score", output_field=CharField() + ) + ) + .order_by("-sortable_score") + .count() + or 0 + ) @property def screening_candidates_count(self): @@ -371,7 +436,7 @@ class JobPosting(Base): def vacancy_fill_rate(self): total_positions = self.open_positions - no_of_positions_filled = self.candidates.filter(stage__in=['HIRED']).count() + no_of_positions_filled = self.applications.filter(stage__in=["HIRED"]).count() if total_positions > 0: vacancy_fill_rate = no_of_positions_filled / total_positions @@ -381,27 +446,128 @@ class JobPosting(Base): return vacancy_fill_rate - class JobPostingImage(models.Model): - job=models.OneToOneField('JobPosting',on_delete=models.CASCADE,related_name='post_images') - post_image = models.ImageField(upload_to='post/',validators=[validate_image_size]) + job = models.OneToOneField( + "JobPosting", on_delete=models.CASCADE, related_name="post_images" + ) + post_image = models.ImageField(upload_to="post/", validators=[validate_image_size]) -class Candidate(Base): +class Person(Base): + """Model to store personal information that can be reused across multiple applications""" + + GENDER_CHOICES = [ + ("M", _("Male")), + ("F", _("Female")), + ("O", _("Other")), + ("P", _("Prefer not to say")), + ] + + # Personal Information + first_name = models.CharField(max_length=255, verbose_name=_("First Name")) + last_name = models.CharField(max_length=255, verbose_name=_("Last Name")) + middle_name = models.CharField(max_length=255, blank=True, null=True, verbose_name=_("Middle Name")) + email = models.EmailField( + unique=True, + db_index=True, + verbose_name=_("Email"), + help_text=_("Unique email address for the person") + ) + phone = models.CharField(max_length=20, blank=True, null=True, verbose_name=_("Phone")) + date_of_birth = models.DateField(null=True, blank=True, verbose_name=_("Date of Birth")) + gender = models.CharField( + max_length=1, + choices=GENDER_CHOICES, + blank=True, + null=True, + verbose_name=_("Gender") + ) + nationality = CountryField(blank=True, null=True, verbose_name=_("Nationality")) + address = models.TextField(blank=True, null=True, verbose_name=_("Address")) + + # Optional linking to user account + user = models.OneToOneField( + User, + on_delete=models.SET_NULL, + related_name="person_profile", + verbose_name=_("User Account"), + null=True, + blank=True, + ) + + # Profile information + profile_image = models.ImageField( + null=True, + blank=True, + upload_to="profile_pic/", + validators=[validate_image_size], + verbose_name=_("Profile Image") + ) + linkedin_profile = models.URLField( + blank=True, + null=True, + verbose_name=_("LinkedIn Profile URL") + ) + agency = models.ForeignKey( + "HiringAgency", + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name=_("Hiring Agency") + ) + class Meta: + verbose_name = _("Person") + verbose_name_plural = _("People") + indexes = [ + models.Index(fields=["email"]), + models.Index(fields=["first_name", "last_name"]), + models.Index(fields=["created_at"]), + ] + + def __str__(self): + return f"{self.first_name} {self.last_name}" + + @property + def full_name(self): + return f"{self.first_name} {self.last_name}" + + @property + def age(self): + """Calculate age from date of birth""" + if self.date_of_birth: + today = timezone.now().date() + return today.year - self.date_of_birth.year - ( + (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day) + ) + return None + + @property + def documents(self): + """Return all documents associated with this Person""" + from django.contrib.contenttypes.models import ContentType + content_type = ContentType.objects.get_for_model(self.__class__) + return Document.objects.filter(content_type=content_type, object_id=self.id) + + +class Application(Base): + """Model to store job-specific application data""" + class Stage(models.TextChoices): APPLIED = "Applied", _("Applied") EXAM = "Exam", _("Exam") INTERVIEW = "Interview", _("Interview") OFFER = "Offer", _("Offer") HIRED = "Hired", _("Hired") + REJECTED = "Rejected", _("Rejected") class ExamStatus(models.TextChoices): PASSED = "Passed", _("Passed") FAILED = "Failed", _("Failed") - class Status(models.TextChoices): + class OfferStatus(models.TextChoices): ACCEPTED = "Accepted", _("Accepted") REJECTED = "Rejected", _("Rejected") + PENDING = "Pending", _("Pending") class ApplicantType(models.TextChoices): APPLICANT = "Applicant", _("Applicant") @@ -409,35 +575,50 @@ class Candidate(Base): # Stage transition validation constants STAGE_SEQUENCE = { - "Applied": ["Exam", "Interview", "Offer"], - "Exam": ["Interview", "Offer"], - "Interview": ["Offer"], - "Offer": [], # Final stage - no further transitions + "Applied": ["Exam", "Interview", "Offer", "Rejected"], + "Exam": ["Interview", "Offer", "Rejected"], + "Interview": ["Offer", "Rejected"], + "Offer": ["Hired", "Rejected"], + "Rejected": [], # Final stage - no further transitions + "Hired": [], # Final stage - no further transitions } + # Core relationships + person = models.ForeignKey( + Person, + on_delete=models.CASCADE, + related_name="applications", + verbose_name=_("Person"), + ) job = models.ForeignKey( JobPosting, on_delete=models.CASCADE, - related_name="candidates", + related_name="applications", verbose_name=_("Job"), ) - first_name = models.CharField(max_length=255, verbose_name=_("First Name")) - last_name = models.CharField(max_length=255, verbose_name=_("Last Name")) - email = models.EmailField(db_index=True, verbose_name=_("Email")) # Added index - phone = models.CharField(max_length=20, verbose_name=_("Phone")) - address = models.TextField(max_length=200, verbose_name=_("Address")) + # Application-specific data resume = models.FileField(upload_to="resumes/", verbose_name=_("Resume")) + cover_letter = models.FileField( + upload_to="cover_letters/", + blank=True, + null=True, + verbose_name=_("Cover Letter") + ) is_resume_parsed = models.BooleanField( - default=False, verbose_name=_("Resume Parsed") + default=False, + verbose_name=_("Resume Parsed") ) - is_potential_candidate = models.BooleanField( - default=False, verbose_name=_("Potential Candidate") + parsed_summary = models.TextField( + blank=True, + verbose_name=_("Parsed Summary") ) - parsed_summary = models.TextField(blank=True, verbose_name=_("Parsed Summary")) + + # Workflow fields applied = models.BooleanField(default=False, verbose_name=_("Applied")) stage = models.CharField( - db_index=True, max_length=100, # Added index + db_index=True, + max_length=20, default="Applied", choices=Stage.choices, verbose_name=_("Stage"), @@ -445,15 +626,17 @@ class Candidate(Base): applicant_status = models.CharField( choices=ApplicantType.choices, default="Applicant", - max_length=100, + max_length=20, null=True, blank=True, verbose_name=_("Applicant Status"), ) + + # Timeline fields exam_date = models.DateTimeField(null=True, blank=True, verbose_name=_("Exam Date")) exam_status = models.CharField( choices=ExamStatus.choices, - max_length=100, + max_length=20, null=True, blank=True, verbose_name=_("Exam Status"), @@ -463,34 +646,46 @@ class Candidate(Base): ) interview_status = models.CharField( choices=ExamStatus.choices, - max_length=100, + max_length=20, null=True, blank=True, verbose_name=_("Interview Status"), ) offer_date = models.DateField(null=True, blank=True, verbose_name=_("Offer Date")) offer_status = models.CharField( - choices=Status.choices, - max_length=100, + choices=OfferStatus.choices, + max_length=20, null=True, blank=True, verbose_name=_("Offer Status"), ) hired_date = models.DateField(null=True, blank=True, verbose_name=_("Hired Date")) join_date = models.DateField(null=True, blank=True, verbose_name=_("Join Date")) + + # AI Analysis ai_analysis_data = models.JSONField( verbose_name="AI Analysis Data", default=dict, - help_text="Full JSON output from the resume scoring model." - )# {'resume_data': {}, 'analysis_data': {}} + help_text="Full JSON output from the resume scoring model.", + null=True, + blank=True, + ) + retry = models.SmallIntegerField( + verbose_name="Resume Parsing Retry", + default=3 + ) - retry = models.SmallIntegerField(verbose_name="Resume Parsing Retry",default=3) + # Source tracking hiring_source = models.CharField( max_length=255, null=True, blank=True, verbose_name=_("Hiring Source"), - choices=[("Public", "Public"), ("Internal", "Internal"), ("Agency", "Agency")], + choices=[ + (_("Public"), _("Public")), + (_("Internal"), _("Internal")), + (_("Agency"), _("Agency")), + ], default="Public", ) hiring_agency = models.ForeignKey( @@ -498,224 +693,264 @@ class Candidate(Base): on_delete=models.SET_NULL, null=True, blank=True, - related_name="candidates", + related_name="applications", verbose_name=_("Hiring Agency"), ) + # Optional linking to user account (for candidate portal access) + # user = models.OneToOneField( + # User, + # on_delete=models.SET_NULL, + # related_name="application_profile", + # verbose_name=_("User Account"), + # null=True, + # blank=True, + # ) + class Meta: - verbose_name = _("Candidate") - verbose_name_plural = _("Candidates") + verbose_name = _("Application") + verbose_name_plural = _("Applications") indexes = [ - models.Index(fields=['stage']), - models.Index(fields=['created_at']), + models.Index(fields=["person", "job"]), + models.Index(fields=["stage"]), + models.Index(fields=["created_at"]), + models.Index(fields=["person", "stage", "created_at"]), ] - def set_field(self, key: str, value: Any): - """ - Generic method to set any single key-value pair and save. - """ - self.ai_analysis_data[key] = value - # self.save(update_fields=['ai_analysis_data']) + unique_together = [["person", "job"]] # Prevent duplicate applications + + def __str__(self): + return f"{self.person.full_name} - {self.job.title}" # ==================================================================== - # ✨ PROPERTIES (GETTERS) + # ✨ PROPERTIES (GETTERS) - Migrated from Candidate # ==================================================================== @property def resume_data(self): - return self.ai_analysis_data.get('resume_data', {}) + return self.ai_analysis_data.get("resume_data", {}) + @property def analysis_data(self): - return self.ai_analysis_data.get('analysis_data', {}) + return self.ai_analysis_data.get("analysis_data", {}) + @property def match_score(self) -> int: """1. A score from 0 to 100 representing how well the candidate fits the role.""" - return self.analysis_data.get('match_score', 0) + return self.analysis_data.get("match_score", 0) @property def years_of_experience(self) -> float: """4. The total number of years of professional experience as a numerical value.""" - return self.analysis_data.get('years_of_experience', 0.0) + return self.analysis_data.get("years_of_experience", 0.0) @property def soft_skills_score(self) -> int: """15. A score (0-100) for inferred non-technical skills.""" - return self.analysis_data.get('soft_skills_score', 0) + return self.analysis_data.get("soft_skills_score", 0) @property def industry_match_score(self) -> int: """16. A score (0-100) for the relevance of the candidate's industry experience.""" - # Renamed to clarify: experience_industry_match - return self.analysis_data.get('experience_industry_match', 0) - - # --- Properties for Funnel & Screening Efficiency --- + return self.analysis_data.get("experience_industry_match", 0) @property def min_requirements_met(self) -> bool: """14. Boolean (true/false) indicating if all non-negotiable minimum requirements are met.""" - return self.analysis_data.get('min_req_met_bool', False) + return self.analysis_data.get("min_req_met_bool", False) @property def screening_stage_rating(self) -> str: """13. A standardized rating (e.g., "A - Highly Qualified", "B - Qualified").""" - return self.analysis_data.get('screening_stage_rating', 'N/A') + return self.analysis_data.get("screening_stage_rating", "N/A") @property def top_3_keywords(self) -> List[str]: """10. A list of the three most dominant and relevant technical skills or technologies.""" - return self.analysis_data.get('top_3_keywords', []) + return self.analysis_data.get("top_3_keywords", []) @property def most_recent_job_title(self) -> str: """8. The candidate's most recent or current professional job title.""" - return self.analysis_data.get('most_recent_job_title', 'N/A') - - # --- Properties for Structured Detail --- + return self.analysis_data.get("most_recent_job_title", "N/A") @property def criteria_checklist(self) -> Dict[str, str]: """5 & 6. An object rating the candidate's match for each specific criterion.""" - return self.analysis_data.get('criteria_checklist', {}) + return self.analysis_data.get("criteria_checklist", {}) @property def professional_category(self) -> str: """7. The most fitting professional field or category for the individual.""" - return self.analysis_data.get('category', 'N/A') + return self.analysis_data.get("category", "N/A") @property def language_fluency(self) -> List[Dict[str, str]]: """12. A list of languages and their fluency levels mentioned.""" - return self.analysis_data.get('language_fluency', []) - - # --- Properties for Summaries and Narrative --- + return self.analysis_data.get("language_fluency", []) @property def strengths(self) -> str: """2. A brief summary of why the candidate is a strong fit.""" - return self.analysis_data.get('strengths', '') + return self.analysis_data.get("strengths", "") @property def weaknesses(self) -> str: """3. A brief summary of where the candidate falls short or what criteria are missing.""" - return self.analysis_data.get('weaknesses', '') + return self.analysis_data.get("weaknesses", "") @property def job_fit_narrative(self) -> str: """11. A single, concise sentence summarizing the core fit.""" - return self.analysis_data.get('job_fit_narrative', '') + return self.analysis_data.get("job_fit_narrative", "") @property def recommendation(self) -> str: """9. Provide a detailed final recommendation for the candidate.""" - # Using a more descriptive name to avoid conflict with potential built-in methods - return self.analysis_data.get('recommendation', '') + return self.analysis_data.get("recommendation", "") - @property - def name(self): - return f"{self.first_name} {self.last_name}" - - @property - def full_name(self): - return self.name - - @property - def get_file_size(self): - if self.resume: - return self.resume.size - return 0 - - - def save(self, *args, **kwargs): - """Override save to ensure validation is called""" - self.clean() # Call validation before saving - super().save(*args, **kwargs) + # ==================================================================== + # 🔄 HELPER METHODS + # ==================================================================== + def set_field(self, key: str, value: Any): + """Generic method to set any single key-value pair and save.""" + self.ai_analysis_data[key] = value def get_available_stages(self): - """Get list of stages this candidate can transition to""" + """Get list of stages this application can transition to""" if not self.pk: # New record return ["Applied"] old_stage = self.__class__.objects.get(pk=self.pk).stage return self.STAGE_SEQUENCE.get(old_stage, []) + def save(self, *args, **kwargs): + """Override save to ensure validation is called""" + self.clean() # Call validation before saving + super().save(*args, **kwargs) + + # ==================================================================== + # 📋 LEGACY COMPATIBILITY PROPERTIES + # ==================================================================== + # These properties maintain compatibility with existing code that expects Candidate model + @property + def first_name(self): + """Legacy compatibility - delegates to person.first_name""" + return self.person.first_name + + @property + def last_name(self): + """Legacy compatibility - delegates to person.last_name""" + return self.person.last_name + + @property + def email(self): + """Legacy compatibility - delegates to person.email""" + return self.person.email + + @property + def phone(self): + """Legacy compatibility - delegates to person.phone if available""" + return self.person.phone or "" + + @property + def address(self): + """Legacy compatibility - delegates to person.address if available""" + return self.person.address or "" + + @property + def name(self): + """Legacy compatibility - delegates to person.full_name""" + return self.person.full_name + + @property + def full_name(self): + """Legacy compatibility - delegates to person.full_name""" + return self.person.full_name + + @property + def get_file_size(self): + """Legacy compatibility - returns resume file size""" + if self.resume: + return self.resume.size + return 0 + @property def submission(self): + """Legacy compatibility - get form submission for this application""" return FormSubmission.objects.filter(template__job=self.job).first() + @property def responses(self): + """Legacy compatibility - get form responses for this application""" if self.submission: return self.submission.responses.all() return [] - def __str__(self): - return self.full_name @property def get_meetings(self): + """Legacy compatibility - get scheduled interviews for this application""" return self.scheduled_interviews.all() + @property def get_latest_meeting(self): - schedule = self.scheduled_interviews.order_by('-created_at').first() + """Legacy compatibility - get latest meeting for this application""" + schedule = self.scheduled_interviews.order_by("-created_at").first() if schedule: return schedule.zoom_meeting return None @property def has_future_meeting(self): - """ - Checks if the candidate has any scheduled interviews for a future date/time. - """ - # Ensure timezone.now() is used for comparison + """Legacy compatibility - check for future meetings""" now = timezone.now() - # Check if any related ScheduledInterview has a future interview_date and interview_time - # We need to combine date and time for a proper datetime comparison if they are separate fields - future_meetings = self.scheduled_interviews.filter( - interview_date__gt=now.date() - ).filter( - interview_time__gte=now.time() - ).exists() - - # Also check for interviews happening later today + future_meetings = ( + self.scheduled_interviews.filter(interview_date__gt=now.date()) + .filter(interview_time__gte=now.time()) + .exists() + ) today_future_meetings = self.scheduled_interviews.filter( - interview_date=now.date(), - interview_time__gte=now.time() + interview_date=now.date(), interview_time__gte=now.time() ).exists() - return future_meetings or today_future_meetings - + @property def scoring_timeout(self): + """Legacy compatibility - check scoring timeout""" return timezone.now() <= (self.created_at + timezone.timedelta(minutes=5)) - + @property def get_interview_date(self): - if hasattr(self, 'scheduled_interview') and self.scheduled_interview: - return self.scheduled_interviews.first().interview_date + """Legacy compatibility - get interview date""" + if hasattr(self, "scheduled_interview") and self.scheduled_interview: + return self.scheduled_interviews.first().interview_date return None - - - - - + @property def get_interview_time(self): - if hasattr(self, 'scheduled_interview') and self.scheduled_interview: - return self.scheduled_interviews.first().interview_time - return None - - + """Legacy compatibility - get interview time""" + if hasattr(self, "scheduled_interview") and self.scheduled_interview: + return self.scheduled_interviews.first().interview_time + return None + @property def time_to_hire_days(self): + """Legacy compatibility - calculate time to hire""" if self.hired_date and self.created_at: time_to_hire = self.hired_date - self.created_at.date() return time_to_hire.days return 0 - + @property - def belong_to_an_agency(self): - return self.hiring_source=='Agency' - + def documents(self): + """Return all documents associated with this Application""" + from django.contrib.contenttypes.models import ContentType + content_type = ContentType.objects.get_for_model(self.__class__) + return Document.objects.filter(content_type=content_type, object_id=self.id) class TrainingMaterial(Base): title = models.CharField(max_length=255, verbose_name=_("Title")) - content = CKEditor5Field(blank=True, verbose_name=_("Content"),config_name='extends') + content = CKEditor5Field( + blank=True, verbose_name=_("Content"), config_name="extends" + ) video_link = models.URLField(blank=True, verbose_name=_("Video Link")) file = models.FileField( upload_to="training_materials/", blank=True, verbose_name=_("File") @@ -758,14 +993,19 @@ class ZoomMeeting(Base): WAITING = "waiting", _("Waiting") STARTED = "started", _("Started") ENDED = "ended", _("Ended") - CANCELLED = "cancelled",_("Cancelled") + CANCELLED = "cancelled", _("Cancelled") + # Basic meeting details topic = models.CharField(max_length=255, verbose_name=_("Topic")) meeting_id = models.CharField( - db_index=True, max_length=20, unique=True, verbose_name=_("Meeting ID") # Added index - + db_index=True, + max_length=20, + unique=True, + verbose_name=_("Meeting ID"), # Added index ) # Unique identifier for the meeting - start_time = models.DateTimeField(db_index=True, verbose_name=_("Start Time")) # Added index + start_time = models.DateTimeField( + db_index=True, verbose_name=_("Start Time") + ) # Added index duration = models.PositiveIntegerField( verbose_name=_("Duration") ) # Duration in minutes @@ -791,7 +1031,8 @@ class ZoomMeeting(Base): blank=True, null=True, verbose_name=_("Zoom Gateway Response") ) status = models.CharField( - db_index=True, max_length=20, # Added index + db_index=True, + max_length=20, # Added index null=True, blank=True, verbose_name=_("Status"), @@ -802,44 +1043,49 @@ class ZoomMeeting(Base): def __str__(self): return self.topic @property + def get_job(self): return self.interview.job + @property def get_candidate(self): - return self.interview.candidate + return self.interview.application.person + @property + def candidate_full_name(self): + return self.interview.application.person.full_name + @property def get_participants(self): - return self.interview.participants.all() + return self.interview.job.participants.all() + @property def get_users(self): - return self.interview.system_users.all() + return self.interview.job.users.all() class MeetingComment(Base): """ Model for storing meeting comments/notes """ + meeting = models.ForeignKey( ZoomMeeting, on_delete=models.CASCADE, related_name="comments", - verbose_name=_("Meeting") + verbose_name=_("Meeting"), ) author = models.ForeignKey( User, on_delete=models.CASCADE, related_name="meeting_comments", - verbose_name=_("Author") - ) - content = CKEditor5Field( - verbose_name=_("Content"), - config_name='extends' + verbose_name=_("Author"), ) + content = CKEditor5Field(verbose_name=_("Content"), config_name="extends") # Inherited from Base: created_at, updated_at, slug class Meta: verbose_name = _("Meeting Comment") verbose_name_plural = _("Meeting Comments") - ordering = ['-created_at'] + ordering = ["-created_at"] def __str__(self): return f"Comment by {self.author.get_username()} on {self.meeting.topic}" @@ -851,14 +1097,22 @@ class FormTemplate(Base): """ job = models.OneToOneField( - JobPosting, on_delete=models.CASCADE, related_name="form_template", db_index=True + JobPosting, + on_delete=models.CASCADE, + related_name="form_template", + db_index=True, ) name = models.CharField(max_length=200, help_text="Name of the form template") description = models.TextField( blank=True, help_text="Description of the form template" ) created_by = models.ForeignKey( - User, on_delete=models.CASCADE, related_name="form_templates",null=True,blank=True, db_index=True + User, + on_delete=models.CASCADE, + related_name="form_templates", + null=True, + blank=True, + db_index=True, ) is_active = models.BooleanField( default=False, help_text="Whether this template is active" @@ -869,8 +1123,8 @@ class FormTemplate(Base): verbose_name = "Form Template" verbose_name_plural = "Form Templates" indexes = [ - models.Index(fields=['created_at']), - models.Index(fields=['is_active']), + models.Index(fields=["created_at"]), + models.Index(fields=["is_active"]), ] def __str__(self): @@ -1021,7 +1275,10 @@ class FormSubmission(Base): """ template = models.ForeignKey( - FormTemplate, on_delete=models.CASCADE, related_name="submissions", db_index=True + FormTemplate, + on_delete=models.CASCADE, + related_name="submissions", + db_index=True, ) submitted_by = models.ForeignKey( User, @@ -1029,20 +1286,22 @@ class FormSubmission(Base): null=True, blank=True, related_name="form_submissions", - db_index=True + db_index=True, ) - submitted_at = models.DateTimeField(db_index=True, auto_now_add=True) # Added index + submitted_at = models.DateTimeField(db_index=True, auto_now_add=True) # Added index applicant_name = models.CharField( max_length=200, blank=True, help_text="Name of the applicant" ) - applicant_email = models.EmailField(db_index=True, blank=True, help_text="Email of the applicant") # Added index + applicant_email = models.EmailField( + db_index=True, blank=True, help_text="Email of the applicant" + ) # Added index class Meta: ordering = ["-submitted_at"] verbose_name = "Form Submission" verbose_name_plural = "Form Submissions" indexes = [ - models.Index(fields=['submitted_at']), + models.Index(fields=["submitted_at"]), ] def __str__(self): @@ -1055,7 +1314,10 @@ class FieldResponse(Base): """ submission = models.ForeignKey( - FormSubmission, on_delete=models.CASCADE, related_name="responses", db_index=True + FormSubmission, + on_delete=models.CASCADE, + related_name="responses", + db_index=True, ) field = models.ForeignKey( FormField, on_delete=models.CASCADE, related_name="responses", db_index=True @@ -1073,8 +1335,8 @@ class FieldResponse(Base): verbose_name = "Field Response" verbose_name_plural = "Field Responses" indexes = [ - models.Index(fields=['submission']), - models.Index(fields=['field']), + models.Index(fields=["submission"]), + models.Index(fields=["field"]), ] def __str__(self): @@ -1320,6 +1582,14 @@ class IntegrationLog(Base): class HiringAgency(Base): + user = models.OneToOneField( + User, + on_delete=models.CASCADE, + related_name="agency_profile", + verbose_name=_("User"), + null=True, + blank=True, + ) name = models.CharField(max_length=200, unique=True, verbose_name=_("Agency Name")) contact_person = models.CharField( max_length=150, blank=True, verbose_name=_("Contact Person") @@ -1353,31 +1623,33 @@ class AgencyJobAssignment(Base): HiringAgency, on_delete=models.CASCADE, related_name="job_assignments", - verbose_name=_("Agency") + verbose_name=_("Agency"), ) job = models.ForeignKey( JobPosting, on_delete=models.CASCADE, related_name="agency_assignments", - verbose_name=_("Job") + verbose_name=_("Job"), ) # Limits & Controls max_candidates = models.PositiveIntegerField( verbose_name=_("Maximum Candidates"), - help_text=_("Maximum candidates agency can submit for this job") + help_text=_("Maximum candidates agency can submit for this job"), ) candidates_submitted = models.PositiveIntegerField( default=0, verbose_name=_("Candidates Submitted"), - help_text=_("Number of candidates submitted so far") + help_text=_("Number of candidates submitted so far"), ) # Timeline - assigned_date = models.DateTimeField(auto_now_add=True, verbose_name=_("Assigned Date")) + assigned_date = models.DateTimeField( + auto_now_add=True, verbose_name=_("Assigned Date") + ) deadline_date = models.DateTimeField( verbose_name=_("Deadline Date"), - help_text=_("Deadline for agency to submit candidates") + help_text=_("Deadline for agency to submit candidates"), ) # Status & Extensions @@ -1386,26 +1658,25 @@ class AgencyJobAssignment(Base): max_length=20, choices=AssignmentStatus.choices, default=AssignmentStatus.ACTIVE, - verbose_name=_("Status") + verbose_name=_("Status"), ) # Extension tracking deadline_extended = models.BooleanField( - default=False, - verbose_name=_("Deadline Extended") + default=False, verbose_name=_("Deadline Extended") ) original_deadline = models.DateTimeField( null=True, blank=True, verbose_name=_("Original Deadline"), - help_text=_("Original deadline before extensions") + help_text=_("Original deadline before extensions"), ) # Admin notes admin_notes = models.TextField( blank=True, verbose_name=_("Admin Notes"), - help_text=_("Internal notes about this assignment") + help_text=_("Internal notes about this assignment"), ) class Meta: @@ -1413,12 +1684,12 @@ class AgencyJobAssignment(Base): verbose_name_plural = _("Agency Job Assignments") ordering = ["-created_at"] indexes = [ - models.Index(fields=['agency', 'status']), - models.Index(fields=['job', 'status']), - models.Index(fields=['deadline_date']), - models.Index(fields=['is_active']), + models.Index(fields=["agency", "status"]), + models.Index(fields=["job", "status"]), + models.Index(fields=["deadline_date"]), + models.Index(fields=["is_active"]), ] - unique_together = ['agency', 'job'] # Prevent duplicate assignments + unique_together = ["agency", "job"] # Prevent duplicate assignments def __str__(self): return f"{self.agency.name} - {self.job.title}" @@ -1435,10 +1706,10 @@ class AgencyJobAssignment(Base): def is_currently_active(self): """Check if assignment is currently active""" return ( - self.status == 'ACTIVE' and - self.deadline_date and - self.deadline_date > timezone.now() and - self.candidates_submitted < self.max_candidates + self.status == "ACTIVE" + and self.deadline_date + and self.deadline_date > timezone.now() + and self.candidates_submitted < self.max_candidates ) @property @@ -1455,7 +1726,9 @@ class AgencyJobAssignment(Base): raise ValidationError(_("Maximum candidates must be greater than 0")) if self.candidates_submitted > self.max_candidates: - raise ValidationError(_("Candidates submitted cannot exceed maximum candidates")) + raise ValidationError( + _("Candidates submitted cannot exceed maximum candidates") + ) @property def remaining_slots(self): @@ -1475,16 +1748,18 @@ class AgencyJobAssignment(Base): @property def can_submit(self): """Check if agency can still submit candidates""" - return (self.is_active and - not self.is_expired and - not self.is_full and - self.status == self.AssignmentStatus.ACTIVE) + return ( + self.is_active + and not self.is_expired + and not self.is_full + and self.status == self.AssignmentStatus.ACTIVE + ) def increment_submission_count(self): """Safely increment the submitted candidates count""" if self.can_submit: self.candidates_submitted += 1 - self.save(update_fields=['candidates_submitted']) + self.save(update_fields=["candidates_submitted"]) # Check if assignment is now complete # if self.candidates_submitted >= self.max_candidates: @@ -1496,13 +1771,23 @@ class AgencyJobAssignment(Base): def extend_deadline(self, new_deadline): """Extend the deadline for this assignment""" # Convert database deadline to timezone-aware for comparison - deadline_aware = timezone.make_aware(self.deadline_date) if timezone.is_naive(self.deadline_date) else self.deadline_date + deadline_aware = ( + timezone.make_aware(self.deadline_date) + if timezone.is_naive(self.deadline_date) + else self.deadline_date + ) if new_deadline > deadline_aware: if not self.deadline_extended: self.original_deadline = self.deadline_date self.deadline_extended = True self.deadline_date = new_deadline - self.save(update_fields=['deadline_date', 'original_deadline', 'deadline_extended']) + self.save( + update_fields=[ + "deadline_date", + "original_deadline", + "deadline_extended", + ] + ) return True return False @@ -1514,52 +1799,42 @@ class AgencyAccessLink(Base): AgencyJobAssignment, on_delete=models.CASCADE, related_name="access_link", - verbose_name=_("Assignment") + verbose_name=_("Assignment"), ) # Security unique_token = models.CharField( - max_length=64, - unique=True, - editable=False, - verbose_name=_("Unique Token") + max_length=64, unique=True, editable=False, verbose_name=_("Unique Token") ) access_password = models.CharField( max_length=32, verbose_name=_("Access Password"), - help_text=_("Password for agency access") + help_text=_("Password for agency access"), ) # Timeline created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) expires_at = models.DateTimeField( - verbose_name=_("Expires At"), - help_text=_("When this access link expires") + verbose_name=_("Expires At"), help_text=_("When this access link expires") ) last_accessed = models.DateTimeField( - null=True, - blank=True, - verbose_name=_("Last Accessed") + null=True, blank=True, verbose_name=_("Last Accessed") ) # Usage tracking access_count = models.PositiveIntegerField( - default=0, - verbose_name=_("Access Count") - ) - is_active = models.BooleanField( - default=True, - verbose_name=_("Is Active") + default=0, verbose_name=_("Access Count") ) + is_active = models.BooleanField(default=True, verbose_name=_("Is Active")) class Meta: verbose_name = _("Agency Access Link") verbose_name_plural = _("Agency Access Links") ordering = ["-created_at"] indexes = [ - models.Index(fields=['unique_token']), - models.Index(fields=['expires_at']), - models.Index(fields=['is_active']), + models.Index(fields=["unique_token"]), + models.Index(fields=["expires_at"]), + models.Index(fields=["is_active"]), ] def __str__(self): @@ -1584,19 +1859,21 @@ class AgencyAccessLink(Base): """Record an access to this link""" self.last_accessed = timezone.now() self.access_count += 1 - self.save(update_fields=['last_accessed', 'access_count']) + self.save(update_fields=["last_accessed", "access_count"]) def generate_token(self): """Generate a unique secure token""" import secrets + self.unique_token = secrets.token_urlsafe(48) def generate_password(self): """Generate a random password""" import secrets import string + alphabet = string.ascii_letters + string.digits - self.access_password = ''.join(secrets.choice(alphabet) for _ in range(12)) + self.access_password = "".join(secrets.choice(alphabet) for _ in range(12)) def save(self, *args, **kwargs): """Override save to generate token and password if not set""" @@ -1607,8 +1884,6 @@ class AgencyAccessLink(Base): super().save(*args, **kwargs) - - class BreakTime(models.Model): """Model to store break times for a schedule""" @@ -1621,34 +1896,45 @@ class BreakTime(models.Model): class InterviewSchedule(Base): """Stores the scheduling criteria for interviews""" - """Stores individual scheduled interviews""" class InterviewType(models.TextChoices): - REMOTE = 'Remote', 'Remote Interview' - ONSITE = 'Onsite', 'In-Person Interview' - + REMOTE = 'Remote', 'Remote Interview' + ONSITE = 'Onsite', 'In-Person Interview' + interview_type = models.CharField( - max_length=10, + max_length=10, choices=InterviewType.choices, default=InterviewType.REMOTE, verbose_name="Interview Meeting Type" ) - job = models.ForeignKey( - JobPosting, on_delete=models.CASCADE, related_name="interview_schedules", db_index=True + JobPosting, + on_delete=models.CASCADE, + related_name="interview_schedules", + db_index=True, ) - candidates = models.ManyToManyField(Candidate, related_name="interview_schedules", blank=True,null=True) - start_date = models.DateField(db_index=True, verbose_name=_("Start Date")) # Added index - end_date = models.DateField(db_index=True, verbose_name=_("End Date")) # Added index + applications = models.ManyToManyField( + Application, related_name="interview_schedules", blank=True, null=True + ) + start_date = models.DateField( + db_index=True, verbose_name=_("Start Date") + ) # Added index + end_date = models.DateField( + db_index=True, verbose_name=_("End Date") + ) # Added index working_days = models.JSONField( verbose_name=_("Working Days") ) # Store days of week as [0,1,2,3,4] for Mon-Fri start_time = models.TimeField(verbose_name=_("Start Time")) end_time = models.TimeField(verbose_name=_("End Time")) - break_start_time = models.TimeField(verbose_name=_("Break Start Time"),null=True,blank=True) - break_end_time = models.TimeField(verbose_name=_("Break End Time"),null=True,blank=True) + break_start_time = models.TimeField( + verbose_name=_("Break Start Time"), null=True, blank=True + ) + break_end_time = models.TimeField( + verbose_name=_("Break End Time"), null=True, blank=True + ) interview_duration = models.PositiveIntegerField( verbose_name=_("Interview Duration (minutes)") @@ -1656,30 +1942,31 @@ class InterviewSchedule(Base): buffer_time = models.PositiveIntegerField( verbose_name=_("Buffer Time (minutes)"), default=0 ) - created_by = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True) # Added index + created_by = models.ForeignKey( + User, on_delete=models.CASCADE, db_index=True + ) # Added index def __str__(self): return f"Interview Schedule for {self.job.title}" class Meta: indexes = [ - models.Index(fields=['start_date']), - models.Index(fields=['end_date']), - models.Index(fields=['created_by']), + models.Index(fields=["start_date"]), + models.Index(fields=["end_date"]), + models.Index(fields=["created_by"]), ] - class ScheduledInterview(Base): - - #for one candidate - candidate = models.ForeignKey( - Candidate, + """Stores individual scheduled interviews""" + + application = models.ForeignKey( + Application, on_delete=models.CASCADE, related_name="scheduled_interviews", - db_index=True + db_index=True, ) - + participants = models.ManyToManyField('Participants', blank=True) @@ -1687,25 +1974,36 @@ class ScheduledInterview(Base): job = models.ForeignKey( - "JobPosting", on_delete=models.CASCADE, related_name="scheduled_interviews", db_index=True + "JobPosting", + on_delete=models.CASCADE, + related_name="scheduled_interviews", + db_index=True, ) zoom_meeting = models.OneToOneField( ZoomMeeting, on_delete=models.CASCADE, related_name="interview", db_index=True, null=True, blank=True ) - + onsite_meeting= models.OneToOneField( OnsiteMeeting, on_delete=models.CASCADE, related_name="onsite_interview", db_index=True, null=True, blank=True ) schedule = models.ForeignKey( - InterviewSchedule, on_delete=models.CASCADE, related_name="interviews",null=True,blank=True, db_index=True + InterviewSchedule, + on_delete=models.CASCADE, + related_name="interviews", + null=True, + blank=True, + db_index=True, ) - interview_date = models.DateField(db_index=True, verbose_name=_("Interview Date")) # Added index + interview_date = models.DateField( + db_index=True, verbose_name=_("Interview Date") + ) # Added index interview_time = models.TimeField(verbose_name=_("Interview Time")) status = models.CharField( - db_index=True, max_length=20, # Added index + db_index=True, + max_length=20, # Added index choices=[ ("scheduled", _("Scheduled")), ("confirmed", _("Confirmed")), @@ -1718,22 +2016,24 @@ class ScheduledInterview(Base): updated_at = models.DateTimeField(auto_now=True) def __str__(self): - return f"Interview with {self.candidate.name} for {self.job.title}" + return f"Interview with {self.application.person.full_name} for {self.job.title}" class Meta: indexes = [ - models.Index(fields=['job', 'status']), - models.Index(fields=['interview_date', 'interview_time']), - models.Index(fields=['candidate', 'job']), + models.Index(fields=["job", "status"]), + models.Index(fields=["interview_date", "interview_time"]), + models.Index(fields=["application", "job"]), ] + class Notification(models.Model): """ Model to store system notifications, primarily for emails. """ + class NotificationType(models.TextChoices): EMAIL = "email", _("Email") - IN_APP = "in_app", _("In-App") # For future expansion + IN_APP = "in_app", _("In-App") # For future expansion class Status(models.TextChoices): PENDING = "pending", _("Pending") @@ -1746,20 +2046,20 @@ class Notification(models.Model): User, on_delete=models.CASCADE, related_name="notifications", - verbose_name=_("Recipient") + verbose_name=_("Recipient"), ) message = models.TextField(verbose_name=_("Notification Message")) notification_type = models.CharField( max_length=20, choices=NotificationType.choices, default=NotificationType.EMAIL, - verbose_name=_("Notification Type") + verbose_name=_("Notification Type"), ) status = models.CharField( max_length=20, choices=Status.choices, default=Status.PENDING, - verbose_name=_("Status") + verbose_name=_("Status"), ) related_meeting = models.ForeignKey( ZoomMeeting, @@ -1767,11 +2067,11 @@ class Notification(models.Model): related_name="notifications", null=True, blank=True, - verbose_name=_("Related Meeting") + verbose_name=_("Related Meeting"), ) scheduled_for = models.DateTimeField( verbose_name=_("Scheduled Send Time"), - help_text=_("The date and time this notification is scheduled to be sent.") + help_text=_("The date and time this notification is scheduled to be sent."), ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -1783,8 +2083,8 @@ class Notification(models.Model): verbose_name = _("Notification") verbose_name_plural = _("Notifications") indexes = [ - models.Index(fields=['status', 'scheduled_for']), - models.Index(fields=['recipient']), + models.Index(fields=["status", "scheduled_for"]), + models.Index(fields=["recipient"]), ] def __str__(self): @@ -1793,28 +2093,242 @@ class Notification(models.Model): def mark_as_sent(self): self.status = Notification.Status.SENT self.last_error = "" - self.save(update_fields=['status', 'last_error']) + self.save(update_fields=["status", "last_error"]) def mark_as_failed(self, error_message=""): self.status = Notification.Status.FAILED self.last_error = error_message self.attempts += 1 - self.save(update_fields=['status', 'last_error', 'attempts']) - + self.save(update_fields=["status", "last_error", "attempts"]) class Participants(Base): """Model to store Participants details""" - name = models.CharField(max_length=255, verbose_name=_("Participant Name"),null=True,blank=True) - email= models.EmailField(verbose_name=_("Email")) - phone = models.CharField(max_length=12,verbose_name=_("Phone Number"),null=True,blank=True) + name = models.CharField( + max_length=255, verbose_name=_("Participant Name"), null=True, blank=True + ) + email = models.EmailField(verbose_name=_("Email")) + phone = models.CharField( + max_length=12, verbose_name=_("Phone Number"), null=True, blank=True + ) designation = models.CharField( - max_length=100, blank=True, verbose_name=_("Designation"),null=True + max_length=100, blank=True, verbose_name=_("Designation"), null=True ) def __str__(self): return f"{self.name} - {self.email}" +class Message(Base): + """Model for messaging between different user types""" + class MessageType(models.TextChoices): + DIRECT = "direct", _("Direct Message") + JOB_RELATED = "job_related", _("Job Related") + SYSTEM = "system", _("System Notification") + + sender = models.ForeignKey( + User, + on_delete=models.CASCADE, + related_name="sent_messages", + verbose_name=_("Sender"), + ) + recipient = models.ForeignKey( + User, + on_delete=models.CASCADE, + related_name="received_messages", + null=True, + blank=True, + verbose_name=_("Recipient"), + ) + job = models.ForeignKey( + JobPosting, + on_delete=models.CASCADE, + null=True, + blank=True, + related_name="messages", + verbose_name=_("Related Job"), + ) + subject = models.CharField(max_length=200, verbose_name=_("Subject")) + content = models.TextField(verbose_name=_("Message Content")) + message_type = models.CharField( + max_length=20, + choices=MessageType.choices, + default=MessageType.DIRECT, + verbose_name=_("Message Type"), + ) + is_read = models.BooleanField(default=False, verbose_name=_("Is Read")) + read_at = models.DateTimeField(null=True, blank=True, verbose_name=_("Read At")) + + class Meta: + verbose_name = _("Message") + verbose_name_plural = _("Messages") + ordering = ["-created_at"] + indexes = [ + models.Index(fields=["sender", "created_at"]), + models.Index(fields=["recipient", "is_read", "created_at"]), + models.Index(fields=["job", "created_at"]), + models.Index(fields=["message_type", "created_at"]), + ] + + def __str__(self): + return f"Message from {self.sender.get_username()} to {self.recipient.get_username() if self.recipient else 'N/A'}" + + def mark_as_read(self): + """Mark message as read and set read timestamp""" + if not self.is_read: + self.is_read = True + self.read_at = timezone.now() + self.save(update_fields=["is_read", "read_at"]) + + @property + def is_job_related(self): + """Check if message is related to a job""" + return self.job is not None + + def get_auto_recipient(self): + """Get auto recipient based on job assignment""" + if self.job and self.job.assigned_to: + return self.job.assigned_to + return None + + def clean(self): + """Validate message constraints""" + super().clean() + + # For job-related messages, ensure recipient is assigned to the job + if self.job and not self.recipient: + if self.job.assigned_to: + self.recipient = self.job.assigned_to + else: + raise ValidationError(_("Job is not assigned to any user. Please assign the job first.")) + + # Validate sender can message this recipient based on user types + # if self.sender and self.recipient: + # self._validate_messaging_permissions() + + def _validate_messaging_permissions(self): + """Validate if sender can message recipient based on user types""" + sender_type = self.sender.user_type + recipient_type = self.recipient.user_type + + # Staff can message anyone + if sender_type == "staff": + return + + # Agency users can only message staff or their own candidates + if sender_type == "agency": + if recipient_type not in ["staff", "candidate"]: + raise ValidationError(_("Agencies can only message staff or candidates.")) + + # If messaging a candidate, ensure candidate is from their agency + if recipient_type == "candidate" and self.job: + if not self.job.hiring_agency.filter(user=self.sender).exists(): + raise ValidationError(_("You can only message candidates from your assigned jobs.")) + + # Candidate users can only message staff + if sender_type == "candidate": + if recipient_type != "staff": + raise ValidationError(_("Candidates can only message staff.")) + + # If job-related, ensure candidate applied for the job + if self.job: + if not Candidate.objects.filter(job=self.job, user=self.sender).exists(): + raise ValidationError(_("You can only message about jobs you have applied for.")) + + def save(self, *args, **kwargs): + """Override save to handle auto-recipient logic""" + self.clean() + super().save(*args, **kwargs) + + +class Document(Base): + """Model for storing documents using Generic Foreign Key""" + + class DocumentType(models.TextChoices): + RESUME = "resume", _("Resume") + COVER_LETTER = "cover_letter", _("Cover Letter") + CERTIFICATE = "certificate", _("Certificate") + ID_DOCUMENT = "id_document", _("ID Document") + PASSPORT = "passport", _("Passport") + EDUCATION = "education", _("Education Document") + EXPERIENCE = "experience", _("Experience Letter") + OTHER = "other", _("Other") + + # Generic Foreign Key fields + content_type = models.ForeignKey( + ContentType, + on_delete=models.CASCADE, + verbose_name=_("Content Type"), + ) + object_id = models.PositiveIntegerField( + verbose_name=_("Object ID"), + ) + content_object = GenericForeignKey('content_type', 'object_id') + + file = models.FileField( + upload_to="documents/%Y/%m/", + verbose_name=_("Document File"), + validators=[validate_image_size], + ) + document_type = models.CharField( + max_length=20, + choices=DocumentType.choices, + default=DocumentType.OTHER, + verbose_name=_("Document Type"), + ) + description = models.CharField( + max_length=200, + blank=True, + verbose_name=_("Description"), + ) + uploaded_by = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name=_("Uploaded By"), + ) + + class Meta: + verbose_name = _("Document") + verbose_name_plural = _("Documents") + ordering = ["-created_at"] + indexes = [ + models.Index(fields=["content_type", "object_id", "document_type", "created_at"]), + ] + + def __str__(self): + try: + if hasattr(self.content_object, 'full_name'): + object_name = self.content_object.full_name + elif hasattr(self.content_object, 'title'): + object_name = self.content_object.title + elif hasattr(self.content_object, '__str__'): + object_name = str(self.content_object) + else: + object_name = f"Object {self.object_id}" + return f"{self.get_document_type_display()} - {object_name}" + except: + return f"{self.get_document_type_display()} - {self.object_id}" + + @property + def file_size(self): + """Return file size in human readable format""" + if self.file: + size = self.file.size + if size < 1024: + return f"{size} bytes" + elif size < 1024 * 1024: + return f"{size / 1024:.1f} KB" + else: + return f"{size / (1024 * 1024):.1f} MB" + return "0 bytes" + + @property + def file_extension(self): + """Return file extension""" + if self.file: + return self.file.name.split('.')[-1].upper() + return "" diff --git a/recruitment/serializers.py b/recruitment/serializers.py index ea52220..6387523 100644 --- a/recruitment/serializers.py +++ b/recruitment/serializers.py @@ -1,14 +1,14 @@ from rest_framework import serializers -from .models import JobPosting, Candidate +from .models import JobPosting, Application class JobPostingSerializer(serializers.ModelSerializer): class Meta: model = JobPosting fields = '__all__' -class CandidateSerializer(serializers.ModelSerializer): +class ApplicationSerializer(serializers.ModelSerializer): job_title = serializers.CharField(source='job.title', read_only=True) class Meta: - model = Candidate + model = Application fields = '__all__' diff --git a/recruitment/signals.py b/recruitment/signals.py index ba2c033..98e12fc 100644 --- a/recruitment/signals.py +++ b/recruitment/signals.py @@ -1,18 +1,20 @@ import logging +import random from datetime import datetime, timedelta from django.db import transaction from django_q.models import Schedule from django_q.tasks import schedule - from django.dispatch import receiver from django_q.tasks import async_task from django.db.models.signals import post_save from django.contrib.auth.models import User from django.utils import timezone -from .models import FormField,FormStage,FormTemplate,Candidate,JobPosting,Notification,AgencyJobAssignment,AgencyAccessLink +from .models import FormField,FormStage,FormTemplate,Application,JobPosting,Notification,HiringAgency,Person +from django.contrib.auth import get_user_model logger = logging.getLogger(__name__) +User = get_user_model() @receiver(post_save, sender=JobPosting) def format_job(sender, instance, created, **kwargs): if created: @@ -57,9 +59,9 @@ def format_job(sender, instance, created, **kwargs): # instance.form_template.is_active = False # instance.save() -@receiver(post_save, sender=Candidate) +@receiver(post_save, sender=Application) def score_candidate_resume(sender, instance, created, **kwargs): - if not instance.is_resume_parsed: + if instance.resume and not instance.is_resume_parsed: logger.info(f"Scoring resume for candidate {instance.pk}") async_task( 'recruitment.tasks.handle_reume_parsing_and_scoring', @@ -399,11 +401,33 @@ def notification_created(sender, instance, created, **kwargs): logger.info(f"Notification cached for SSE: {notification_data}") -@receiver(post_save,sender=AgencyJobAssignment) -def create_access_link(sender,instance,created,**kwargs): +def generate_random_password(): + import string + return ''.join(random.choices(string.ascii_letters + string.digits, k=12)) +@receiver(post_save, sender=HiringAgency) +def hiring_agency_created(sender, instance, created, **kwargs): if created: - link=AgencyAccessLink(assignment=instance) - link.access_password = link.generate_password() - link.unique_token = link.generate_token() - link.expires_at = datetime.now() + timedelta(days=4) - link.save() \ No newline at end of file + logger.info(f"New hiring agency created: {instance.pk} - {instance.name}") + user = User.objects.create_user( + username=instance.name, + email=instance.email, + user_type="agency" + ) + user.set_password(generate_random_password()) + user.save() + instance.user = user + instance.save() +@receiver(post_save, sender=Person) +def person_created(sender, instance, created, **kwargs): + if created: + logger.info(f"New Person created: {instance.pk} - {instance.email}") + user = User.objects.create_user( + username=instance.slug, + first_name=instance.first_name, + last_name=instance.last_name, + email=instance.email, + phone=instance.phone, + user_type="candidate" + ) + instance.user = user + instance.save() \ No newline at end of file diff --git a/recruitment/tasks.py b/recruitment/tasks.py index 4515e1b..a0c05cf 100644 --- a/recruitment/tasks.py +++ b/recruitment/tasks.py @@ -7,7 +7,7 @@ from PyPDF2 import PdfReader from datetime import datetime from django.db import transaction from .utils import create_zoom_meeting -from recruitment.models import Candidate +from recruitment.models import Application from . linkedin_service import LinkedInService from django.shortcuts import get_object_or_404 from . models import JobPosting @@ -244,8 +244,8 @@ def handle_reume_parsing_and_scoring(pk): # --- 1. Robust Object Retrieval (Prevents looping on DoesNotExist) --- try: - instance = Candidate.objects.get(pk=pk) - except Candidate.DoesNotExist: + instance = Application.objects.get(pk=pk) + except Application.DoesNotExist: # Exit gracefully if the candidate was deleted after the task was queued logger.warning(f"Candidate matching query does not exist for pk={pk}. Exiting task.") print(f"Candidate matching query does not exist for pk={pk}. Exiting task.") @@ -453,7 +453,7 @@ def create_interview_and_meeting( Synchronous task for a single interview slot, dispatched by django-q. """ try: - candidate = Candidate.objects.get(pk=candidate_id) + candidate = Application.objects.get(pk=candidate_id) job = JobPosting.objects.get(pk=job_id) schedule = InterviewSchedule.objects.get(pk=schedule_id) @@ -477,7 +477,7 @@ def create_interview_and_meeting( password=result["meeting_details"]["password"] ) ScheduledInterview.objects.create( - candidate=candidate, + application=Application, job=job, zoom_meeting=zoom_meeting, schedule=schedule, @@ -485,11 +485,11 @@ def create_interview_and_meeting( interview_time=slot_time ) # Log success or use Django-Q result system for monitoring - logger.info(f"Successfully scheduled interview for {candidate.name}") + logger.info(f"Successfully scheduled interview for {Application.name}") return True # Task succeeded else: # Handle Zoom API failure (e.g., log it or notify administrator) - logger.error(f"Zoom API failed for {candidate.name}: {result['message']}") + logger.error(f"Zoom API failed for {Application.name}: {result['message']}") return False # Task failed except Exception as e: @@ -704,14 +704,14 @@ def sync_candidate_to_source_task(candidate_id, source_id): try: # Get the candidate and source - candidate = Candidate.objects.get(pk=candidate_id) + application = Application.objects.get(pk=candidate_id) source = Source.objects.get(pk=source_id) # Initialize sync service sync_service = CandidateSyncService() # Perform the sync operation - result = sync_service.sync_candidate_to_source(candidate, source) + result = sync_service.sync_candidate_to_source(application, source) # Log the operation IntegrationLog.objects.create( @@ -719,7 +719,7 @@ def sync_candidate_to_source_task(candidate_id, source_id): action=IntegrationLog.ActionChoices.SYNC, endpoint=source.sync_endpoint or "unknown", method=source.sync_method or "POST", - request_data={"candidate_id": candidate_id, "candidate_name": candidate.name}, + request_data={"candidate_id": candidate_id, "application_name": application.name}, response_data=result, status_code="SUCCESS" if result.get('success') else "ERROR", error_message=result.get('error') if not result.get('success') else None, @@ -731,8 +731,8 @@ def sync_candidate_to_source_task(candidate_id, source_id): logger.info(f"Sync completed for candidate {candidate_id} to source {source_id}: {result}") return result - except Candidate.DoesNotExist: - error_msg = f"Candidate not found: {candidate_id}" + except Application.DoesNotExist: + error_msg = f"Application not found: {candidate_id}" logger.error(error_msg) return {"success": False, "error": error_msg} diff --git a/recruitment/templatetags/file_filters.py b/recruitment/templatetags/file_filters.py new file mode 100644 index 0000000..4ed1701 --- /dev/null +++ b/recruitment/templatetags/file_filters.py @@ -0,0 +1,27 @@ +from django import template + +register = template.Library() + +@register.filter +def filename(value): + """ + Extract just the filename from a file path. + Example: 'documents/resume.pdf' -> 'resume.pdf' + """ + if not value: + return '' + + # Convert to string and split by path separators + import os + return os.path.basename(str(value)) + +@register.filter +def split(value, delimiter): + """ + Split a string by a delimiter and return a list. + This is a custom implementation of the split functionality. + """ + if not value: + return [] + + return str(value).split(delimiter) diff --git a/recruitment/tests.py b/recruitment/tests.py index 20feb89..afadf48 100644 --- a/recruitment/tests.py +++ b/recruitment/tests.py @@ -1,5 +1,5 @@ from django.test import TestCase, Client -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model from django.urls import reverse from django.utils import timezone from django.core.files.uploadedfile import SimpleUploadedFile @@ -7,6 +7,8 @@ from datetime import datetime, time, timedelta import json from unittest.mock import patch, MagicMock +User = get_user_model() + from .models import ( JobPosting, Candidate, ZoomMeeting, FormTemplate, FormStage, FormField, FormSubmission, FieldResponse, InterviewSchedule, ScheduledInterview, @@ -14,11 +16,11 @@ from .models import ( ) from .forms import ( JobPostingForm, CandidateForm, ZoomMeetingForm, MeetingCommentForm, - CandidateStageForm, InterviewScheduleForm + CandidateStageForm, InterviewScheduleForm, CandidateSignupForm ) from .views import ( ZoomMeetingListView, ZoomMeetingCreateView, job_detail, candidate_screening_view, - candidate_exam_view, candidate_interview_view, submit_form, api_schedule_candidate_meeting + candidate_exam_view, candidate_interview_view, api_schedule_candidate_meeting ) from .views_frontend import CandidateListView, JobListView from .utils import create_zoom_meeting, get_candidates_from_request @@ -46,14 +48,21 @@ class BaseTestCase(TestCase): location_country='Saudi Arabia', description='Job description', qualifications='Job qualifications', + application_deadline=timezone.now() + timedelta(days=30), created_by=self.user ) - self.candidate = Candidate.objects.create( + # Create a person first + from .models import Person + person = Person.objects.create( first_name='John', last_name='Doe', email='john@example.com', - phone='1234567890', + phone='1234567890' + ) + + self.candidate = Candidate.objects.create( + person=person, resume=SimpleUploadedFile('resume.pdf', b'file_content', content_type='application/pdf'), job=self.job, stage='Applied' @@ -231,28 +240,6 @@ class ViewTests(BaseTestCase): self.assertEqual(response.status_code, 200) self.assertContains(response, 'success') - def test_submit_form(self): - """Test submit_form view""" - # Create a form template first - template = FormTemplate.objects.create( - job=self.job, - name='Test Template', - created_by=self.user, - is_active=True - ) - - data = { - 'field_1': 'John', # Assuming field ID 1 corresponds to First Name - 'field_2': 'Doe', # Assuming field ID 2 corresponds to Last Name - 'field_3': 'john@example.com', # Email - } - - response = self.client.post( - reverse('application_submit', kwargs={'template_id': template.id}), - data - ) - # After successful submission, should redirect to success page - self.assertEqual(response.status_code, 302) class FormTests(BaseTestCase): @@ -268,13 +255,13 @@ class FormTests(BaseTestCase): 'location_city': 'Riyadh', 'location_state': 'Riyadh', 'location_country': 'Saudi Arabia', - 'description': 'Job description', + 'description': 'Job description with at least 20 characters to meet validation requirements', 'qualifications': 'Job qualifications', 'salary_range': '5000-7000', 'application_deadline': '2025-12-31', 'max_applications': '100', 'open_positions': '2', - 'hash_tags': '#hiring, #jobopening' + 'hash_tags': '#hiring,#jobopening' } form = JobPostingForm(data=form_data) self.assertTrue(form.is_valid()) @@ -315,24 +302,51 @@ class FormTests(BaseTestCase): form_data = { 'stage': 'Exam' } - form = CandidateStageForm(data=form_data, candidate=self.candidate) + form = CandidateStageForm(data=form_data, instance=self.candidate) self.assertTrue(form.is_valid()) def test_interview_schedule_form(self): """Test InterviewScheduleForm""" + # Update candidate to Interview stage first + self.candidate.stage = 'Interview' + self.candidate.save() + form_data = { 'candidates': [self.candidate.id], 'start_date': (timezone.now() + timedelta(days=1)).date(), 'end_date': (timezone.now() + timedelta(days=7)).date(), 'working_days': [0, 1, 2, 3, 4], # Monday to Friday - 'start_time': '09:00', - 'end_time': '17:00', - 'interview_duration': 60, - 'buffer_time': 15 } form = InterviewScheduleForm(slug=self.job.slug, data=form_data) self.assertTrue(form.is_valid()) + def test_candidate_signup_form_valid(self): + """Test CandidateSignupForm with valid data""" + form_data = { + 'first_name': 'John', + 'last_name': 'Doe', + 'email': 'john.doe@example.com', + 'phone': '+1234567890', + 'password': 'SecurePass123', + 'confirm_password': 'SecurePass123' + } + form = CandidateSignupForm(data=form_data) + self.assertTrue(form.is_valid()) + + def test_candidate_signup_form_password_mismatch(self): + """Test CandidateSignupForm with password mismatch""" + form_data = { + 'first_name': 'John', + 'last_name': 'Doe', + 'email': 'john.doe@example.com', + 'phone': '+1234567890', + 'password': 'SecurePass123', + 'confirm_password': 'DifferentPass123' + } + form = CandidateSignupForm(data=form_data) + self.assertFalse(form.is_valid()) + self.assertIn('Passwords do not match', str(form.errors)) + class IntegrationTests(BaseTestCase): """Integration tests for multiple components""" @@ -340,11 +354,14 @@ class IntegrationTests(BaseTestCase): def test_candidate_journey(self): """Test the complete candidate journey from application to interview""" # 1. Create candidate - candidate = Candidate.objects.create( + person = Person.objects.create( first_name='Jane', last_name='Smith', email='jane@example.com', - phone='9876543210', + phone='9876543210' + ) + candidate = Candidate.objects.create( + person=person, resume=SimpleUploadedFile('resume.pdf', b'file_content', content_type='application/pdf'), job=self.job, stage='Applied' @@ -449,11 +466,15 @@ class PerformanceTests(BaseTestCase): """Test pagination with large datasets""" # Create many candidates for i in range(100): - Candidate.objects.create( + person = Person.objects.create( first_name=f'Candidate{i}', last_name=f'Test{i}', email=f'candidate{i}@example.com', - phone=f'123456789{i}', + phone=f'123456789{i}' + ) + Candidate.objects.create( + person=person, + resume=SimpleUploadedFile(f'resume{i}.pdf', b'file_content', content_type='application/pdf'), job=self.job, stage='Applied' ) @@ -594,13 +615,17 @@ class TestFactories: @staticmethod def create_candidate(**kwargs): job = TestFactories.create_job_posting() + person = Person.objects.create( + first_name='Test', + last_name='Candidate', + email='test@example.com', + phone='1234567890' + ) defaults = { - 'first_name': 'Test', - 'last_name': 'Candidate', - 'email': 'test@example.com', - 'phone': '1234567890', + 'person': person, 'job': job, - 'stage': 'Applied' + 'stage': 'Applied', + 'resume': SimpleUploadedFile('resume.pdf', b'file_content', content_type='application/pdf') } defaults.update(kwargs) return Candidate.objects.create(**defaults) diff --git a/recruitment/urls.py b/recruitment/urls.py index 794e2c5..394687b 100644 --- a/recruitment/urls.py +++ b/recruitment/urls.py @@ -5,13 +5,22 @@ from . import views_integration from . import views_source urlpatterns = [ - path('', views_frontend.dashboard_view, name='dashboard'), - + path("", views_frontend.dashboard_view, name="dashboard"), # Job URLs (using JobPosting model) - path('jobs/', views_frontend.JobListView.as_view(), name='job_list'), - path('jobs/create/', views.create_job, name='job_create'), - path('job//upload_image_simple/', views.job_image_upload, name='job_image_upload'), - path('jobs//update/', views.edit_job, name='job_update'), + path("persons/", views.PersonListView.as_view(), name="person_list"), + path("persons/create/", views.PersonCreateView.as_view(), name="person_create"), + path("persons//", views.PersonDetailView.as_view(), name="person_detail"), + path("persons//update/", views.PersonUpdateView.as_view(), name="person_update"), + path("persons//delete/", views.PersonDeleteView.as_view(), name="person_delete"), + + path("jobs/", views_frontend.JobListView.as_view(), name="job_list"), + path("jobs/create/", views.create_job, name="job_create"), + path( + "job//upload_image_simple/", + views.job_image_upload, + name="job_image_upload", + ), + path("jobs//update/", views.edit_job, name="job_update"), # path('jobs//delete/', views., name='job_delete'), path('jobs//', views.job_detail, name='job_detail'), path('jobs//download/cvs/', views.job_cvs_download, name='job_cvs_download'), @@ -19,85 +28,250 @@ urlpatterns = [ path('careers/',views.kaauh_career,name='kaauh_career'), # LinkedIn Integration URLs - path('jobs//post-to-linkedin/', views.post_to_linkedin, name='post_to_linkedin'), - path('jobs/linkedin/login/', views.linkedin_login, name='linkedin_login'), - path('jobs/linkedin/callback/', views.linkedin_callback, name='linkedin_callback'), - - path('jobs//schedule-interviews/', views.schedule_interviews_view, name='schedule_interviews'), - path('jobs//confirm-schedule-interviews/', views.confirm_schedule_interviews_view, name='confirm_schedule_interviews_view'), + path( + "jobs//post-to-linkedin/", + views.post_to_linkedin, + name="post_to_linkedin", + ), + path("jobs/linkedin/login/", views.linkedin_login, name="linkedin_login"), + path("jobs/linkedin/callback/", views.linkedin_callback, name="linkedin_callback"), + path( + "jobs//schedule-interviews/", + views.schedule_interviews_view, + name="schedule_interviews", + ), + path( + "jobs//confirm-schedule-interviews/", + views.confirm_schedule_interviews_view, + name="confirm_schedule_interviews_view", + ), # Candidate URLs - path('candidates/', views_frontend.CandidateListView.as_view(), name='candidate_list'), - path('candidates/create/', views_frontend.CandidateCreateView.as_view(), name='candidate_create'), - path('candidates/create//', views_frontend.CandidateCreateView.as_view(), name='candidate_create_for_job'), - path('jobs//candidates/', views_frontend.JobCandidatesListView.as_view(), name='job_candidates_list'), - path('candidates//update/', views_frontend.CandidateUpdateView.as_view(), name='candidate_update'), - path('candidates//delete/', views_frontend.CandidateDeleteView.as_view(), name='candidate_delete'), - path('candidate//view/', views_frontend.candidate_detail, name='candidate_detail'), - path('candidate//resume-template/', views_frontend.candidate_resume_template_view, name='candidate_resume_template'), - path('candidate//update-stage/', views_frontend.candidate_update_stage, name='candidate_update_stage'), - path('candidate//retry-scoring/', views_frontend.retry_scoring_view, name='candidate_retry_scoring'), - + path( + "candidates/", views_frontend.ApplicationListView.as_view(), name="candidate_list" + ), + path( + "candidates/create/", + views_frontend.ApplicationCreateView.as_view(), + name="candidate_create", + ), + path( + "candidates/create//", + views_frontend.ApplicationCreateView.as_view(), + name="candidate_create_for_job", + ), + path( + "jobs//candidates/", + views_frontend.JobApplicationListView.as_view(), + name="job_candidates_list", + ), + path( + "candidates//update/", + views_frontend.ApplicationUpdateView.as_view(), + name="candidate_update", + ), + path( + "candidates//delete/", + views_frontend.ApplicationDeleteView.as_view(), + name="candidate_delete", + ), + path( + "candidate//view/", + views_frontend.candidate_detail, + name="candidate_detail", + ), + path( + "candidate//resume-template/", + views_frontend.candidate_resume_template_view, + name="candidate_resume_template", + ), + path( + "candidate//update-stage/", + views_frontend.candidate_update_stage, + name="candidate_update_stage", + ), + path( + "candidate//retry-scoring/", + views_frontend.retry_scoring_view, + name="candidate_retry_scoring", + ), # Training URLs - path('training/', views_frontend.TrainingListView.as_view(), name='training_list'), - path('training/create/', views_frontend.TrainingCreateView.as_view(), name='training_create'), - path('training//', views_frontend.TrainingDetailView.as_view(), name='training_detail'), - path('training//update/', views_frontend.TrainingUpdateView.as_view(), name='training_update'), - path('training//delete/', views_frontend.TrainingDeleteView.as_view(), name='training_delete'), - + path("training/", views_frontend.TrainingListView.as_view(), name="training_list"), + path( + "training/create/", + views_frontend.TrainingCreateView.as_view(), + name="training_create", + ), + path( + "training//", + views_frontend.TrainingDetailView.as_view(), + name="training_detail", + ), + path( + "training//update/", + views_frontend.TrainingUpdateView.as_view(), + name="training_update", + ), + path( + "training//delete/", + views_frontend.TrainingDeleteView.as_view(), + name="training_delete", + ), # Meeting URLs - path('meetings/', views.ZoomMeetingListView.as_view(), name='list_meetings'), - path('meetings/create-meeting/', views.ZoomMeetingCreateView.as_view(), name='create_meeting'), - path('meetings/meeting-details//', views.ZoomMeetingDetailsView.as_view(), name='meeting_details'), - path('meetings/update-meeting//', views.ZoomMeetingUpdateView.as_view(), name='update_meeting'), - path('meetings/delete-meeting//', views.ZoomMeetingDeleteView, name='delete_meeting'), - + path("meetings/", views.ZoomMeetingListView.as_view(), name="list_meetings"), + path( + "meetings/create-meeting/", + views.ZoomMeetingCreateView.as_view(), + name="create_meeting", + ), + path( + "meetings/meeting-details//", + views.ZoomMeetingDetailsView.as_view(), + name="meeting_details", + ), + path( + "meetings/update-meeting//", + views.ZoomMeetingUpdateView.as_view(), + name="update_meeting", + ), + path( + "meetings/delete-meeting//", + views.ZoomMeetingDeleteView, + name="delete_meeting", + ), # JobPosting functional views URLs (keeping for compatibility) - path('api/create/', views.create_job, name='create_job_api'), - path('api//edit/', views.edit_job, name='edit_job_api'), - + path("api/create/", views.create_job, name="create_job_api"), + path("api//edit/", views.edit_job, name="edit_job_api"), # ERP Integration URLs - path('integration/erp/', views_integration.ERPIntegrationView.as_view(), name='erp_integration'), - path('integration/erp/create-job/', views_integration.erp_create_job_view, name='erp_create_job'), - path('integration/erp/update-job/', views_integration.erp_update_job_view, name='erp_update_job'), - path('integration/erp/health/', views_integration.erp_integration_health, name='erp_integration_health'), - + path( + "integration/erp/", + views_integration.ERPIntegrationView.as_view(), + name="erp_integration", + ), + path( + "integration/erp/create-job/", + views_integration.erp_create_job_view, + name="erp_create_job", + ), + path( + "integration/erp/update-job/", + views_integration.erp_update_job_view, + name="erp_update_job", + ), + path( + "integration/erp/health/", + views_integration.erp_integration_health, + name="erp_integration_health", + ), # Form Preview URLs # path('forms/', views.form_list, name='form_list'), - - path('forms/builder/', views.form_builder, name='form_builder'), - path('forms/builder//', views.form_builder, name='form_builder'), - path('forms/', views.form_templates_list, name='form_templates_list'), - path('forms/create-template/', views.create_form_template, name='create_form_template'), - - path('jobs//edit_linkedin_post_content/',views.edit_linkedin_post_content,name='edit_linkedin_post_content'), - path('jobs//candidate_screening_view/', views.candidate_screening_view, name='candidate_screening_view'), - path('jobs//candidate_exam_view/', views.candidate_exam_view, name='candidate_exam_view'), - path('jobs//candidate_interview_view/', views.candidate_interview_view, name='candidate_interview_view'), - path('jobs//candidate_offer_view/', views_frontend.candidate_offer_view, name='candidate_offer_view'), - path('jobs//candidate_hired_view/', views_frontend.candidate_hired_view, name='candidate_hired_view'), - path('jobs//export//csv/', views_frontend.export_candidates_csv, name='export_candidates_csv'), - path('jobs//candidates//update_status///', views_frontend.update_candidate_status, name='update_candidate_status'), - + path("forms/builder/", views.form_builder, name="form_builder"), + path( + "forms/builder//", views.form_builder, name="form_builder" + ), + path("forms/", views.form_templates_list, name="form_templates_list"), + path( + "forms/create-template/", + views.create_form_template, + name="create_form_template", + ), + path( + "jobs//edit_linkedin_post_content/", + views.edit_linkedin_post_content, + name="edit_linkedin_post_content", + ), + path( + "jobs//candidate_screening_view/", + views.candidate_screening_view, + name="candidate_screening_view", + ), + path( + "jobs//candidate_exam_view/", + views.candidate_exam_view, + name="candidate_exam_view", + ), + path( + "jobs//candidate_interview_view/", + views.candidate_interview_view, + name="candidate_interview_view", + ), + path( + "jobs//candidate_offer_view/", + views_frontend.candidate_offer_view, + name="candidate_offer_view", + ), + path( + "jobs//candidate_hired_view/", + views_frontend.candidate_hired_view, + name="candidate_hired_view", + ), + path( + "jobs//export//csv/", + views_frontend.export_candidates_csv, + name="export_candidates_csv", + ), + path( + "jobs//candidates//update_status///", + views_frontend.update_candidate_status, + name="update_candidate_status", + ), # Sync URLs - path('jobs//sync-hired-candidates/', views_frontend.sync_hired_candidates, name='sync_hired_candidates'), - path('sources//test-connection/', views_frontend.test_source_connection, name='test_source_connection'), - - path('jobs///reschedule_meeting_for_candidate//', views.reschedule_meeting_for_candidate, name='reschedule_meeting_for_candidate'), - - path('jobs//update_candidate_exam_status/', views.update_candidate_exam_status, name='update_candidate_exam_status'), - path('jobs//bulk_update_candidate_exam_status/', views.bulk_update_candidate_exam_status, name='bulk_update_candidate_exam_status'), - - path('htmx//candidate_criteria_view/', views.candidate_criteria_view_htmx, name='candidate_criteria_view_htmx'), - path('htmx//candidate_set_exam_date/', views.candidate_set_exam_date, name='candidate_set_exam_date'), - - path('htmx//candidate_update_status/', views.candidate_update_status, name='candidate_update_status'), - + path( + "jobs//sync-hired-candidates/", + views_frontend.sync_hired_candidates, + name="sync_hired_candidates", + ), + path( + "sources//test-connection/", + views_frontend.test_source_connection, + name="test_source_connection", + ), + path( + "jobs///reschedule_meeting_for_candidate//", + views.reschedule_meeting_for_candidate, + name="reschedule_meeting_for_candidate", + ), + path( + "jobs//update_candidate_exam_status/", + views.update_candidate_exam_status, + name="update_candidate_exam_status", + ), + path( + "jobs//bulk_update_candidate_exam_status/", + views.bulk_update_candidate_exam_status, + name="bulk_update_candidate_exam_status", + ), + path( + "htmx//candidate_criteria_view/", + views.candidate_criteria_view_htmx, + name="candidate_criteria_view_htmx", + ), + path( + "htmx//candidate_set_exam_date/", + views.candidate_set_exam_date, + name="candidate_set_exam_date", + ), + path( + "htmx//candidate_update_status/", + views.candidate_update_status, + name="candidate_update_status", + ), # path('forms/form//submit/', views.submit_form, name='submit_form'), # path('forms/form//', views.form_wizard_view, name='form_wizard'), - path('forms//submissions//', views.form_submission_details, name='form_submission_details'), - path('forms/template//submissions/', views.form_template_submissions_list, name='form_template_submissions_list'), - path('forms/template//all-submissions/', views.form_template_all_submissions, name='form_template_all_submissions'), - + path( + "forms//submissions//", + views.form_submission_details, + name="form_submission_details", + ), + path( + "forms/template//submissions/", + views.form_template_submissions_list, + name="form_template_submissions_list", + ), + path( + "forms/template//all-submissions/", + views.form_template_all_submissions, + name="form_template_all_submissions", + ), # path('forms//', views.form_preview, name='form_preview'), # path('forms//submit/', views.form_submit, name='form_submit'), # path('forms//embed/', views.form_embed, name='form_embed'), @@ -110,74 +284,188 @@ urlpatterns = [ # path('api/templates/save/', views.save_form_template, name='save_form_template'), # path('api/templates//', views.load_form_template, name='load_form_template'), # path('api/templates//delete/', views.delete_form_template, name='delete_form_template'), - - - path('jobs//calendar/', views.interview_calendar_view, name='interview_calendar'), - path('jobs//calendar/interview//', views.interview_detail_view, name='interview_detail'), - + path( + "jobs//calendar/", + views.interview_calendar_view, + name="interview_calendar", + ), + path( + "jobs//calendar/interview//", + views.interview_detail_view, + name="interview_detail", + ), # Candidate Meeting Scheduling/Rescheduling URLs - path('jobs//candidates//schedule-meeting/', views.schedule_candidate_meeting, name='schedule_candidate_meeting'), - path('api/jobs//candidates//schedule-meeting/', views.api_schedule_candidate_meeting, name='api_schedule_candidate_meeting'), - path('jobs//candidates//reschedule-meeting//', views.reschedule_candidate_meeting, name='reschedule_candidate_meeting'), - path('api/jobs//candidates//reschedule-meeting//', views.api_reschedule_candidate_meeting, name='api_reschedule_candidate_meeting'), + path( + "jobs//candidates//schedule-meeting/", + views.schedule_candidate_meeting, + name="schedule_candidate_meeting", + ), + path( + "api/jobs//candidates//schedule-meeting/", + views.api_schedule_candidate_meeting, + name="api_schedule_candidate_meeting", + ), + path( + "jobs//candidates//reschedule-meeting//", + views.reschedule_candidate_meeting, + name="reschedule_candidate_meeting", + ), + path( + "api/jobs//candidates//reschedule-meeting//", + views.api_reschedule_candidate_meeting, + name="api_reschedule_candidate_meeting", + ), # New URL for simple page-based meeting scheduling - path('jobs//candidates//schedule-meeting-page/', views.schedule_meeting_for_candidate, name='schedule_meeting_for_candidate'), - path('jobs//candidates//delete_meeting_for_candidate//', views.delete_meeting_for_candidate, name='delete_meeting_for_candidate'), - - + path( + "jobs//candidates//schedule-meeting-page/", + views.schedule_meeting_for_candidate, + name="schedule_meeting_for_candidate", + ), + path( + "jobs//candidates//delete_meeting_for_candidate//", + views.delete_meeting_for_candidate, + name="delete_meeting_for_candidate", + ), # users urls - path('user/',views.user_detail,name='user_detail'), - path('user/user_profile_image_update/',views.user_profile_image_update,name='user_profile_image_update'), - path('easy_logs/',views.easy_logs,name='easy_logs'), - path('settings/',views.admin_settings,name='admin_settings'), - path('staff/create',views.create_staff_user,name='create_staff_user'), - path('set_staff_password//',views.set_staff_password,name='set_staff_password'), - path('account_toggle_status/',views.account_toggle_status,name='account_toggle_status'), - - - + path("user/", views.user_detail, name="user_detail"), + path( + "user/user_profile_image_update/", + views.user_profile_image_update, + name="user_profile_image_update", + ), + path("easy_logs/", views.easy_logs, name="easy_logs"), + path("settings/", views.admin_settings, name="admin_settings"), + path("staff/create", views.create_staff_user, name="create_staff_user"), + path( + "set_staff_password//", + views.set_staff_password, + name="set_staff_password", + ), + path( + "account_toggle_status/", + views.account_toggle_status, + name="account_toggle_status", + ), # Source URLs - path('sources/', views_source.SourceListView.as_view(), name='source_list'), - path('sources/create/', views_source.SourceCreateView.as_view(), name='source_create'), - path('sources//', views_source.SourceDetailView.as_view(), name='source_detail'), - path('sources//update/', views_source.SourceUpdateView.as_view(), name='source_update'), - path('sources//delete/', views_source.SourceDeleteView.as_view(), name='source_delete'), - path('sources//generate-keys/', views_source.generate_api_keys_view, name='generate_api_keys'), - path('sources//toggle-status/', views_source.toggle_source_status_view, name='toggle_source_status'), - path('sources/api/copy-to-clipboard/', views_source.copy_to_clipboard_view, name='copy_to_clipboard'), - - + path("sources/", views_source.SourceListView.as_view(), name="source_list"), + path( + "sources/create/", views_source.SourceCreateView.as_view(), name="source_create" + ), + path( + "sources//", + views_source.SourceDetailView.as_view(), + name="source_detail", + ), + path( + "sources//update/", + views_source.SourceUpdateView.as_view(), + name="source_update", + ), + path( + "sources//delete/", + views_source.SourceDeleteView.as_view(), + name="source_delete", + ), + path( + "sources//generate-keys/", + views_source.generate_api_keys_view, + name="generate_api_keys", + ), + path( + "sources//toggle-status/", + views_source.toggle_source_status_view, + name="toggle_source_status", + ), + path( + "sources/api/copy-to-clipboard/", + views_source.copy_to_clipboard_view, + name="copy_to_clipboard", + ), # Meeting Comments URLs - path('meetings//comments/add/', views.add_meeting_comment, name='add_meeting_comment'), - path('meetings//comments//edit/', views.edit_meeting_comment, name='edit_meeting_comment'), - - path('meetings//comments//delete/', views.delete_meeting_comment, name='delete_meeting_comment'), - - path('meetings//set_meeting_candidate/', views.set_meeting_candidate, name='set_meeting_candidate'), - + path( + "meetings//comments/add/", + views.add_meeting_comment, + name="add_meeting_comment", + ), + path( + "meetings//comments//edit/", + views.edit_meeting_comment, + name="edit_meeting_comment", + ), + path( + "meetings//comments//delete/", + views.delete_meeting_comment, + name="delete_meeting_comment", + ), + path( + "meetings//set_meeting_candidate/", + views.set_meeting_candidate, + name="set_meeting_candidate", + ), # Hiring Agency URLs - path('agencies/', views.agency_list, name='agency_list'), - path('agencies/create/', views.agency_create, name='agency_create'), - path('agencies//', views.agency_detail, name='agency_detail'), - path('agencies//update/', views.agency_update, name='agency_update'), - path('agencies//delete/', views.agency_delete, name='agency_delete'), - path('agencies//candidates/', views.agency_candidates, name='agency_candidates'), + path("agencies/", views.agency_list, name="agency_list"), + path("agencies/create/", views.agency_create, name="agency_create"), + path("agencies//", views.agency_detail, name="agency_detail"), + path("agencies//update/", views.agency_update, name="agency_update"), + path("agencies//delete/", views.agency_delete, name="agency_delete"), + path( + "agencies//candidates/", + views.agency_candidates, + name="agency_candidates", + ), # path('agencies//send-message/', views.agency_detail_send_message, name='agency_detail_send_message'), - # Agency Assignment Management URLs - path('agency-assignments/', views.agency_assignment_list, name='agency_assignment_list'), - path('agency-assignments/create/', views.agency_assignment_create, name='agency_assignment_create'), - path('agency-assignments//create/', views.agency_assignment_create, name='agency_assignment_create'), - path('agency-assignments//', views.agency_assignment_detail, name='agency_assignment_detail'), - path('agency-assignments//update/', views.agency_assignment_update, name='agency_assignment_update'), - path('agency-assignments//extend-deadline/', views.agency_assignment_extend_deadline, name='agency_assignment_extend_deadline'), - + path( + "agency-assignments/", + views.agency_assignment_list, + name="agency_assignment_list", + ), + path( + "agency-assignments/create/", + views.agency_assignment_create, + name="agency_assignment_create", + ), + path( + "agency-assignments//create/", + views.agency_assignment_create, + name="agency_assignment_create", + ), + path( + "agency-assignments//", + views.agency_assignment_detail, + name="agency_assignment_detail", + ), + path( + "agency-assignments//update/", + views.agency_assignment_update, + name="agency_assignment_update", + ), + path( + "agency-assignments//extend-deadline/", + views.agency_assignment_extend_deadline, + name="agency_assignment_extend_deadline", + ), # Agency Access Link URLs - path('agency-access-links/create/', views.agency_access_link_create, name='agency_access_link_create'), - path('agency-access-links//', views.agency_access_link_detail, name='agency_access_link_detail'), - path('agency-access-links//deactivate/', views.agency_access_link_deactivate, name='agency_access_link_deactivate'), - path('agency-access-links//reactivate/', views.agency_access_link_reactivate, name='agency_access_link_reactivate'), - + path( + "agency-access-links/create/", + views.agency_access_link_create, + name="agency_access_link_create", + ), + path( + "agency-access-links//", + views.agency_access_link_detail, + name="agency_access_link_detail", + ), + path( + "agency-access-links//deactivate/", + views.agency_access_link_deactivate, + name="agency_access_link_deactivate", + ), + path( + "agency-access-links//reactivate/", + views.agency_access_link_reactivate, + name="agency_access_link_reactivate", + ), # Admin Message Center URLs (messaging functionality removed) # path('admin/messages/', views.admin_message_center, name='admin_message_center'), # path('admin/messages/compose/', views.admin_compose_message, name='admin_compose_message'), @@ -185,35 +473,72 @@ urlpatterns = [ # path('admin/messages//reply/', views.admin_message_reply, name='admin_message_reply'), # path('admin/messages//mark-read/', views.admin_mark_message_read, name='admin_mark_message_read'), # path('admin/messages//delete/', views.admin_delete_message, name='admin_delete_message'), - # Agency Portal URLs (for external agencies) - path('portal/login/', views.agency_portal_login, name='agency_portal_login'), - path('portal/dashboard/', views.agency_portal_dashboard, name='agency_portal_dashboard'), - path('portal/assignment//', views.agency_portal_assignment_detail, name='agency_portal_assignment_detail'), - path('portal/assignment//submit-candidate/', views.agency_portal_submit_candidate_page, name='agency_portal_submit_candidate_page'), - path('portal/submit-candidate/', views.agency_portal_submit_candidate, name='agency_portal_submit_candidate'), - path('portal/logout/', views.agency_portal_logout, name='agency_portal_logout'), - + path("portal/login/", views.agency_portal_login, name="agency_portal_login"), + path( + "portal/dashboard/", + views.agency_portal_dashboard, + name="agency_portal_dashboard", + ), + # Unified Portal URLs + path("login/", views.portal_login, name="portal_login"), + path( + "candidate/dashboard/", + views.candidate_portal_dashboard, + name="candidate_portal_dashboard", + ), + path( + "portal/dashboard/", + views.agency_portal_dashboard, + name="agency_portal_dashboard", + ), + path( + "portal/persons/", + views.agency_portal_persons_list, + name="agency_portal_persons_list", + ), + path( + "portal/assignment//", + views.agency_portal_assignment_detail, + name="agency_portal_assignment_detail", + ), + path( + "portal/assignment//submit-candidate/", + views.agency_portal_submit_candidate_page, + name="agency_portal_submit_candidate_page", + ), + path( + "portal/submit-candidate/", + views.agency_portal_submit_candidate, + name="agency_portal_submit_candidate", + ), + path("portal/logout/", views.portal_logout, name="portal_logout"), # Agency Portal Candidate Management URLs - path('portal/candidates//edit/', views.agency_portal_edit_candidate, name='agency_portal_edit_candidate'), - path('portal/candidates//delete/', views.agency_portal_delete_candidate, name='agency_portal_delete_candidate'), - + path( + "portal/candidates//edit/", + views.agency_portal_edit_candidate, + name="agency_portal_edit_candidate", + ), + path( + "portal/candidates//delete/", + views.agency_portal_delete_candidate, + name="agency_portal_delete_candidate", + ), # API URLs for messaging (removed) # path('api/agency/messages//', views.api_agency_message_detail, name='api_agency_message_detail'), # path('api/agency/messages//mark-read/', views.api_agency_mark_message_read, name='api_agency_mark_message_read'), - # API URLs for candidate management - path('api/candidate//', views.api_candidate_detail, name='api_candidate_detail'), - + path( + "api/candidate//", + views.api_candidate_detail, + name="api_candidate_detail", + ), # # Admin Notification API # path('api/admin/notification-count/', views.api_notification_count, name='admin_notification_count'), - # # Agency Notification API # path('api/agency/notification-count/', views.api_notification_count, name='api_agency_notification_count'), - # # SSE Notification Stream # path('api/notifications/stream/', views.notification_stream, name='notification_stream'), - # # Notification URLs # path('notifications/', views.notification_list, name='notification_list'), # path('notifications//', views.notification_detail, name='notification_detail'), @@ -222,27 +547,63 @@ urlpatterns = [ # path('notifications//delete/', views.notification_delete, name='notification_delete'), # path('notifications/mark-all-read/', views.notification_mark_all_read, name='notification_mark_all_read'), # path('api/notification-count/', views.api_notification_count, name='api_notification_count'), - - - #participants urls - path('participants/', views_frontend.ParticipantsListView.as_view(), name='participants_list'), - path('participants/create/', views_frontend.ParticipantsCreateView.as_view(), name='participants_create'), - path('participants//', views_frontend.ParticipantsDetailView.as_view(), name='participants_detail'), - path('participants//update/', views_frontend.ParticipantsUpdateView.as_view(), name='participants_update'), - path('participants//delete/', views_frontend.ParticipantsDeleteView.as_view(), name='participants_delete'), - + # participants urls + path( + "participants/", + views_frontend.ParticipantsListView.as_view(), + name="participants_list", + ), + path( + "participants/create/", + views_frontend.ParticipantsCreateView.as_view(), + name="participants_create", + ), + path( + "participants//", + views_frontend.ParticipantsDetailView.as_view(), + name="participants_detail", + ), + path( + "participants//update/", + views_frontend.ParticipantsUpdateView.as_view(), + name="participants_update", + ), + path( + "participants//delete/", + views_frontend.ParticipantsDeleteView.as_view(), + name="participants_delete", + ), # Email composition URLs + path( + "jobs//candidates//compose-email/", + views.compose_candidate_email, + name="compose_candidate_email", + ), + # Message URLs + path("messages/", views.message_list, name="message_list"), + path("messages/create/", views.message_create, name="message_create"), + path("messages//", views.message_detail, name="message_detail"), + path("messages//reply/", views.message_reply, name="message_reply"), + path("messages//mark-read/", views.message_mark_read, name="message_mark_read"), + path("messages//mark-unread/", views.message_mark_unread, name="message_mark_unread"), + path("messages//delete/", views.message_delete, name="message_delete"), + path("api/unread-count/", views.api_unread_count, name="api_unread_count"), + + # Documents + path("documents/upload//", views.document_upload, name="document_upload"), + path("documents//delete/", views.document_delete, name="document_delete"), + path("documents//download/", views.document_download, name="document_download"), path('jobs//candidates/compose_email/', views.compose_candidate_email, name='compose_candidate_email'), path('interview/partcipants//',views.create_interview_participants,name='create_interview_participants'), path('interview/email//',views.send_interview_email,name='send_interview_email'), - - + + # # --- SCHEDULED INTERVIEW URLS (New Centralized Management) --- # path('interview/list/', views.InterviewListView.as_view(), name='interview_list'), # path('interviews//', views.ScheduledInterviewDetailView.as_view(), name='scheduled_interview_detail'), # path('interviews//update/', views.ScheduledInterviewUpdateView.as_view(), name='update_scheduled_interview'), # path('interviews//delete/', views.ScheduledInterviewDeleteView.as_view(), name='delete_scheduled_interview'), - + ] diff --git a/recruitment/views.py b/recruitment/views.py index c77b52e..c4e1841 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -4,58 +4,71 @@ import zipfile from django.core.paginator import Paginator from django.utils.translation import gettext as _ -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model, authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.admin.views.decorators import staff_member_required from django.contrib.auth.mixins import LoginRequiredMixin - -from rich import print - +from .decorators import ( + agency_user_required, + candidate_user_required, + staff_user_required, + staff_or_agency_required, + staff_or_candidate_required, + AgencyRequiredMixin, + CandidateRequiredMixin, + StaffRequiredMixin, + StaffOrAgencyRequiredMixin, + StaffOrCandidateRequiredMixin +) +from .forms import StaffUserCreationForm,ToggleAccountForm, JobPostingStatusForm,LinkedPostContentForm,CandidateEmailForm,InterviewForm,ProfileImageUploadForm,ParticipantsSelectForm from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from django.http import HttpResponse, JsonResponse -from datetime import datetime,time,timedelta +from datetime import datetime, time, timedelta from django.views import View from django.urls import reverse from django.conf import settings from django.utils import timezone -from django.db.models import FloatField,CharField, DurationField -from django.db.models import F, IntegerField, Count, Avg, Sum, Q, ExpressionWrapper, fields +from django.db.models import FloatField, CharField, DurationField +from django.db.models import ( + F, + IntegerField, + Count, + Avg, + Sum, + Q, + ExpressionWrapper, + fields, +) from django.db.models.functions import Cast, Coalesce, TruncDate from django.db.models.fields.json import KeyTextTransform from django.db.models.expressions import ExpressionWrapper from django.urls import reverse_lazy from django.db.models import Count, Avg, F,Q from .forms import ( - CandidateExamDateForm, - InterviewForm, ZoomMeetingForm, + CandidateExamDateForm, JobPostingForm, - FormTemplateForm, - InterviewScheduleForm,JobPostingStatusForm, - BreakTimeFormSet, JobPostingImageForm, - ProfileImageUploadForm, - StaffUserCreationForm, MeetingCommentForm, - ToggleAccountForm, - HiringAgencyForm, - AgencyCandidateSubmissionForm, - AgencyLoginForm, - AgencyAccessLinkForm, - AgencyJobAssignmentForm, - LinkedPostContentForm, - CandidateEmailForm, + InterviewScheduleForm, + FormTemplateForm, SourceForm, - InterviewEmailForm, - + HiringAgencyForm, + AgencyJobAssignmentForm, + AgencyAccessLinkForm, + AgencyApplicationSubmissionForm, + AgencyLoginForm, + PortalLoginForm, + MessageForm, + PersonForm ) from easyaudit.models import CRUDEvent, LoginEvent, RequestEvent from rest_framework import viewsets from django.contrib import messages from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from .linkedin_service import LinkedInService -from .serializers import JobPostingSerializer, CandidateSerializer +from .serializers import JobPostingSerializer, ApplicationSerializer from django.shortcuts import get_object_or_404, render, redirect from django.views.generic import CreateView, UpdateView, DetailView, ListView,DeleteView from .utils import ( @@ -79,15 +92,20 @@ from .models import ( InterviewSchedule, BreakTime, ZoomMeeting, - Candidate, + Application, + Person, JobPosting, ScheduledInterview, JobPostingImage, - Profile,MeetingComment,HiringAgency, + Profile, + MeetingComment, + HiringAgency, AgencyJobAssignment, AgencyAccessLink, Notification, - Source + Source, + Message, + Document, ) import logging from datastar_py.django import ( @@ -100,20 +118,70 @@ from django_q.tasks import async_task from django.db.models import Prefetch from django.db.models import Q, Count, Avg from django.db.models import FloatField +from django.urls import reverse_lazy logger = logging.getLogger(__name__) +User = get_user_model() + +class PersonListView(StaffRequiredMixin, ListView): + model = Person + template_name = "people/person_list.html" + context_object_name = "people_list" + + +class PersonCreateView(CreateView): + model = Person + template_name = "people/create_person.html" + form_class = PersonForm + # success_url = reverse_lazy("person_list") + + def form_valid(self, form): + if 'HX-Request' in self.request.headers: + instance = form.save() + view = self.request.POST.get("view") + if view == "portal": + slug = self.request.POST.get("agency") + if slug: + agency = HiringAgency.objects.get(slug=slug) + print(agency) + instance.agency = agency + instance.save() + return redirect("agency_portal_persons_list") + if view == "job": + return redirect("candidate_create") + return super().form_valid(form) + + +class PersonDetailView(DetailView): + model = Person + template_name = "people/person_detail.html" + context_object_name = "person" + + +class PersonUpdateView(StaffRequiredMixin, UpdateView): + model = Person + template_name = "people/update_person.html" + form_class = PersonForm + success_url = reverse_lazy("person_list") + + +class PersonDeleteView(StaffRequiredMixin, DeleteView): + model = Person + template_name = "people/delete_person.html" + success_url = reverse_lazy("person_list") + class JobPostingViewSet(viewsets.ModelViewSet): queryset = JobPosting.objects.all() serializer_class = JobPostingSerializer class CandidateViewSet(viewsets.ModelViewSet): - queryset = Candidate.objects.all() - serializer_class = CandidateSerializer + queryset = Application.objects.all() + serializer_class = ApplicationSerializer -class ZoomMeetingCreateView(LoginRequiredMixin, CreateView): +class ZoomMeetingCreateView(StaffRequiredMixin, CreateView): model = ZoomMeeting template_name = "meetings/create_meeting.html" form_class = ZoomMeetingForm @@ -125,7 +193,9 @@ class ZoomMeetingCreateView(LoginRequiredMixin, CreateView): topic = instance.topic if instance.start_time < timezone.now(): messages.error(self.request, "Start time must be in the future.") - return redirect(reverse("create_meeting",kwargs={"slug": instance.slug})) + return redirect( + reverse("create_meeting", kwargs={"slug": instance.slug}) + ) start_time = instance.start_time duration = instance.duration @@ -143,13 +213,15 @@ class ZoomMeetingCreateView(LoginRequiredMixin, CreateView): return redirect(reverse("list_meetings")) else: messages.error(self.request, result["message"]) - return redirect(reverse("create_meeting",kwargs={"slug": instance.slug})) + return redirect( + reverse("create_meeting", kwargs={"slug": instance.slug}) + ) except Exception as e: messages.error(self.request, f"Error creating meeting: {e}") - return redirect(reverse("create_meeting",kwargs={"slug": instance.slug})) + return redirect(reverse("create_meeting", kwargs={"slug": instance.slug})) -class ZoomMeetingListView(LoginRequiredMixin, ListView): +class ZoomMeetingListView(StaffRequiredMixin, ListView): model = ZoomMeeting template_name = "meetings/list_meetings.html" context_object_name = "meetings" @@ -162,14 +234,16 @@ class ZoomMeetingListView(LoginRequiredMixin, ListView): queryset = queryset.prefetch_related( Prefetch( - 'interview', # related_name from ZoomMeeting to ScheduledInterview - queryset=ScheduledInterview.objects.select_related('candidate', 'job'), - to_attr='interview_details' # Changed to not start with underscore + "interview", # related_name from ZoomMeeting to ScheduledInterview + queryset=ScheduledInterview.objects.select_related("application", "job"), + to_attr="interview_details", # Changed to not start with underscore ) ) # Handle search by topic or meeting_id - search_query = self.request.GET.get("q", "") # Renamed from 'search' to 'q' for consistency + search_query = self.request.GET.get( + "q", "" + ) # Renamed from 'search' to 'q' for consistency if search_query: queryset = queryset.filter( Q(topic__icontains=search_query) | Q(meeting_id__icontains=search_query) @@ -185,8 +259,8 @@ class ZoomMeetingListView(LoginRequiredMixin, ListView): if candidate_name: # Filter based on the name of the candidate associated with the meeting's interview queryset = queryset.filter( - Q(interview__candidate__first_name__icontains=candidate_name) | - Q(interview__candidate__last_name__icontains=candidate_name) + Q(interview__application__first_name__icontains=candidate_name) + | Q(interview__application__last_name__icontains=candidate_name) ) return queryset @@ -197,7 +271,7 @@ class ZoomMeetingListView(LoginRequiredMixin, ListView): context["status_filter"] = self.request.GET.get("status", "") context["candidate_name_filter"] = self.request.GET.get("candidate_name", "") return context - + # @login_required # def InterviewListView(request): # # interview_type=request.GET.get('interview_type','Remote') @@ -208,7 +282,41 @@ class ZoomMeetingListView(LoginRequiredMixin, ListView): # 'meetings':meetings, # }) - + + # search_query = request.GET.get("q", "") # Renamed from 'search' to 'q' for consistency + # if search_query: + # interviews = interviews.filter( + # Q(topic__icontains=search_query) | Q(meeting_id__icontains=search_query) + # ) + + # # Handle filter by status + # status_filter = request.GET.get("status", "") + # if status_filter: + # queryset = queryset.filter(status=status_filter) + + # # Handle search by candidate name + # candidate_name = request.GET.get("candidate_name", "") + # if candidate_name: + # # Filter based on the name of the candidate associated with the meeting's interview + # queryset = queryset.filter( + # Q(interview__candidate__first_name__icontains=candidate_name) | + # Q(interview__candidate__last_name__icontains=candidate_name) + # ) + + + + +# @login_required +# def InterviewListView(request): +# # interview_type=request.GET.get('interview_type','Remote') +# # print(interview_type) +# interview_type='Onsite' +# meetings=ScheduledInterview.objects.filter(schedule__interview_type=interview_type) +# return render(request, "meetings/list_meetings.html",{ +# 'meetings':meetings, +# }) + + # search_query = request.GET.get("q", "") # Renamed from 'search' to 'q' for consistency # if search_query: # interviews = interviews.filter( @@ -233,20 +341,21 @@ class ZoomMeetingListView(LoginRequiredMixin, ListView): -class ZoomMeetingDetailsView(LoginRequiredMixin, DetailView): +class ZoomMeetingDetailsView(StaffRequiredMixin, DetailView): model = ZoomMeeting template_name = "meetings/meeting_details.html" context_object_name = "meeting" + def get_context_data(self, **kwargs): context=super().get_context_data(**kwargs) - meeting = self.object + meeting = self.object try: interview=meeting.interview except Exception as e: print(e) candidate = interview.candidate job=meeting.get_job - + # Assuming interview.participants and interview.system_users hold the people: participants = list(interview.participants.all()) + list(interview.system_users.all()) external_participants=list(interview.participants.all()) @@ -263,10 +372,8 @@ class ZoomMeetingDetailsView(LoginRequiredMixin, DetailView): ) context['total_participants']=total_participants return context - - -class ZoomMeetingUpdateView(LoginRequiredMixin, UpdateView): +class ZoomMeetingUpdateView(StaffRequiredMixin, UpdateView): model = ZoomMeeting form_class = ZoomMeetingForm context_object_name = "meeting" @@ -311,20 +418,33 @@ class ZoomMeetingUpdateView(LoginRequiredMixin, UpdateView): def ZoomMeetingDeleteView(request, slug): meeting = get_object_or_404(ZoomMeeting, slug=slug) if "HX-Request" in request.headers: - return render(request, "meetings/delete_meeting_form.html", {"meeting": meeting,"delete_url": reverse("delete_meeting", kwargs={"slug": meeting.slug})}) + return render( + request, + "meetings/delete_meeting_form.html", + { + "meeting": meeting, + "delete_url": reverse("delete_meeting", kwargs={"slug": meeting.slug}), + }, + ) if request.method == "POST": try: result = delete_zoom_meeting(meeting.meeting_id) - if result["status"] == "success" or "Meeting does not exist" in result["details"]["message"]: + if ( + result["status"] == "success" + or "Meeting does not exist" in result["details"]["message"] + ): meeting.delete() messages.success(request, "Meeting deleted successfully.") else: - messages.error(request, f"{result["message"]} , {result['details']["message"]}") + messages.error( + request, f"{result['message']} , {result['details']['message']}" + ) return redirect(reverse("list_meetings")) except Exception as e: messages.error(request, str(e)) return redirect(reverse("list_meetings")) + # Job Posting # def job_list(request): # """Display the list of job postings order by creation date descending""" @@ -348,21 +468,24 @@ def ZoomMeetingDeleteView(request, slug): @login_required +@staff_user_required def create_job(request): """Create a new job posting""" if request.method == "POST": - form = JobPostingForm( - request.POST - ) + form = JobPostingForm(request.POST) # to check user is authenticated or not if form.is_valid(): try: job = form.save(commit=False) job.save() - job_apply_url_relative=reverse('application_detail',kwargs={'slug':job.slug}) - job_apply_url_absolute=request.build_absolute_uri(job_apply_url_relative) - job.application_url=job_apply_url_absolute + job_apply_url_relative = reverse( + "application_detail", kwargs={"slug": job.slug} + ) + job_apply_url_absolute = request.build_absolute_uri( + job_apply_url_relative + ) + job.application_url = job_apply_url_absolute # FormTemplate.objects.create(job=job, is_active=False, name=job.title,created_by=request.user) job.save() messages.success(request, f'Job "{job.title}" created successfully!') @@ -378,14 +501,12 @@ def create_job(request): @login_required +@staff_user_required def edit_job(request, slug): """Edit an existing job posting""" job = get_object_or_404(JobPosting, slug=slug) if request.method == "POST": - form = JobPostingForm( - request.POST, - instance=job - ) + form = JobPostingForm(request.POST, instance=job) if form.is_valid(): try: form.save() @@ -398,37 +519,35 @@ def edit_job(request, slug): messages.error(request, "Please correct the errors below.") else: job = get_object_or_404(JobPosting, slug=slug) - form = JobPostingForm( - instance=job - ) + form = JobPostingForm(instance=job) return render(request, "jobs/edit_job.html", {"form": form, "job": job}) -SCORE_PATH = 'ai_analysis_data__analysis_data__match_score' -HIGH_POTENTIAL_THRESHOLD=75 +SCORE_PATH = "ai_analysis_data__analysis_data__match_score" +HIGH_POTENTIAL_THRESHOLD = 75 from django.contrib.sites.shortcuts import get_current_site -@login_required + + +@staff_user_required def job_detail(request, slug): """View details of a specific job""" job = get_object_or_404(JobPosting, slug=slug) - current_site=get_current_site(request) - print(current_site) - # Get all candidates for this job, ordered by most recent - applicants = job.candidates.all().order_by("-created_at") + # Get all applications for this job, ordered by most recent + applicants = job.applications.all().order_by("-created_at") - # Count candidates by stage for summary statistics + # Count applications by stage for summary statistics total_applicant = applicants.count() applied_count = applicants.filter(stage="Applied").count() - exam_count=applicants.filter(stage="Exam").count + exam_count = applicants.filter(stage="Exam").count() interview_count = applicants.filter(stage="Interview").count() offer_count = applicants.filter(stage="Offer").count() status_form = JobPostingStatusForm(instance=job) - linkedin_content_form=LinkedPostContentForm(instance=job) + linkedin_content_form = LinkedPostContentForm(instance=job) try: # If the related object exists, use its instance data image_upload_form = JobPostingImageForm(instance=job.post_images) @@ -436,35 +555,32 @@ def job_detail(request, slug): # If the related object does NOT exist, create a blank form image_upload_form = JobPostingImageForm() - # 2. Check for POST request (Status Update Submission) - if request.method == 'POST': - + if request.method == "POST": status_form = JobPostingStatusForm(request.POST, instance=job) if status_form.is_valid(): - job_status=status_form.cleaned_data['status'] - form_template=job.form_template - if job_status=='ACTIVE': - form_template.is_active=True - form_template.save(update_fields=['is_active']) + job_status = status_form.cleaned_data["status"] + form_template = job.form_template + if job_status == "ACTIVE": + form_template.is_active = True + form_template.save(update_fields=["is_active"]) else: - form_template.is_active=False - form_template.save(update_fields=['is_active']) + form_template.is_active = False + form_template.save(update_fields=["is_active"]) status_form.save() # Add a success message - messages.success(request, f"Status for '{job.title}' updated to '{job.get_status_display()}' successfully!") + messages.success( + request, + f"Status for '{job.title}' updated to '{job.get_status_display()}' successfully!", + ) - - return redirect('job_detail', slug=slug) + return redirect("job_detail", slug=slug) else: - - messages.error(request, "Failed to update status due to validation errors.") - # --- 2. Quality Metrics (JSON Aggregation) --- # Filter for candidates who have been scored and annotate with a sortable score @@ -478,88 +594,98 @@ def job_detail(request, slug): # # Cast the extracted text score to a FloatField for numerical operations # sortable_score=Cast('score_as_text', output_field=FloatField()) # ) - candidates_with_score = applicants.filter( - is_resume_parsed=True - ).annotate( - annotated_match_score=Coalesce( - Cast(SCORE_PATH, output_field=IntegerField()), - 0 - ) + candidates_with_score = applicants.filter(is_resume_parsed=True).annotate( + annotated_match_score=Coalesce(Cast(SCORE_PATH, output_field=IntegerField()), 0) ) - total_candidates=applicants.count() - avg_match_score_result = candidates_with_score.aggregate(avg_score=Avg('annotated_match_score'))['avg_score'] + total_candidates = applicants.count() + avg_match_score_result = candidates_with_score.aggregate( + avg_score=Avg("annotated_match_score") + )["avg_score"] avg_match_score = round(avg_match_score_result or 0, 1) - high_potential_count = candidates_with_score.filter(annotated_match_score__gte=HIGH_POTENTIAL_THRESHOLD).count() - high_potential_ratio = round( (high_potential_count / total_candidates) * 100, 1 ) if total_candidates > 0 else 0 - + high_potential_count = candidates_with_score.filter( + annotated_match_score__gte=HIGH_POTENTIAL_THRESHOLD + ).count() + high_potential_ratio = ( + round((high_potential_count / total_candidates) * 100, 1) + if total_candidates > 0 + else 0 + ) # --- 3. Time Metrics (Duration Aggregation) --- # Metric: Average Time from Applied to Interview (T2I) - t2i_candidates = applicants.filter( - interview_date__isnull=False - ).annotate( + t2i_candidates = applicants.filter(interview_date__isnull=False).annotate( time_to_interview=ExpressionWrapper( - F('interview_date') - F('created_at'), - output_field=DurationField() + F("interview_date") - F("created_at"), output_field=DurationField() ) ) - avg_t2i_duration = t2i_candidates.aggregate( - avg_t2i=Avg('time_to_interview') - )['avg_t2i'] + avg_t2i_duration = t2i_candidates.aggregate(avg_t2i=Avg("time_to_interview"))[ + "avg_t2i" + ] # Convert timedelta to days - avg_t2i_days = round(avg_t2i_duration.total_seconds() / (60*60*24), 1) if avg_t2i_duration else 0 + avg_t2i_days = ( + round(avg_t2i_duration.total_seconds() / (60 * 60 * 24), 1) + if avg_t2i_duration + else 0 + ) # Metric: Average Time in Exam Stage t_in_exam_candidates = applicants.filter( exam_date__isnull=False, interview_date__isnull=False ).annotate( time_in_exam=ExpressionWrapper( - F('interview_date') - F('exam_date'), - output_field=DurationField() + F("interview_date") - F("exam_date"), output_field=DurationField() ) ) avg_t_in_exam_duration = t_in_exam_candidates.aggregate( - avg_t_in_exam=Avg('time_in_exam') - )['avg_t_in_exam'] + avg_t_in_exam=Avg("time_in_exam") + )["avg_t_in_exam"] # Convert timedelta to days - avg_t_in_exam_days = round(avg_t_in_exam_duration.total_seconds() / (60*60*24), 1) if avg_t_in_exam_duration else 0 + avg_t_in_exam_days = ( + round(avg_t_in_exam_duration.total_seconds() / (60 * 60 * 24), 1) + if avg_t_in_exam_duration + else 0 + ) - category_data = applicants.filter( - ai_analysis_data__analysis_data__category__isnull=False - ).values('ai_analysis_data__analysis_data__category').annotate( - candidate_count=Count('id'), - category=Cast('ai_analysis_data__analysis_data__category',output_field=CharField()) - ).order_by('ai_analysis_data__analysis_data__category') + category_data = ( + applicants.filter(ai_analysis_data__analysis_data__category__isnull=False) + .values("ai_analysis_data__analysis_data__category") + .annotate( + candidate_count=Count("id"), + category=Cast( + "ai_analysis_data__analysis_data__category", output_field=CharField() + ), + ) + .order_by("ai_analysis_data__analysis_data__category") + ) # Prepare data for Chart.js print(category_data) - categories = [item['category'] for item in category_data] - candidate_counts = [item['candidate_count'] for item in category_data] + categories = [item["category"] for item in category_data] + candidate_counts = [item["candidate_count"] for item in category_data] # avg_scores = [round(item['avg_match_score'], 2) if item['avg_match_score'] is not None else 0 for item in category_data] - context = { "job": job, "applicants": applicants, - "total_applicants": total_applicant, # This was total_candidates in the prompt, using total_applicant for consistency + "total_applicants": total_applicant, # This was total_candidates in the prompt, using total_applicant for consistency "applied_count": applied_count, - 'exam_count':exam_count, + "exam_count": exam_count, "interview_count": interview_count, "offer_count": offer_count, - 'status_form':status_form, - 'image_upload_form':image_upload_form, - 'categories': categories, - 'candidate_counts': candidate_counts, + "status_form": status_form, + "image_upload_form": image_upload_form, + "categories": categories, + "candidate_counts": candidate_counts, # 'avg_scores': avg_scores, # New statistics - 'avg_match_score': avg_match_score, - 'high_potential_count': high_potential_count, - 'high_potential_ratio': high_potential_ratio, - 'avg_t2i_days': avg_t2i_days, - 'avg_t_in_exam_days': avg_t_in_exam_days, - 'linkedin_content_form':linkedin_content_form + "avg_match_score": avg_match_score, + "high_potential_count": high_potential_count, + "high_potential_ratio": high_potential_ratio, + "avg_t2i_days": avg_t2i_days, + "avg_t_in_exam_days": avg_t_in_exam_days, + "linkedin_content_form": linkedin_content_form, } return render(request, "jobs/job_detail.html", context) @@ -568,17 +694,17 @@ def job_detail(request, slug): ALLOWED_EXTENSIONS = ('.pdf', '.docx') def job_cvs_download(request,slug): - + job = get_object_or_404(JobPosting,slug=slug) entries=Candidate.objects.filter(job=job) - + # 2. Create an in-memory byte stream (BytesIO) zip_buffer = io.BytesIO() # 3. Create the ZIP archive with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf: - + for entry in entries: # Check if the file field has a file if not entry.resume: @@ -587,84 +713,92 @@ def job_cvs_download(request,slug): # Get the file name and check extension (case-insensitive) file_name = entry.resume.name.split('/')[-1] file_name_lower = file_name.lower() - + if file_name_lower.endswith(ALLOWED_EXTENSIONS): try: # Open the file object (rb is read binary) file_obj = entry.resume.open('rb') - + # *** ROBUST METHOD: Read the content and write it to the ZIP *** file_content = file_obj.read() - + # Write the file content directly to the ZIP archive - zf.writestr(file_name, file_content) - + zf.writestr(file_name, file_content) + file_obj.close() - + except Exception as e: # Log the error but continue with the rest of the files print(f"Error processing file {file_name}: {e}") continue - + # 4. Prepare the response zip_buffer.seek(0) - + # 5. Create the HTTP response response = HttpResponse(zip_buffer.read(), content_type='application/zip') - + # Set the header for the browser to download the file response['Content-Disposition'] = 'attachment; filename=f"all_cvs_for_{job.title}.zip"' - + return response @login_required +@staff_user_required def job_image_upload(request, slug): - #only for handling the post request - job=get_object_or_404(JobPosting,slug=slug) + # only for handling the post request + job = get_object_or_404(JobPosting, slug=slug) try: instance = JobPostingImage.objects.get(job=job) except JobPostingImage.DoesNotExist: # If it doesn't exist, create a new instance placeholder instance = None - if request.method == 'POST': + if request.method == "POST": # Pass the existing instance to the form if it exists - image_upload_form = JobPostingImageForm(request.POST, request.FILES, instance=instance) + image_upload_form = JobPostingImageForm( + request.POST, request.FILES, instance=instance + ) if image_upload_form.is_valid(): - # If creating a new one (instance is None), set the job link manually if instance is None: image_instance = image_upload_form.save(commit=False) image_instance.job = job image_instance.save() - messages.success(request, f"Image uploaded successfully for {job.title}.") + messages.success( + request, f"Image uploaded successfully for {job.title}." + ) else: # If updating, the form will update the instance passed to it image_upload_form.save() - messages.success(request, f"Image updated successfully for {job.title}.") + messages.success( + request, f"Image updated successfully for {job.title}." + ) else: - - messages.error(request, "Image upload failed: Please ensure a valid image file was selected.") - return redirect('job_detail', slug=job.slug) - return redirect('job_detail', slug=job.slug) + messages.error( + request, + "Image upload failed: Please ensure a valid image file was selected.", + ) + return redirect("job_detail", slug=job.slug) + return redirect("job_detail", slug=job.slug) @login_required -def edit_linkedin_post_content(request,slug): - - job=get_object_or_404(JobPosting,slug=slug) - linkedin_content_form=LinkedPostContentForm(instance=job) - if request.method=='POST': - linkedin_content_form=LinkedPostContentForm(request.POST,instance=job) +@staff_user_required +def edit_linkedin_post_content(request, slug): + job = get_object_or_404(JobPosting, slug=slug) + linkedin_content_form = LinkedPostContentForm(instance=job) + if request.method == "POST": + linkedin_content_form = LinkedPostContentForm(request.POST, instance=job) if linkedin_content_form.is_valid(): linkedin_content_form.save() - messages.success(request,"Linked post content updated successfully!") - return redirect('job_detail',job.slug) + messages.success(request, "Linked post content updated successfully!") + return redirect("job_detail", job.slug) else: - messages.error(request,"Error update the Linkedin Post content") - return redirect('job_detail',job.slug) + messages.error(request, "Error update the Linkedin Post content") + return redirect("job_detail", job.slug) else: linkedin_content_form=LinkedPostContentForm() @@ -690,27 +824,24 @@ WORKPLACE_TYPES = [ def kaauh_career(request): - active_jobs = JobPosting.objects.select_related( - 'form_template' - ).filter( - status='ACTIVE', - form_template__is_active=True + active_jobs = JobPosting.objects.select_related("form_template").filter( + status="ACTIVE", form_template__is_active=True ) selected_department=request.GET.get('department','') department_type_keys=active_jobs.exclude( department__isnull=True ).exclude(department__exact='' ).values_list( - 'department', + 'department', flat=True ).distinct().order_by('department') - + if selected_department and selected_department in department_type_keys: active_jobs=active_jobs.filter(department=selected_department) selected_workplace_type=request.GET.get('workplace_type','') print(selected_workplace_type) selected_job_type = request.GET.get('employment_type', '') - + job_type_keys = active_jobs.values_list('job_type', flat=True).distinct() workplace_type_keys=active_jobs.values_list('workplace_type',flat=True).distinct() if selected_job_type and selected_job_type in job_type_keys: @@ -721,14 +852,14 @@ def kaauh_career(request): JOBS_PER_PAGE=10 paginator = Paginator(active_jobs, JOBS_PER_PAGE) page_number = request.GET.get('page', 1) - + try: page_obj = paginator.get_page(page_number) except EmptyPage: page_obj = paginator.page(paginator.num_pages) - + total_open_roles=active_jobs.all().count() - + return render(request,'applicant/career.html',{'active_jobs': page_obj.object_list, 'job_type_keys':job_type_keys, @@ -745,9 +876,8 @@ def application_detail(request, slug): return render(request, "applicant/application_detail.html", {"job": job}) -from django_q.tasks import async_task - @login_required +@staff_user_required def post_to_linkedin(request, slug): """Post a job to LinkedIn""" job = get_object_or_404(JobPosting, slug=slug) @@ -756,15 +886,14 @@ def post_to_linkedin(request, slug): return redirect("job_list") if request.method == "POST": - linkedin_access_token=request.session.get("linkedin_access_token") - # Check if user is authenticated with LinkedIn + linkedin_access_token = request.session.get("linkedin_access_token") + # Check if user is authenticated with LinkedIn if not "linkedin_access_token": - messages.error(request, "Please authenticate with LinkedIn first.") - return redirect("linkedin_login") + messages.error(request, "Please authenticate with LinkedIn first.") + return redirect("linkedin_login") try: - # Clear previous LinkedIn data for re-posting - #Prepare the job object for background processing + # Prepare the job object for background processing job.posted_to_linkedin = False job.linkedin_post_id = "" job.linkedin_post_url = "" @@ -776,19 +905,21 @@ def post_to_linkedin(request, slug): # Pass the function path, the job slug, and the token as arguments async_task( - 'recruitment.tasks.linkedin_post_task', - job.slug, - linkedin_access_token + "recruitment.tasks.linkedin_post_task", job.slug, linkedin_access_token ) messages.success( request, - _(f"✅ Job posting process for job with JOB ID: {job.internal_job_id} started! Check the job details page in a moment for the final status.") + _( + f"✅ Job posting process for job with JOB ID: {job.internal_job_id} started! Check the job details page in a moment for the final status." + ), ) except Exception as e: logger.error(f"Error enqueuing LinkedIn post: {e}") - messages.error(request, _("Failed to start the job posting process. Please try again.")) + messages.error( + request, _("Failed to start the job posting process. Please try again.") + ) return redirect("job_detail", slug=job.slug) @@ -832,23 +963,24 @@ def linkedin_callback(request): # applicant views def applicant_job_detail(request, slug): """View job details for applicants""" - job=get_object_or_404(JobPosting,slug=slug,status='ACTIVE') - return render(request,'jobs/applicant_job_detail.html',{'job':job}) + job = get_object_or_404(JobPosting, slug=slug, status="ACTIVE") + return render(request, "jobs/applicant_job_detail.html", {"job": job}) + + +def application_success(request, slug): + job = get_object_or_404(JobPosting, slug=slug) + return render(request, "jobs/application_success.html", {"job": job}) -def application_success(request,slug): - job=get_object_or_404(JobPosting,slug=slug) - return render(request,'jobs/application_success.html',{'job':job}) @ensure_csrf_cookie @login_required +@staff_user_required def form_builder(request, template_slug=None): """Render the form builder interface""" context = {} if template_slug: - template = get_object_or_404( - FormTemplate, slug=template_slug - ) - context['template']=template + template = get_object_or_404(FormTemplate, slug=template_slug) + context["template"] = template context["template_slug"] = template.slug context["template_name"] = template.name return render(request, "forms/form_builder.html", context) @@ -866,18 +998,14 @@ def save_form_template(request): if template_slug: # Update existing template - template = get_object_or_404( - FormTemplate, slug=template_slug - ) + template = get_object_or_404(FormTemplate, slug=template_slug) template.name = template_name template.save() # Clear existing stages and fields template.stages.all().delete() else: # Create new template - template = FormTemplate.objects.create( - name=template_name - ) + template = FormTemplate.objects.create(name=template_name) # Create stages and fields for stage_order, stage_data in enumerate(stages_data): @@ -968,6 +1096,7 @@ def load_form_template(request, template_slug): @login_required +@staff_user_required def form_templates_list(request): """List all form templates for the current user""" query = request.GET.get("q", "") @@ -989,6 +1118,7 @@ def form_templates_list(request): @login_required +@staff_user_required def create_form_template(request): """Create a new form template""" if request.method == "POST": @@ -1008,6 +1138,7 @@ def create_form_template(request): @login_required +@staff_user_required @require_http_methods(["GET"]) def list_form_templates(request): """List all form templates for the current user""" @@ -1018,6 +1149,49 @@ def list_form_templates(request): @login_required +@staff_user_required +def form_submission_details(request, template_id, slug): + """Display detailed view of a specific form submission""" + # Get form template and verify ownership + template = get_object_or_404(FormTemplate, id=template_id) + # Get the specific submission + submission = get_object_or_404(FormSubmission, slug=slug, template=template) + + # Get all stages with their fields + stages = template.stages.prefetch_related("fields").order_by("order") + + # Get all responses for this submission, ordered by field order + responses = submission.responses.select_related("field").order_by("field__order") + + # Group responses by stage + stage_responses = {} + for stage in stages: + stage_responses[stage.id] = { + "stage": stage, + "responses": responses.filter(field__stage=stage), + } + + return render( + request, + "forms/form_submission_details.html", + { + "template": template, + "submission": submission, + "stages": stages, + "responses": responses, + "stage_responses": stage_responses, + }, + ) + # return redirect("application_detail", slug=job.slug) + + # return render( + # request, + # "forms/application_submit_form.html", + # {"template_slug": template_slug, "job_id": job_id}, + # ) + +@login_required +@staff_user_required @require_http_methods(["DELETE"]) def delete_form_template(request, template_id): """Delete a form template""" @@ -1027,25 +1201,30 @@ def delete_form_template(request, template_id): {"success": True, "message": "Form template deleted successfully!"} ) - +@login_required +@staff_user_required def application_submit_form(request, template_slug): """Display the form as a step-by-step wizard""" template = get_object_or_404(FormTemplate, slug=template_slug, is_active=True) job_id = template.job.internal_job_id - job=template.job + job = template.job is_limit_exceeded = job.is_application_limit_reached if is_limit_exceeded: messages.error( - request, - _('Application limit reached: This job is no longer accepting new applications.') + request, + _( + "Application limit reached: This job is no longer accepting new applications." + ), ) - return redirect('application_detail',slug=job.slug) + return redirect("application_detail", slug=job.slug) if job.is_expired: messages.error( - request, - _('Application deadline passed: This job is no longer accepting new applications.') + request, + _( + "Application deadline passed: This job is no longer accepting new applications." + ), ) - return redirect('application_detail',slug=job.slug) + return redirect("application_detail", slug=job.slug) return render( request, @@ -1066,14 +1245,19 @@ def application_submit(request, template_slug): if request.method == "POST": try: with transaction.atomic(): - job_posting = JobPosting.objects.select_for_update().get(form_template=template) + job_posting = JobPosting.objects.select_for_update().get( + form_template=template + ) - current_count = job_posting.candidates.count() + current_count = job_posting.applications.count() if current_count >= job_posting.max_applications: template.is_active = False template.save() return JsonResponse( - {"success": False, "message": "Application limit reached for this job."} + { + "success": False, + "message": "Application limit reached for this job.", + } ) submission = FormSubmission.objects.create(template=template) @@ -1122,22 +1306,24 @@ def application_submit(request, template_slug): submission.applicant_email = email.display_value submission.save() # time=timezone.now() - Candidate.objects.create( + Application.objects.create( first_name=first_name.display_value, last_name=last_name.display_value, email=email.display_value, phone=phone.display_value, address=address.display_value, resume=resume.get_file if resume.is_file else None, - job=job + job=job, ) return JsonResponse( - { - "success": True, - "message": "Form submitted successfully!", - "redirect_url": reverse('application_success',kwargs={'slug':job.slug}), - } - ) + { + "success": True, + "message": "Form submitted successfully!", + "redirect_url": reverse( + "application_success", kwargs={"slug": job.slug} + ), + } + ) # return redirect('application_success',slug=job.slug) except Exception as e: @@ -1161,6 +1347,7 @@ def application_submit(request, template_slug): @login_required +@staff_user_required def form_template_submissions_list(request, slug): """List all submissions for a specific form template""" template = get_object_or_404(FormTemplate, slug=slug) @@ -1182,15 +1369,22 @@ def form_template_submissions_list(request, slug): @login_required +@staff_user_required def form_template_all_submissions(request, template_id): """Display all submissions for a form template in table format""" template = get_object_or_404(FormTemplate, id=template_id) print(template) # Get all submissions for this template - submissions = FormSubmission.objects.filter(template=template).order_by("-submitted_at") + submissions = FormSubmission.objects.filter(template=template).order_by( + "-submitted_at" + ) # Get all fields for this template, ordered by stage and field order - fields = FormField.objects.filter(stage__template=template).select_related('stage').order_by('stage__order', 'order') + fields = ( + FormField.objects.filter(stage__template=template) + .select_related("stage") + .order_by("stage__order", "order") + ) # Pagination paginator = Paginator(submissions, 10) # Show 10 submissions per page @@ -1242,6 +1436,7 @@ def form_submission_details(request, template_id, slug): }, ) + def _handle_get_request(request, slug, job): """ Handles GET requests, setting up forms and restoring candidate selections @@ -1268,8 +1463,9 @@ def _handle_get_request(request, slug, job): # 3. Use the list of IDs to initialize the form if selected_ids: - candidates_to_load = Candidate.objects.filter(pk__in=selected_ids) - form.initial["candidates"] = candidates_to_load + candidates_to_load = Application.objects.filter(pk__in=selected_ids) + print(candidates_to_load) + form.initial["applications"] = candidates_to_load return render( request, @@ -1279,7 +1475,6 @@ def _handle_get_request(request, slug, job): def _handle_preview_submission(request, slug, job): - """ Handles the initial POST request (Preview Schedule). Validates forms, calculates slots, saves data to session, and renders preview. @@ -1290,7 +1485,7 @@ def _handle_preview_submission(request, slug, job): if form.is_valid(): # Get the form data - candidates = form.cleaned_data["candidates"] + applications = form.cleaned_data["applications"] interview_type=form.cleaned_data["interview_type"] start_date = form.cleaned_data["start_date"] end_date = form.cleaned_data["end_date"] @@ -1323,18 +1518,18 @@ def _handle_preview_submission(request, slug, job): start_time=start_time, end_time=end_time, interview_duration=interview_duration, - buffer_time=buffer_time, - break_start_time=break_start_time, - break_end_time=break_end_time + buffer_time=buffer_time or 5, + break_start_time=break_start_time or None, + break_end_time=break_end_time or None, ) # Get available slots (temp_breaks logic moved into get_available_time_slots if needed) available_slots = get_available_time_slots(temp_schedule) - if len(available_slots) < len(candidates): + if len(available_slots) < len(applications): messages.error( request, - f"Not enough available slots. Required: {len(candidates)}, Available: {len(available_slots)}", + f"Not enough available slots. Required: {len(applications)}, Available: {len(available_slots)}", ) return render( request, @@ -1344,10 +1539,10 @@ def _handle_preview_submission(request, slug, job): # Create a preview schedule preview_schedule = [] - for i, candidate in enumerate(candidates): + for i, candidate in enumerate(applications): slot = available_slots[i] preview_schedule.append( - {"candidate": candidate, "date": slot["date"], "time": slot["time"]} + {"applications": applications, "date": slot["date"], "time": slot["time"]} ) # Save the form data to session for later use @@ -1362,7 +1557,7 @@ def _handle_preview_submission(request, slug, job): "buffer_time": buffer_time, "break_start_time": break_start_time.isoformat(), "break_end_time": break_end_time.isoformat(), - "candidate_ids": [c.id for c in candidates], + "candidate_ids": [c.id for c in applications], } request.session[SESSION_DATA_KEY] = schedule_data @@ -1400,7 +1595,6 @@ def _handle_confirm_schedule(request, slug, job): Creates the main schedule record and queues individual interviews asynchronously. """ - SESSION_DATA_KEY = "interview_schedule_data" SESSION_ID_KEY = f"schedule_candidate_ids_{slug}" @@ -1425,7 +1619,6 @@ def _handle_confirm_schedule(request, slug, job): end_time=time.fromisoformat(schedule_data["end_time"]), interview_duration=schedule_data["interview_duration"], buffer_time=schedule_data["buffer_time"], - # Use the simple break times saved in the session # If the value is None (because required=False in form), handle it gracefully break_start_time=schedule_data.get("break_start_time"), @@ -1434,14 +1627,16 @@ def _handle_confirm_schedule(request, slug, job): except Exception as e: # Handle database creation error messages.error(request, f"Error creating schedule: {e}") - if SESSION_ID_KEY in request.session: del request.session[SESSION_ID_KEY] + if SESSION_ID_KEY in request.session: + del request.session[SESSION_ID_KEY] return redirect("schedule_interviews", slug=slug) - # 3. Setup candidates and get slots - candidates = Candidate.objects.filter(id__in=schedule_data["candidate_ids"]) + candidates = Application.objects.filter(id__in=schedule_data["candidate_ids"]) schedule.candidates.set(candidates) - available_slots = get_available_time_slots(schedule) # This should still be synchronous and fast + available_slots = get_available_time_slots( + schedule + ) # This should still be synchronous and fast # 4. Queue scheduled interviews asynchronously (FAST RESPONSE) if schedule.interview_type=='Remote': @@ -1450,34 +1645,34 @@ def _handle_confirm_schedule(request, slug, job): if i < len(available_slots): slot = available_slots[i] - # Dispatch the individual creation task to the background queue - - async_task( - "recruitment.tasks.create_interview_and_meeting", - candidate.pk, - job.pk, - schedule.pk, - slot['date'], - slot['time'], - schedule.interview_duration, - ) - queued_count += 1 + # Dispatch the individual creation task to the background queue + async_task( + "recruitment.tasks.create_interview_and_meeting", + candidate.pk, + job.pk, + schedule.pk, + slot["date"], + slot["time"], + schedule.interview_duration, + ) + queued_count += 1 - # 5. Success and Cleanup (IMMEDIATE RESPONSE) messages.success( request, - f"Schedule successfully created. Queued {queued_count} interviews to be booked asynchronously. Check back in a moment!" + f"Schedule successfully created. Queued {queued_count} interviews to be booked asynchronously. Check back in a moment!", ) # Clear both session data keys upon successful completion - if SESSION_DATA_KEY in request.session: del request.session[SESSION_DATA_KEY] - if SESSION_ID_KEY in request.session: del request.session[SESSION_ID_KEY] + if SESSION_DATA_KEY in request.session: + del request.session[SESSION_DATA_KEY] + if SESSION_ID_KEY in request.session: + del request.session[SESSION_ID_KEY] return redirect("job_detail", slug=slug) else: for i, candidate in enumerate(candidates): if i < len(available_slots): - slot = available_slots[i] + slot = available_slots[i] ScheduledInterview.objects.create( candidate=candidate, job=job, @@ -1486,7 +1681,7 @@ def _handle_confirm_schedule(request, slug, job): interview_date=slot['date'], interview_time= slot['time'] ) - + messages.success( request, f"Onsite schedule Interview Create succesfully" @@ -1497,22 +1692,21 @@ def _handle_confirm_schedule(request, slug, job): if SESSION_ID_KEY in request.session: del request.session[SESSION_ID_KEY] return redirect('schedule_interview_location_form',slug=schedule.slug) - def schedule_interviews_view(request, slug): job = get_object_or_404(JobPosting, slug=slug) if request.method == "POST": - # return _handle_confirm_schedule(request, slug, job) + # return _handle_confirm_schedule(request, slug, job) return _handle_preview_submission(request, slug, job) else: return _handle_get_request(request, slug, job) - + def confirm_schedule_interviews_view(request, slug): job = get_object_or_404(JobPosting, slug=slug) if request.method == "POST": return _handle_confirm_schedule(request, slug, job) -@login_required +@staff_user_required def candidate_screening_view(request, slug): """ Manage candidate tiers and stage transitions @@ -1521,10 +1715,10 @@ def candidate_screening_view(request, slug): candidates = job.screening_candidates # Get filter parameters - min_ai_score_str = request.GET.get('min_ai_score') - min_experience_str = request.GET.get('min_experience') - screening_rating = request.GET.get('screening_rating') - tier1_count_str = request.GET.get('tier1_count') + min_ai_score_str = request.GET.get("min_ai_score") + min_experience_str = request.GET.get("min_experience") + screening_rating = request.GET.get("screening_rating") + tier1_count_str = request.GET.get("tier1_count") try: # Check if the string value exists and is not an empty string before conversion @@ -1551,13 +1745,19 @@ def candidate_screening_view(request, slug): # Apply filters if min_ai_score > 0: - candidates = candidates.filter(ai_analysis_data__analysis_data__match_score__gte=min_ai_score) + candidates = candidates.filter( + ai_analysis_data__analysis_data__match_score__gte=min_ai_score + ) if min_experience > 0: - candidates = candidates.filter(ai_analysis_data__analysis_data__years_of_experience__gte=min_experience) + candidates = candidates.filter( + ai_analysis_data__analysis_data__years_of_experience__gte=min_experience + ) if screening_rating: - candidates = candidates.filter(ai_analysis_data__analysis_data__screening_stage_rating=screening_rating) + candidates = candidates.filter( + ai_analysis_data__analysis_data__screening_stage_rating=screening_rating + ) if tier1_count > 0: candidates = candidates[:tier1_count] @@ -1565,33 +1765,29 @@ def candidate_screening_view(request, slug): context = { "job": job, "candidates": candidates, - 'min_ai_score':min_ai_score, - 'min_experience':min_experience, - 'screening_rating':screening_rating, - 'tier1_count':tier1_count, - "current_stage" : "Applied" + "min_ai_score": min_ai_score, + "min_experience": min_experience, + "screening_rating": screening_rating, + "tier1_count": tier1_count, + "current_stage": "Applied", } return render(request, "recruitment/candidate_screening_view.html", context) -@login_required +@staff_user_required def candidate_exam_view(request, slug): """ Manage candidate tiers and stage transitions """ job = get_object_or_404(JobPosting, slug=slug) - context = { - "job": job, - "candidates": job.exam_candidates, - 'current_stage' : "Exam" - } + context = {"job": job, "candidates": job.exam_candidates, "current_stage": "Exam"} return render(request, "recruitment/candidate_exam_view.html", context) -@login_required +@staff_user_required def update_candidate_exam_status(request, slug): - candidate = get_object_or_404(Candidate, slug=slug) + candidate = get_object_or_404(Application, slug=slug) if request.method == "POST": form = CandidateExamDateForm(request.POST, instance=candidate) if form.is_valid(): @@ -1599,11 +1795,17 @@ def update_candidate_exam_status(request, slug): return redirect("candidate_exam_view", slug=candidate.job.slug) else: form = CandidateExamDateForm(request.POST, instance=candidate) - return render(request, "includes/candidate_exam_status_form.html", {"candidate": candidate,"form": form}) -@login_required -def bulk_update_candidate_exam_status(request,slug): + return render( + request, + "includes/candidate_exam_status_form.html", + {"candidate": candidate, "form": form}, + ) + + +@staff_user_required +def bulk_update_candidate_exam_status(request, slug): job = get_object_or_404(JobPosting, slug=slug) - status = request.headers.get('status') + status = request.headers.get("status") if status: for candidate in get_candidates_from_request(request): try: @@ -1618,71 +1820,140 @@ def bulk_update_candidate_exam_status(request,slug): messages.success(request, f"Updated exam status selected candidates") return redirect("candidate_exam_view", slug=job.slug) + def candidate_criteria_view_htmx(request, pk): - candidate = get_object_or_404(Candidate, pk=pk) - return render(request, "includes/candidate_modal_body.html", {"candidate": candidate}) + candidate = get_object_or_404(Application, pk=pk) + return render( + request, "includes/candidate_modal_body.html", {"candidate": candidate} + ) -@login_required +@staff_user_required def candidate_set_exam_date(request, slug): - candidate = get_object_or_404(Candidate, slug=slug) + candidate = get_object_or_404(Application, slug=slug) candidate.exam_date = timezone.now() candidate.save() - messages.success(request, f"Set exam date for {candidate.name} to {candidate.exam_date}") + messages.success( + request, f"Set exam date for {candidate.name} to {candidate.exam_date}" + ) return redirect("candidate_screening_view", slug=candidate.job.slug) -@login_required + +@staff_user_required def candidate_update_status(request, slug): job = get_object_or_404(JobPosting, slug=slug) - mark_as = request.POST.get('mark_as') - - if mark_as != '----------': + mark_as = request.POST.get("mark_as") + + if mark_as != "----------": candidate_ids = request.POST.getlist("candidate_ids") print(candidate_ids) - if c := Candidate.objects.filter(pk__in = candidate_ids): - - if mark_as=='Exam': - c.update(exam_date=timezone.now(),interview_date=None,offer_date=None,hired_date=None,stage=mark_as,applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") - elif mark_as=='Interview': + if c := Application.objects.filter(pk__in=candidate_ids): + if mark_as == "Exam": + c.update( + exam_date=timezone.now(), + interview_date=None, + offer_date=None, + hired_date=None, + stage=mark_as, + applicant_status="Candidate" + if mark_as in ["Exam", "Interview", "Offer"] + else "Applicant", + ) + elif mark_as == "Interview": # interview_date update when scheduling the interview - c.update(stage=mark_as,offer_date=None,hired_date=None,applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") - elif mark_as=='Offer': - c.update(stage=mark_as,offer_date=timezone.now(),hired_date=None,applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") - elif mark_as=='Hired': - print('hired') - c.update(stage=mark_as,hired_date=timezone.now(),applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") + c.update( + stage=mark_as, + offer_date=None, + hired_date=None, + applicant_status="Candidate" + if mark_as in ["Exam", "Interview", "Offer"] + else "Applicant", + ) + elif mark_as == "Offer": + c.update( + stage=mark_as, + offer_date=timezone.now(), + hired_date=None, + applicant_status="Candidate" + if mark_as in ["Exam", "Interview", "Offer"] + else "Applicant", + ) + elif mark_as == "Hired": + print("hired") + c.update( + stage=mark_as, + hired_date=timezone.now(), + applicant_status="Candidate" + if mark_as in ["Exam", "Interview", "Offer"] + else "Applicant", + ) else: - c.update(stage=mark_as,exam_date=None,interview_date=None,offer_date=None,hired_date=None,applicant_status="Candidate" if mark_as in ["Exam","Interview","Offer"] else "Applicant") - - + c.update( + stage=mark_as, + exam_date=None, + interview_date=None, + offer_date=None, + hired_date=None, + applicant_status="Candidate" + if mark_as in ["Exam", "Interview", "Offer"] + else "Applicant", + ) messages.success(request, f"Candidates Updated") response = HttpResponse(redirect("candidate_screening_view", slug=job.slug)) response.headers["HX-Refresh"] = "true" return response -@login_required -def candidate_interview_view(request,slug): - job = get_object_or_404(JobPosting,slug=slug) +@staff_user_required +def candidate_interview_view(request, slug): + job = get_object_or_404(JobPosting, slug=slug) + + if request.method == "POST": + form = ParticipantsSelectForm(request.POST, instance=job) + print(form.errors) + + if form.is_valid(): + # Save the main instance (JobPosting) + job_instance = form.save(commit=False) + job_instance.save() + + # MANUALLY set the M2M relationships based on submitted data + job_instance.participants.set(form.cleaned_data["participants"]) + job_instance.users.set(form.cleaned_data["users"]) + + messages.success(request, "Interview participants updated successfully.") + return redirect("candidate_interview_view", slug=job.slug) + + else: + initial_data = { + "participants": job.participants.all(), + "users": job.users.all(), + } + form = ParticipantsSelectForm(instance=job, initial=initial_data) + + else: + form = ParticipantsSelectForm(instance=job) context = { - "job":job, - "candidates":job.interview_candidates, - 'current_stage':'Interview', - + "job": job, + "candidates": job.interview_candidates, + "current_stage": "Interview", + "form": form, + "participants_count": job.participants.count() + job.users.count(), } - return render(request,"recruitment/candidate_interview_view.html",context) + return render(request, "recruitment/candidate_interview_view.html", context) -@login_required -def reschedule_meeting_for_candidate(request,slug,candidate_id,meeting_id): - job = get_object_or_404(JobPosting,slug=slug) - candidate = get_object_or_404(Candidate,pk=candidate_id) - meeting = get_object_or_404(ZoomMeeting,pk=meeting_id) + +@staff_user_required +def reschedule_meeting_for_candidate(request, slug, candidate_id, meeting_id): + job = get_object_or_404(JobPosting, slug=slug) + candidate = get_object_or_404(Application, pk=candidate_id) + meeting = get_object_or_404(ZoomMeeting, pk=meeting_id) form = ZoomMeetingForm(instance=meeting) if request.method == "POST": - form = ZoomMeetingForm(request.POST,instance=meeting) + form = ZoomMeetingForm(request.POST, instance=meeting) if form.is_valid(): instance = form.save(commit=False) updated_data = { @@ -1692,7 +1963,12 @@ def reschedule_meeting_for_candidate(request,slug,candidate_id,meeting_id): } if instance.start_time < timezone.now(): messages.error(request, "Start time must be in the future.") - return redirect("reschedule_meeting_for_candidate",slug=job.slug,candidate_id=candidate_id,meeting_id=meeting_id) + return redirect( + "reschedule_meeting_for_candidate", + slug=job.slug, + candidate_id=candidate_id, + meeting_id=meeting_id, + ) result = update_meeting(instance, updated_data) @@ -1700,46 +1976,62 @@ def reschedule_meeting_for_candidate(request,slug,candidate_id,meeting_id): messages.success(request, result["message"]) else: messages.error(request, result["message"]) - return redirect(reverse("candidate_interview_view", kwargs={"slug": job.slug})) + return redirect( + reverse("candidate_interview_view", kwargs={"slug": job.slug}) + ) - context = {"job":job,"candidate":candidate,"meeting":meeting,"form":form} - return render(request,"meetings/reschedule_meeting.html",context) + context = {"job": job, "candidate": candidate, "meeting": meeting, "form": form} + return render(request, "meetings/reschedule_meeting.html", context) -@login_required -def delete_meeting_for_candidate(request,slug,candidate_pk,meeting_id): - job = get_object_or_404(JobPosting,slug=slug) - candidate = get_object_or_404(Candidate,pk=candidate_pk) - meeting = get_object_or_404(ZoomMeeting,pk=meeting_id) +@staff_user_required +def delete_meeting_for_candidate(request, slug, candidate_pk, meeting_id): + job = get_object_or_404(JobPosting, slug=slug) + candidate = get_object_or_404(Application, pk=candidate_pk) + meeting = get_object_or_404(ZoomMeeting, pk=meeting_id) if request.method == "POST": result = delete_zoom_meeting(meeting.meeting_id) - if result["status"] == "success" or "Meeting does not exist" in result["details"]["message"]: + if ( + result["status"] == "success" + or "Meeting does not exist" in result["details"]["message"] + ): meeting.delete() messages.success(request, "Meeting deleted successfully") else: messages.error(request, result["message"]) return redirect(reverse("candidate_interview_view", kwargs={"slug": job.slug})) - context = {"job":job,"candidate":candidate,"meeting":meeting,'delete_url':reverse("delete_meeting_for_candidate",kwargs={"slug":job.slug,"candidate_pk":candidate_pk,"meeting_id":meeting_id})} - return render(request,"meetings/delete_meeting_form.html",context) + context = { + "job": job, + "candidate": candidate, + "meeting": meeting, + "delete_url": reverse( + "delete_meeting_for_candidate", + kwargs={ + "slug": job.slug, + "candidate_pk": candidate_pk, + "meeting_id": meeting_id, + }, + ), + } + return render(request, "meetings/delete_meeting_form.html", context) -@login_required +@staff_user_required def interview_calendar_view(request, slug): job = get_object_or_404(JobPosting, slug=slug) # Get all scheduled interviews for this job - scheduled_interviews = ScheduledInterview.objects.filter( - job=job - ).select_related('candidate', 'zoom_meeting') + scheduled_interviews = ScheduledInterview.objects.filter(job=job).select_related( + "applicaton", "zoom_meeting" + ) # Convert interviews to calendar events events = [] for interview in scheduled_interviews: # Create start datetime start_datetime = datetime.combine( - interview.interview_date, - interview.interview_time + interview.interview_date, interview.interview_time ) # Calculate end datetime based on interview duration @@ -1747,53 +2039,55 @@ def interview_calendar_view(request, slug): end_datetime = start_datetime + timedelta(minutes=duration) # Determine event color based on status - color = '#00636e' # Default color - if interview.status == 'confirmed': - color = '#00a86b' # Green for confirmed - elif interview.status == 'cancelled': - color = '#e74c3c' # Red for cancelled - elif interview.status == 'completed': - color = '#95a5a6' # Gray for completed + color = "#00636e" # Default color + if interview.status == "confirmed": + color = "#00a86b" # Green for confirmed + elif interview.status == "cancelled": + color = "#e74c3c" # Red for cancelled + elif interview.status == "completed": + color = "#95a5a6" # Gray for completed - events.append({ - 'title': f"Interview: {interview.candidate.name}", - 'start': start_datetime.isoformat(), - 'end': end_datetime.isoformat(), - 'url': f"{request.path}interview/{interview.id}/", - 'color': color, - 'extendedProps': { - 'candidate': interview.candidate.name, - 'email': interview.candidate.email, - 'status': interview.status, - 'meeting_id': interview.zoom_meeting.meeting_id if interview.zoom_meeting else None, - 'join_url': interview.zoom_meeting.join_url if interview.zoom_meeting else None, + events.append( + { + "title": f"Interview: {interview.candidate.name}", + "start": start_datetime.isoformat(), + "end": end_datetime.isoformat(), + "url": f"{request.path}interview/{interview.id}/", + "color": color, + "extendedProps": { + "candidate": interview.candidate.name, + "email": interview.candidate.email, + "status": interview.status, + "meeting_id": interview.zoom_meeting.meeting_id + if interview.zoom_meeting + else None, + "join_url": interview.zoom_meeting.join_url + if interview.zoom_meeting + else None, + }, } - }) + ) context = { - 'job': job, - 'events': events, - 'calendar_color': '#00636e', + "job": job, + "events": events, + "calendar_color": "#00636e", } - return render(request, 'recruitment/interview_calendar.html', context) + return render(request, "recruitment/interview_calendar.html", context) -@login_required +@staff_user_required def interview_detail_view(request, slug, interview_id): job = get_object_or_404(JobPosting, slug=slug) - interview = get_object_or_404( - ScheduledInterview, - id=interview_id, - job=job - ) + interview = get_object_or_404(ScheduledInterview, id=interview_id, job=job) context = { - 'job': job, - 'interview': interview, + "job": job, + "interview": interview, } - return render(request, 'recruitment/interview_detail.html', context) + return render(request, "recruitment/interview_detail.html", context) # Candidate Meeting Scheduling/Rescheduling Views @@ -1804,14 +2098,16 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): Returns JSON response for modal update. """ job = get_object_or_404(JobPosting, slug=job_slug) - candidate = get_object_or_404(Candidate, pk=candidate_pk, job=job) + candidate = get_object_or_404(Application, pk=candidate_pk, job=job) topic = f"Interview: {job.title} with {candidate.name}" - start_time_str = request.POST.get('start_time') - duration = int(request.POST.get('duration', 60)) + start_time_str = request.POST.get("start_time") + duration = int(request.POST.get("duration", 60)) if not start_time_str: - return JsonResponse({'success': False, 'error': 'Start time is required.'}, status=400) + return JsonResponse( + {"success": False, "error": "Start time is required."}, status=400 + ) try: # Parse datetime from datetime-local input (YYYY-MM-DDTHH:MM) @@ -1821,12 +2117,17 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): # For simplicity, assuming create_zoom_meeting handles naive datetimes or they are in UTC. # If start_time is expected to be in a specific timezone, convert it here. # e.g., start_time = timezone.make_aware(naive_start_time, timezone.get_current_timezone()) - start_time = naive_start_time # Or timezone.make_aware(naive_start_time) + start_time = naive_start_time # Or timezone.make_aware(naive_start_time) except ValueError: - return JsonResponse({'success': False, 'error': 'Invalid date/time format for start time.'}, status=400) + return JsonResponse( + {"success": False, "error": "Invalid date/time format for start time."}, + status=400, + ) if start_time <= timezone.now(): - return JsonResponse({'success': False, 'error': 'Start time must be in the future.'}, status=400) + return JsonResponse( + {"success": False, "error": "Start time must be in the future."}, status=400 + ) result = create_zoom_meeting(topic=topic, start_time=start_time, duration=duration) @@ -1834,7 +2135,7 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): zoom_meeting_details = result["meeting_details"] zoom_meeting = ZoomMeeting.objects.create( topic=topic, - start_time=start_time, # Store in local timezone + start_time=start_time, # Store in local timezone duration=duration, meeting_id=zoom_meeting_details["meeting_id"], join_url=zoom_meeting_details["join_url"], @@ -1849,24 +2150,26 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): zoom_meeting=zoom_meeting, interview_date=start_time.date(), interview_time=start_time.time(), - status='scheduled' # Or 'confirmed' depending on your workflow + status="scheduled", # Or 'confirmed' depending on your workflow ) messages.success(request, f"Meeting scheduled with {candidate.name}.") # Return updated table row or a success message # For HTMX, you might want to return a fragment of the updated table # For now, returning JSON to indicate success and close modal - return JsonResponse({ - 'success': True, - 'message': 'Meeting scheduled successfully!', - 'join_url': zoom_meeting.join_url, - 'meeting_id': zoom_meeting.meeting_id, - 'candidate_name': candidate.name, - 'interview_datetime': start_time.strftime("%Y-%m-%d %H:%M") - }) + return JsonResponse( + { + "success": True, + "message": "Meeting scheduled successfully!", + "join_url": zoom_meeting.join_url, + "meeting_id": zoom_meeting.meeting_id, + "candidate_name": candidate.name, + "interview_datetime": start_time.strftime("%Y-%m-%d %H:%M"), + } + ) else: messages.error(request, result["message"]) - return JsonResponse({'success': False, 'error': result["message"]}, status=400) + return JsonResponse({"success": False, "error": result["message"]}, status=400) def schedule_candidate_meeting(request, job_slug, candidate_pk): @@ -1875,17 +2178,20 @@ def schedule_candidate_meeting(request, job_slug, candidate_pk): POST: Handled by api_schedule_candidate_meeting. """ job = get_object_or_404(JobPosting, slug=job_slug) - candidate = get_object_or_404(Candidate, pk=candidate_pk, job=job) + candidate = get_object_or_404(Application, pk=candidate_pk, job=job) if request.method == "POST": return api_schedule_candidate_meeting(request, job_slug, candidate_pk) # GET request - render the form snippet for HTMX context = { - 'job': job, - 'candidate': candidate, - 'action_url': reverse('api_schedule_candidate_meeting', kwargs={'job_slug': job_slug, 'candidate_pk': candidate_pk}), - 'scheduled_interview': None, # Explicitly None for schedule + "job": job, + "candidate": candidate, + "action_url": reverse( + "api_schedule_candidate_meeting", + kwargs={"job_slug": job_slug, "candidate_pk": candidate_pk}, + ), + "scheduled_interview": None, # Explicitly None for schedule } # Render just the form part, or the whole modal body content return render(request, "includes/meeting_form.html", context) @@ -1897,34 +2203,44 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): Handles GET to render form and POST to process scheduling. """ job = get_object_or_404(JobPosting, slug=job_slug) - candidate = get_object_or_404(Candidate, pk=candidate_pk, job=job) + candidate = get_object_or_404(Application, pk=candidate_pk, job=job) if request.method == "GET": # This GET is for HTMX to fetch the form context = { - 'job': job, - 'candidate': candidate, - 'action_url': reverse('api_schedule_candidate_meeting', kwargs={'job_slug': job_slug, 'candidate_pk': candidate_pk}), - 'scheduled_interview': None, + "job": job, + "candidate": candidate, + "action_url": reverse( + "api_schedule_candidate_meeting", + kwargs={"job_slug": job_slug, "candidate_pk": candidate_pk}, + ), + "scheduled_interview": None, } return render(request, "includes/meeting_form.html", context) # POST logic (remains the same) topic = f"Interview: {job.title} with {candidate.name}" - start_time_str = request.POST.get('start_time') - duration = int(request.POST.get('duration', 60)) + start_time_str = request.POST.get("start_time") + duration = int(request.POST.get("duration", 60)) if not start_time_str: - return JsonResponse({'success': False, 'error': 'Start time is required.'}, status=400) + return JsonResponse( + {"success": False, "error": "Start time is required."}, status=400 + ) try: naive_start_time = datetime.fromisoformat(start_time_str) start_time = naive_start_time except ValueError: - return JsonResponse({'success': False, 'error': 'Invalid date/time format for start time.'}, status=400) + return JsonResponse( + {"success": False, "error": "Invalid date/time format for start time."}, + status=400, + ) if start_time <= timezone.now(): - return JsonResponse({'success': False, 'error': 'Start time must be in the future.'}, status=400) + return JsonResponse( + {"success": False, "error": "Start time must be in the future."}, status=400 + ) result = create_zoom_meeting(topic=topic, start_time=start_time, duration=duration) @@ -1947,20 +2263,22 @@ def api_schedule_candidate_meeting(request, job_slug, candidate_pk): zoom_meeting=zoom_meeting, interview_date=start_time.date(), interview_time=start_time.time(), - status='scheduled' + status="scheduled", ) messages.success(request, f"Meeting scheduled with {candidate.name}.") - return JsonResponse({ - 'success': True, - 'message': 'Meeting scheduled successfully!', - 'join_url': zoom_meeting.join_url, - 'meeting_id': zoom_meeting.meeting_id, - 'candidate_name': candidate.name, - 'interview_datetime': start_time.strftime("%Y-%m-%d %H:%M") - }) + return JsonResponse( + { + "success": True, + "message": "Meeting scheduled successfully!", + "join_url": zoom_meeting.join_url, + "meeting_id": zoom_meeting.meeting_id, + "candidate_name": candidate.name, + "interview_datetime": start_time.strftime("%Y-%m-%d %H:%M"), + } + ) else: messages.error(request, result["message"]) - return JsonResponse({'success': False, 'error': result["message"]}, status=400) + return JsonResponse({"success": False, "error": result["message"]}, status=400) @require_http_methods(["GET", "POST"]) @@ -1970,44 +2288,58 @@ def api_reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_ """ job = get_object_or_404(JobPosting, slug=job_slug) scheduled_interview = get_object_or_404( - ScheduledInterview.objects.select_related('zoom_meeting'), + ScheduledInterview.objects.select_related("zoom_meeting"), pk=interview_pk, - candidate__pk=candidate_pk, - job=job + application__pk=candidate_pk, + job=job, ) zoom_meeting = scheduled_interview.zoom_meeting if request.method == "GET": # This GET is for HTMX to fetch the form initial_data = { - 'topic': zoom_meeting.topic, - 'start_time': zoom_meeting.start_time.strftime('%Y-%m-%dT%H:%M'), - 'duration': zoom_meeting.duration, + "topic": zoom_meeting.topic, + "start_time": zoom_meeting.start_time.strftime("%Y-%m-%dT%H:%M"), + "duration": zoom_meeting.duration, } context = { - 'job': job, - 'candidate': scheduled_interview.candidate, - 'scheduled_interview': scheduled_interview, # Pass for conditional logic in template - 'initial_data': initial_data, - 'action_url': reverse('api_reschedule_candidate_meeting', kwargs={'job_slug': job_slug, 'candidate_pk': candidate_pk, 'interview_pk': interview_pk}) + "job": job, + "candidate": scheduled_interview.application, + "scheduled_interview": scheduled_interview, # Pass for conditional logic in template + "initial_data": initial_data, + "action_url": reverse( + "api_reschedule_candidate_meeting", + kwargs={ + "job_slug": job_slug, + "candidate_pk": candidate_pk, + "interview_pk": interview_pk, + }, + ), } return render(request, "includes/meeting_form.html", context) # POST logic (remains the same) - new_start_time_str = request.POST.get('start_time') - new_duration = int(request.POST.get('duration', zoom_meeting.duration)) + new_start_time_str = request.POST.get("start_time") + new_duration = int(request.POST.get("duration", zoom_meeting.duration)) if not new_start_time_str: - return JsonResponse({'success': False, 'error': 'New start time is required.'}, status=400) + return JsonResponse( + {"success": False, "error": "New start time is required."}, status=400 + ) try: naive_new_start_time = datetime.fromisoformat(new_start_time_str) new_start_time = naive_new_start_time except ValueError: - return JsonResponse({'success': False, 'error': 'Invalid date/time format for new start time.'}, status=400) + return JsonResponse( + {"success": False, "error": "Invalid date/time format for new start time."}, + status=400, + ) if new_start_time <= timezone.now(): - return JsonResponse({'success': False, 'error': 'Start time must be in the future.'}, status=400) + return JsonResponse( + {"success": False, "error": "Start time must be in the future."}, status=400 + ) updated_data = { "topic": f"Interview: {job.title} with {scheduled_interview.candidate.name}", @@ -2024,36 +2356,52 @@ def api_reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_ zoom_meeting.topic = updated_zoom_details.get("topic", zoom_meeting.topic) zoom_meeting.start_time = new_start_time zoom_meeting.duration = new_duration - zoom_meeting.join_url = updated_zoom_details.get("join_url", zoom_meeting.join_url) - zoom_meeting.password = updated_zoom_details.get("password", zoom_meeting.password) - zoom_meeting.status = updated_zoom_details.get("status", zoom_meeting.status) + zoom_meeting.join_url = updated_zoom_details.get( + "join_url", zoom_meeting.join_url + ) + zoom_meeting.password = updated_zoom_details.get( + "password", zoom_meeting.password + ) + zoom_meeting.status = updated_zoom_details.get( + "status", zoom_meeting.status + ) zoom_meeting.zoom_gateway_response = updated_zoom_details zoom_meeting.save() scheduled_interview.interview_date = new_start_time.date() scheduled_interview.interview_time = new_start_time.time() - scheduled_interview.status = 'rescheduled' + scheduled_interview.status = "rescheduled" scheduled_interview.save() - messages.success(request, f"Meeting for {scheduled_interview.candidate.name} rescheduled.") + messages.success( + request, + f"Meeting for {scheduled_interview.candidate.name} rescheduled.", + ) else: - logger.warning(f"Zoom meeting {zoom_meeting.meeting_id} updated, but failed to fetch latest details.") + logger.warning( + f"Zoom meeting {zoom_meeting.meeting_id} updated, but failed to fetch latest details." + ) zoom_meeting.start_time = new_start_time zoom_meeting.duration = new_duration zoom_meeting.save() scheduled_interview.interview_date = new_start_time.date() scheduled_interview.interview_time = new_start_time.time() scheduled_interview.save() - messages.success(request, f"Meeting for {scheduled_interview.candidate.name} rescheduled. (Note: Could not refresh all details from Zoom.)") + messages.success( + request, + f"Meeting for {scheduled_interview.candidate.name} rescheduled. (Note: Could not refresh all details from Zoom.)", + ) - return JsonResponse({ - 'success': True, - 'message': 'Meeting rescheduled successfully!', - 'join_url': zoom_meeting.join_url, - 'new_interview_datetime': new_start_time.strftime("%Y-%m-%d %H:%M") - }) + return JsonResponse( + { + "success": True, + "message": "Meeting rescheduled successfully!", + "join_url": zoom_meeting.join_url, + "new_interview_datetime": new_start_time.strftime("%Y-%m-%d %H:%M"), + } + ) else: messages.error(request, result["message"]) - return JsonResponse({'success': False, 'error': result["message"]}, status=400) + return JsonResponse({"success": False, "error": result["message"]}, status=400) # The original schedule_candidate_meeting and reschedule_candidate_meeting (without api_ prefix) @@ -2069,12 +2417,12 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): Handles POST to process the rescheduling of a meeting. """ job = get_object_or_404(JobPosting, slug=job_slug) - candidate = get_object_or_404(Candidate, pk=candidate_pk, job=job) + application = get_object_or_404(Application, pk=candidate_pk, job=job) scheduled_interview = get_object_or_404( - ScheduledInterview.objects.select_related('zoom_meeting'), + ScheduledInterview.objects.select_related("zoom_meeting"), pk=interview_pk, - candidate=candidate, - job=job + application=application, + job=job, ) zoom_meeting = scheduled_interview.zoom_meeting @@ -2084,7 +2432,7 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): # If candidate.has_future_meeting is True, it implies they have at least one other upcoming meeting, # or the specific meeting being rescheduled is itself in the future. # We can refine this logic if needed, e.g., check for meetings *other than* the current `interview_pk`. - has_other_future_meetings = candidate.has_future_meeting + has_other_future_meetings = application.has_future_meeting # More precise check: if the current meeting being rescheduled is in the future, then by definition # the candidate will have a future meeting (this one). The UI might want to know if there are *others*. # For now, `candidate.has_future_meeting` is a good general indicator. @@ -2092,29 +2440,42 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): if request.method == "POST": form = ZoomMeetingForm(request.POST) if form.is_valid(): - new_topic = form.cleaned_data.get('topic') - new_start_time = form.cleaned_data.get('start_time') - new_duration = form.cleaned_data.get('duration') + new_topic = form.cleaned_data.get("topic") + new_start_time = form.cleaned_data.get("start_time") + new_duration = form.cleaned_data.get("duration") # Use a default topic if not provided, keeping with the original structure if not new_topic: - new_topic = f"Interview: {job.title} with {candidate.name}" + new_topic = f"Interview: {job.title} with {application.name}" # Ensure new_start_time is in the future if new_start_time <= timezone.now(): messages.error(request, "Start time must be in the future.") # Re-render form with error and initial data - return render(request, "recruitment/schedule_meeting_form.html", { # Reusing the same form template - 'form': form, - 'job': job, - 'candidate': candidate, - 'scheduled_interview': scheduled_interview, - 'initial_topic': new_topic, - 'initial_start_time': new_start_time.strftime('%Y-%m-%dT%H:%M') if new_start_time else '', - 'initial_duration': new_duration, - 'action_url': reverse('reschedule_candidate_meeting', kwargs={'job_slug': job_slug, 'candidate_pk': candidate_pk, 'interview_pk': interview_pk}), - 'has_future_meeting': has_other_future_meetings # Pass status for template - }) + return render( + request, + "recruitment/schedule_meeting_form.html", + { # Reusing the same form template + "form": form, + "job": job, + "application": application, + "scheduled_interview": scheduled_interview, + "initial_topic": new_topic, + "initial_start_time": new_start_time.strftime("%Y-%m-%dT%H:%M") + if new_start_time + else "", + "initial_duration": new_duration, + "action_url": reverse( + "reschedule_candidate_meeting", + kwargs={ + "job_slug": job_slug, + "candidate_pk": candidate_pk, + "interview_pk": interview_pk, + }, + ), + "has_future_meeting": has_other_future_meetings, # Pass status for template + }, + ) # Prepare data for Zoom API update # The update_zoom_meeting expects start_time as ISO string with 'Z' @@ -2125,7 +2486,9 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): } # Update Zoom meeting using utility function - zoom_update_result = update_zoom_meeting(zoom_meeting.meeting_id, zoom_update_data) + zoom_update_result = update_zoom_meeting( + zoom_meeting.meeting_id, zoom_update_data + ) if zoom_update_result["status"] == "success": # Fetch the latest details from Zoom after successful update @@ -2135,20 +2498,35 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): updated_zoom_details = details_result["meeting_details"] # Update local ZoomMeeting record zoom_meeting.topic = updated_zoom_details.get("topic", new_topic) - zoom_meeting.start_time = new_start_time # Store the original datetime + zoom_meeting.start_time = ( + new_start_time # Store the original datetime + ) zoom_meeting.duration = new_duration - zoom_meeting.join_url = updated_zoom_details.get("join_url", zoom_meeting.join_url) - zoom_meeting.password = updated_zoom_details.get("password", zoom_meeting.password) - zoom_meeting.status = updated_zoom_details.get("status", zoom_meeting.status) - zoom_meeting.zoom_gateway_response = details_result.get("meeting_details") + zoom_meeting.join_url = updated_zoom_details.get( + "join_url", zoom_meeting.join_url + ) + zoom_meeting.password = updated_zoom_details.get( + "password", zoom_meeting.password + ) + zoom_meeting.status = updated_zoom_details.get( + "status", zoom_meeting.status + ) + zoom_meeting.zoom_gateway_response = details_result.get( + "meeting_details" + ) zoom_meeting.save() # Update ScheduledInterview record scheduled_interview.interview_date = new_start_time.date() scheduled_interview.interview_time = new_start_time.time() - scheduled_interview.status = 'rescheduled' # Or 'scheduled' if you prefer + scheduled_interview.status = ( + "rescheduled" # Or 'scheduled' if you prefer + ) scheduled_interview.save() - messages.success(request, f"Meeting for {candidate.name} rescheduled successfully.") + messages.success( + request, + f"Meeting for {application.name} rescheduled successfully.", + ) else: # If fetching details fails, update with form data and log a warning logger.warning( @@ -2163,52 +2541,98 @@ def reschedule_candidate_meeting(request, job_slug, candidate_pk, interview_pk): scheduled_interview.interview_date = new_start_time.date() scheduled_interview.interview_time = new_start_time.time() scheduled_interview.save() - messages.success(request, f"Meeting for {candidate.name} rescheduled. (Note: Could not refresh all details from Zoom.)") + messages.success( + request, + f"Meeting for {application.name} rescheduled. (Note: Could not refresh all details from Zoom.)", + ) - return redirect('candidate_interview_view', slug=job.slug) + return redirect("candidate_interview_view", slug=job.slug) else: - messages.error(request, f"Failed to update Zoom meeting: {zoom_update_result['message']}") + messages.error( + request, + f"Failed to update Zoom meeting: {zoom_update_result['message']}", + ) # Re-render form with error - return render(request, "recruitment/schedule_meeting_form.html", { - 'form': form, - 'job': job, - 'candidate': candidate, - 'scheduled_interview': scheduled_interview, - 'initial_topic': new_topic, - 'initial_start_time': new_start_time.strftime('%Y-%m-%dT%H:%M') if new_start_time else '', - 'initial_duration': new_duration, - 'action_url': reverse('reschedule_candidate_meeting', kwargs={'job_slug': job_slug, 'candidate_pk': candidate_pk, 'interview_pk': interview_pk}), - 'has_future_meeting': has_other_future_meetings - }) + return render( + request, + "recruitment/schedule_meeting_form.html", + { + "form": form, + "job": job, + "application": application, + "scheduled_interview": scheduled_interview, + "initial_topic": new_topic, + "initial_start_time": new_start_time.strftime("%Y-%m-%dT%H:%M") + if new_start_time + else "", + "initial_duration": new_duration, + "action_url": reverse( + "reschedule_candidate_meeting", + kwargs={ + "job_slug": job_slug, + "candidate_pk": candidate_pk, + "interview_pk": interview_pk, + }, + ), + "has_future_meeting": has_other_future_meetings, + }, + ) else: # Form validation errors - return render(request, "recruitment/schedule_meeting_form.html", { - 'form': form, - 'job': job, - 'candidate': candidate, - 'scheduled_interview': scheduled_interview, - 'initial_topic': request.POST.get('topic', new_topic), - 'initial_start_time': request.POST.get('start_time', new_start_time.strftime('%Y-%m-%dT%H:%M') if new_start_time else ''), - 'initial_duration': request.POST.get('duration', new_duration), - 'action_url': reverse('reschedule_candidate_meeting', kwargs={'job_slug': job_slug, 'candidate_pk': candidate_pk, 'interview_pk': interview_pk}), - 'has_future_meeting': has_other_future_meetings - }) - else: # GET request + return render( + request, + "recruitment/schedule_meeting_form.html", + { + "form": form, + "job": job, + "application": application, + "scheduled_interview": scheduled_interview, + "initial_topic": request.POST.get("topic", new_topic), + "initial_start_time": request.POST.get( + "start_time", + new_start_time.strftime("%Y-%m-%dT%H:%M") + if new_start_time + else "", + ), + "initial_duration": request.POST.get("duration", new_duration), + "action_url": reverse( + "reschedule_candidate_meeting", + kwargs={ + "job_slug": job_slug, + "candidate_pk": candidate_pk, + "interview_pk": interview_pk, + }, + ), + "has_future_meeting": has_other_future_meetings, + }, + ) + else: # GET request # Pre-populate form with existing meeting details initial_data = { - 'topic': zoom_meeting.topic, - 'start_time': zoom_meeting.start_time.strftime('%Y-%m-%dT%H:%M'), - 'duration': zoom_meeting.duration, + "topic": zoom_meeting.topic, + "start_time": zoom_meeting.start_time.strftime("%Y-%m-%dT%H:%M"), + "duration": zoom_meeting.duration, } form = ZoomMeetingForm(initial=initial_data) - return render(request, "recruitment/schedule_meeting_form.html", { - 'form': form, - 'job': job, - 'candidate': candidate, - 'scheduled_interview': scheduled_interview, # Pass to template for title/differentiation - 'action_url': reverse('reschedule_candidate_meeting', kwargs={'job_slug': job_slug, 'candidate_pk': candidate_pk, 'interview_pk': interview_pk}), - 'has_future_meeting': has_other_future_meetings # Pass status for template - }) + return render( + request, + "recruitment/schedule_meeting_form.html", + { + "form": form, + "job": job, + "application": application, + "scheduled_interview": scheduled_interview, # Pass to template for title/differentiation + "action_url": reverse( + "reschedule_candidate_meeting", + kwargs={ + "job_slug": job_slug, + "candidate_pk": candidate_pk, + "interview_pk": interview_pk, + }, + ), + "has_future_meeting": has_other_future_meetings, # Pass status for template + }, + ) def schedule_meeting_for_candidate(request, slug, candidate_pk): @@ -2217,14 +2641,14 @@ def schedule_meeting_for_candidate(request, slug, candidate_pk): Handles POST to process the form, create a meeting, and redirect back. """ job = get_object_or_404(JobPosting, slug=slug) - candidate = get_object_or_404(Candidate, pk=candidate_pk, job=job) + candidate = get_object_or_404(Application, pk=candidate_pk, job=job) if request.method == "POST": form = ZoomMeetingForm(request.POST) if form.is_valid(): - topic_val = form.cleaned_data.get('topic') - start_time_val = form.cleaned_data.get('start_time') - duration_val = form.cleaned_data.get('duration') + topic_val = form.cleaned_data.get("topic") + start_time_val = form.cleaned_data.get("start_time") + duration_val = form.cleaned_data.get("duration") # Use a default topic if not provided if not topic_val: @@ -2234,7 +2658,7 @@ def schedule_meeting_for_candidate(request, slug, candidate_pk): if start_time_val <= timezone.now(): messages.error(request, "Start time must be in the future.") # Re-render form with error and initial data - return redirect('candidate_interview_view', slug=job.slug) + return redirect("candidate_interview_view", slug=job.slug) # return render(request, "recruitment/schedule_meeting_form.html", { # 'form': form, # 'job': job, @@ -2249,94 +2673,120 @@ def schedule_meeting_for_candidate(request, slug, candidate_pk): # and handles its own conversion to UTC for the API call. zoom_creation_result = create_zoom_meeting( topic=topic_val, - start_time=start_time_val, # Pass the datetime object - duration=duration_val + start_time=start_time_val, # Pass the datetime object + duration=duration_val, ) if zoom_creation_result["status"] == "success": zoom_details = zoom_creation_result["meeting_details"] zoom_meeting_instance = ZoomMeeting.objects.create( topic=topic_val, - start_time=start_time_val, # Store the original datetime + start_time=start_time_val, # Store the original datetime duration=duration_val, meeting_id=zoom_details["meeting_id"], join_url=zoom_details["join_url"], - password=zoom_details.get("password"), # password might be None - status=zoom_creation_result["zoom_gateway_response"].get("status", "waiting"), + password=zoom_details.get("password"), # password might be None + status=zoom_creation_result["zoom_gateway_response"].get( + "status", "waiting" + ), zoom_gateway_response=zoom_creation_result["zoom_gateway_response"], ) # Create a ScheduledInterview record ScheduledInterview.objects.create( - candidate=candidate, + application=candidate, job=job, zoom_meeting=zoom_meeting_instance, interview_date=start_time_val.date(), interview_time=start_time_val.time(), - status='scheduled' + status="scheduled", ) messages.success(request, f"Meeting scheduled with {candidate.name}.") - return redirect('candidate_interview_view', slug=job.slug) + return redirect("candidate_interview_view", slug=job.slug) else: - messages.error(request, f"Failed to create Zoom meeting: {zoom_creation_result['message']}") + messages.error( + request, + f"Failed to create Zoom meeting: {zoom_creation_result['message']}", + ) # Re-render form with error - return render(request, "recruitment/schedule_meeting_form.html", { - 'form': form, - 'job': job, - 'candidate': candidate, - 'initial_topic': topic_val, - 'initial_start_time': start_time_val.strftime('%Y-%m-%dT%H:%M') if start_time_val else '', - 'initial_duration': duration_val - }) + return render( + request, + "recruitment/schedule_meeting_form.html", + { + "form": form, + "job": job, + "candidate": candidate, + "initial_topic": topic_val, + "initial_start_time": start_time_val.strftime("%Y-%m-%dT%H:%M") + if start_time_val + else "", + "initial_duration": duration_val, + }, + ) else: # Form validation errors - return render(request, "meetings/schedule_meeting_form.html", { - 'form': form, - 'job': job, - 'candidate': candidate, - 'initial_topic': request.POST.get('topic', f"Interview: {job.title} with {candidate.name}"), - 'initial_start_time': request.POST.get('start_time', ''), - 'initial_duration': request.POST.get('duration', 60) - }) - else: # GET request + return render( + request, + "meetings/schedule_meeting_form.html", + { + "form": form, + "job": job, + "candidate": candidate, + "initial_topic": request.POST.get( + "topic", f"Interview: {job.title} with {candidate.name}" + ), + "initial_start_time": request.POST.get("start_time", ""), + "initial_duration": request.POST.get("duration", 60), + }, + ) + else: # GET request initial_data = { - 'topic': f"Interview: {job.title} with {candidate.name}", - 'start_time': (timezone.now() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%M'), # Default to 1 hour from now - 'duration': 60, # Default duration + "topic": f"Interview: {job.title} with {candidate.name}", + "start_time": (timezone.now() + timedelta(hours=1)).strftime( + "%Y-%m-%dT%H:%M" + ), # Default to 1 hour from now + "duration": 60, # Default duration } form = ZoomMeetingForm(initial=initial_data) - return render(request, "meetings/schedule_meeting_form.html", { - 'form': form, - 'job': job, - 'candidate': candidate - }) + return render( + request, + "meetings/schedule_meeting_form.html", + {"form": form, "job": job, "candidate": candidate}, + ) from django.core.exceptions import ObjectDoesNotExist + def user_profile_image_update(request, pk): user = get_object_or_404(User, pk=pk) try: - instance =user.profile + instance = user.profile except ObjectDoesNotExist as e: Profile.objects.create(user=user) - if request.method == 'POST': - profile_form = ProfileImageUploadForm(request.POST, request.FILES, instance=user.profile) + if request.method == "POST": + profile_form = ProfileImageUploadForm( + request.POST, request.FILES, instance=user.profile + ) if profile_form.is_valid(): profile_form.save() - messages.success(request, 'Image uploaded successfully') - return redirect('user_detail', pk=user.pk) + messages.success(request, "Image uploaded successfully") + return redirect("user_detail", pk=user.pk) else: - messages.error(request, 'An error occurred while uploading image. Please check the errors below.') + messages.error( + request, + "An error occurred while uploading image. Please check the errors below.", + ) else: profile_form = ProfileImageUploadForm(instance=user.profile) context = { - 'profile_form': profile_form, - 'user': user, + "profile_form": profile_form, + "user": user, } - return render(request, 'user/profile.html', context) + return render(request, "user/profile.html", context) + def user_detail(request, pk): user = get_object_or_404(User, pk=pk) @@ -2347,23 +2797,16 @@ def user_detail(request, pk): except: profile_form = ProfileImageUploadForm() - if request.method == 'POST': - first_name=request.POST.get('first_name') - last_name=request.POST.get('last_name') + if request.method == "POST": + first_name = request.POST.get("first_name") + last_name = request.POST.get("last_name") if first_name: - user.first_name=first_name + user.first_name = first_name if last_name: - user.last_name=last_name + user.last_name = last_name user.save() - context = { - - 'user': user, - 'profile_form':profile_form - - } - return render(request, 'user/profile.html', context) - - + context = {"user": user, "profile_form": profile_form} + return render(request, "user/profile.html", context) def easy_logs(request): @@ -2372,56 +2815,50 @@ def easy_logs(request): """ logs_per_page = 20 + active_tab = request.GET.get("tab", "crud") - active_tab = request.GET.get('tab', 'crud') - - if active_tab == 'login': - queryset = LoginEvent.objects.order_by('-datetime') + if active_tab == "login": + queryset = LoginEvent.objects.order_by("-datetime") tab_title = _("User Authentication") - elif active_tab == 'request': - queryset = RequestEvent.objects.order_by('-datetime') + elif active_tab == "request": + queryset = RequestEvent.objects.order_by("-datetime") tab_title = _("HTTP Requests") else: - queryset = CRUDEvent.objects.order_by('-datetime') + queryset = CRUDEvent.objects.order_by("-datetime") tab_title = _("Model Changes (CRUD)") - active_tab = 'crud' - + active_tab = "crud" paginator = Paginator(queryset, logs_per_page) - page = request.GET.get('page') + page = request.GET.get("page") try: - logs_page = paginator.page(page) except PageNotAnInteger: - logs_page = paginator.page(1) except EmptyPage: - logs_page = paginator.page(paginator.num_pages) context = { - 'logs': logs_page, - 'total_count': queryset.count(), - 'active_tab': active_tab, - 'tab_title': tab_title, + "logs": logs_page, + "total_count": queryset.count(), + "active_tab": active_tab, + "tab_title": tab_title, } return render(request, "includes/easy_logs.html", context) - from allauth.account.views import SignupView from django.contrib.auth.decorators import user_passes_test + def is_superuser_check(user): return user.is_superuser -@user_passes_test(is_superuser_check) +@staff_user_required def create_staff_user(request): - if request.method == 'POST': - + if request.method == "POST": form = StaffUserCreationForm(request.POST) print(form) if form.is_valid(): @@ -2429,68 +2866,70 @@ def create_staff_user(request): messages.success( request, f"Staff user {form.cleaned_data['first_name']} {form.cleaned_data['last_name']} " - f"({form.cleaned_data['email']}) created successfully!" + f"({form.cleaned_data['email']}) created successfully!", ) - return redirect('admin_settings') + return redirect("admin_settings") else: form = StaffUserCreationForm() - return render(request, 'user/create_staff.html', {'form': form}) + return render(request, "user/create_staff.html", {"form": form}) - - -@user_passes_test(is_superuser_check) +@staff_user_required def admin_settings(request): - staffs=User.objects.filter(is_superuser=False) + staffs = User.objects.filter(is_superuser=False) form = ToggleAccountForm() - context={ - 'staffs':staffs, - 'form':form - } - return render(request,'user/admin_settings.html',context) + context = {"staffs": staffs, "form": form} + return render(request, "user/admin_settings.html", context) from django.contrib.auth.forms import SetPasswordForm -@user_passes_test(is_superuser_check) -def set_staff_password(request,pk): - user=get_object_or_404(User,pk=pk) + +@staff_user_required +def set_staff_password(request, pk): + user = get_object_or_404(User, pk=pk) print(request.POST) - if request.method=='POST': + if request.method == "POST": form = SetPasswordForm(user, data=request.POST) if form.is_valid(): - form.save() - messages.success(request,f'Password successfully changed') - return redirect('admin_settings') + form.save() + messages.success(request, f"Password successfully changed") + return redirect("admin_settings") else: - form=SetPasswordForm(user=user) - messages.error(request,f'Password does not match please try again.') - return redirect('admin_settings') + form = SetPasswordForm(user=user) + messages.error(request, f"Password does not match please try again.") + return redirect("admin_settings") else: - form=SetPasswordForm(user=user) - return render(request,'user/staff_password_create.html',{'form':form,'user':user}) + form = SetPasswordForm(user=user) + return render( + request, "user/staff_password_create.html", {"form": form, "user": user} + ) -@user_passes_test(is_superuser_check) -def account_toggle_status(request,pk): - user=get_object_or_404(User,pk=pk) - if request.method=='POST': +@staff_user_required +def account_toggle_status(request, pk): + user = get_object_or_404(User, pk=pk) + if request.method == "POST": print(user.is_active) - form=ToggleAccountForm(request.POST) + form = ToggleAccountForm(request.POST) if form.is_valid(): if user.is_active: - user.is_active=False + user.is_active = False user.save() - messages.success(request,f'Staff with email: {user.email} deactivated successfully') - return redirect('admin_settings') + messages.success( + request, f"Staff with email: {user.email} deactivated successfully" + ) + return redirect("admin_settings") else: - user.is_active=True + user.is_active = True user.save() - messages.success(request,f'Staff with email: {user.email} activated successfully') - return redirect('admin_settings') + messages.success( + request, f"Staff with email: {user.email} activated successfully" + ) + return redirect("admin_settings") else: - messages.error(f'Please correct the error below') + messages.error(f"Please correct the error below") # @login_required @@ -2500,12 +2939,13 @@ def account_toggle_status(request,pk): @csrf_exempt +@staff_user_required def zoom_webhook_view(request): print(request.headers) print(settings.ZOOM_WEBHOOK_API_KEY) # if api_key != settings.ZOOM_WEBHOOK_API_KEY: # return HttpResponse(status=405) - if request.method == 'POST': + if request.method == "POST": try: payload = json.loads(request.body) async_task("recruitment.tasks.handle_zoom_webhook_event", payload) @@ -2516,46 +2956,48 @@ def zoom_webhook_view(request): # Meeting Comments Views -@login_required +@staff_user_required def add_meeting_comment(request, slug): """Add a comment to a meeting""" meeting = get_object_or_404(ZoomMeeting, slug=slug) - if request.method == 'POST': + if request.method == "POST": form = MeetingCommentForm(request.POST) if form.is_valid(): comment = form.save(commit=False) comment.meeting = meeting comment.author = request.user comment.save() - messages.success(request, 'Comment added successfully!') + messages.success(request, "Comment added successfully!") # HTMX response - return just the comment section - if 'HX-Request' in request.headers: - return render(request, 'includes/comment_list.html', { - 'comments': meeting.comments.all().order_by('-created_at'), - 'meeting': meeting - }) + if "HX-Request" in request.headers: + return render( + request, + "includes/comment_list.html", + { + "comments": meeting.comments.all().order_by("-created_at"), + "meeting": meeting, + }, + ) - return redirect('meeting_details', slug=slug) + return redirect("meeting_details", slug=slug) else: form = MeetingCommentForm() context = { - 'form': form, - 'meeting': meeting, + "form": form, + "meeting": meeting, } # HTMX response - return the comment form - if 'HX-Request' in request.headers: - return render(request, 'includes/comment_form.html', context) + if "HX-Request" in request.headers: + return render(request, "includes/comment_form.html", context) - return redirect('meeting_details', slug=slug) + return redirect("meeting_details", slug=slug) - - -@login_required +@staff_user_required def edit_meeting_comment(request, slug, comment_id): """Edit a meeting comment""" meeting = get_object_or_404(ZoomMeeting, slug=slug) @@ -2563,35 +3005,35 @@ def edit_meeting_comment(request, slug, comment_id): # Check if user is author if comment.author != request.user and not request.user.is_staff: - messages.error(request, 'You can only edit your own comments.') - return redirect('meeting_details', slug=slug) + messages.error(request, "You can only edit your own comments.") + return redirect("meeting_details", slug=slug) - if request.method == 'POST': + if request.method == "POST": form = MeetingCommentForm(request.POST, instance=comment) if form.is_valid(): comment = form.save() - messages.success(request, 'Comment updated successfully!') + messages.success(request, "Comment updated successfully!") # HTMX response - return just comment section - if 'HX-Request' in request.headers: - return render(request, 'includes/comment_list.html', { - 'comments': meeting.comments.all().order_by('-created_at'), - 'meeting': meeting - }) + if "HX-Request" in request.headers: + return render( + request, + "includes/comment_list.html", + { + "comments": meeting.comments.all().order_by("-created_at"), + "meeting": meeting, + }, + ) - return redirect('meeting_details', slug=slug) + return redirect("meeting_details", slug=slug) else: form = MeetingCommentForm(instance=comment) - context = { - 'form': form, - 'meeting': meeting, - 'comment': comment - } - return render(request, 'includes/edit_comment_form.html', context) + context = {"form": form, "meeting": meeting, "comment": comment} + return render(request, "includes/edit_comment_form.html", context) -@login_required +@staff_user_required def delete_meeting_comment(request, slug, comment_id): """Delete a meeting comment""" meeting = get_object_or_404(ZoomMeeting, slug=slug) @@ -2599,37 +3041,48 @@ def delete_meeting_comment(request, slug, comment_id): # Check if user is the author if comment.author != request.user and not request.user.is_staff: - messages.error(request, 'You can only delete your own comments.') - return redirect('meeting_details', slug=slug) + messages.error(request, "You can only delete your own comments.") + return redirect("meeting_details", slug=slug) - if request.method == 'POST': + if request.method == "POST": comment.delete() - messages.success(request, 'Comment deleted successfully!') + messages.success(request, "Comment deleted successfully!") # HTMX response - return just the comment section - if 'HX-Request' in request.headers: - return render(request, 'includes/comment_list.html', { - 'comments': meeting.comments.all().order_by('-created_at'), - 'meeting': meeting - }) + if "HX-Request" in request.headers: + return render( + request, + "includes/comment_list.html", + { + "comments": meeting.comments.all().order_by("-created_at"), + "meeting": meeting, + }, + ) - return redirect('meeting_details', slug=slug) + return redirect("meeting_details", slug=slug) # HTMX response - return the delete confirmation modal - if 'HX-Request' in request.headers: - return render(request, 'includes/delete_comment_form.html', { - 'meeting': meeting, - 'comment': comment, - 'delete_url': reverse('delete_meeting_comment', kwargs={'slug': slug, 'comment_id': comment_id}) - }) + if "HX-Request" in request.headers: + return render( + request, + "includes/delete_comment_form.html", + { + "meeting": meeting, + "comment": comment, + "delete_url": reverse( + "delete_meeting_comment", + kwargs={"slug": slug, "comment_id": comment_id}, + ), + }, + ) - return redirect('meeting_details', slug=slug) + return redirect("meeting_details", slug=slug) -@login_required -def set_meeting_candidate(request,slug): +@staff_user_required +def set_meeting_candidate(request, slug): meeting = get_object_or_404(ZoomMeeting, slug=slug) - if request.method == 'POST' and 'HX-Request' not in request.headers: + if request.method == "POST" and "HX-Request" not in request.headers: form = InterviewForm(request.POST) if form.is_valid(): candidate = form.save(commit=False) @@ -2637,154 +3090,155 @@ def set_meeting_candidate(request,slug): candidate.interview_date = meeting.start_time.date() candidate.interview_time = meeting.start_time.time() candidate.save() - messages.success(request, 'Candidate added successfully!') - return redirect('list_meetings') + messages.success(request, "Candidate added successfully!") + return redirect("list_meetings") job = request.GET.get("job") form = InterviewForm() if job: - form.fields['candidate'].queryset = Candidate.objects.filter(job=job) + form.fields["candidate"].queryset = Application.objects.filter(job=job) else: - form.fields['candidate'].queryset = Candidate.objects.none() - form.fields['job'].widget.attrs.update({ - 'hx-get': reverse('set_meeting_candidate', kwargs={'slug': slug}), - 'hx-target': '#div_id_candidate', - 'hx-select': '#div_id_candidate', - 'hx-swap': 'outerHTML' - }) - context = { - "form": form, - "meeting": meeting - } - return render(request, 'meetings/set_candidate_form.html', context) + form.fields["candidate"].queryset = Application.objects.none() + form.fields["job"].widget.attrs.update( + { + "hx-get": reverse("set_meeting_candidate", kwargs={"slug": slug}), + "hx-target": "#div_id_candidate", + "hx-select": "#div_id_candidate", + "hx-swap": "outerHTML", + } + ) + context = {"form": form, "meeting": meeting} + return render(request, "meetings/set_candidate_form.html", context) # Hiring Agency CRUD Views -@login_required +@staff_user_required def agency_list(request): """List all hiring agencies with search and pagination""" - search_query = request.GET.get('q', '') + search_query = request.GET.get("q", "") agencies = HiringAgency.objects.all() if search_query: agencies = agencies.filter( - Q(name__icontains=search_query) | - Q(contact_person__icontains=search_query) | - Q(email__icontains=search_query) | - Q(country__icontains=search_query) + Q(name__icontains=search_query) + | Q(contact_person__icontains=search_query) + | Q(email__icontains=search_query) + | Q(country__icontains=search_query) ) # Order by most recently created - agencies = agencies.order_by('-created_at') + agencies = agencies.order_by("-created_at") # Pagination paginator = Paginator(agencies, 10) # Show 10 agencies per page - page_number = request.GET.get('page') + page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) context = { - 'page_obj': page_obj, - 'search_query': search_query, - 'total_agencies': agencies.count(), + "page_obj": page_obj, + "search_query": search_query, + "total_agencies": agencies.count(), } - return render(request, 'recruitment/agency_list.html', context) + return render(request, "recruitment/agency_list.html", context) -@login_required +@staff_user_required def agency_create(request): """Create a new hiring agency""" - if request.method == 'POST': + if request.method == "POST": form = HiringAgencyForm(request.POST) if form.is_valid(): agency = form.save() messages.success(request, f'Agency "{agency.name}" created successfully!') - return redirect('agency_detail', slug=agency.slug) + return redirect("agency_detail", slug=agency.slug) else: - messages.error(request, 'Please correct the errors below.') + messages.error(request, "Please correct the errors below.") else: form = HiringAgencyForm() context = { - 'form': form, - 'title': 'Create New Agency', - 'button_text': 'Create Agency', + "form": form, + "title": "Create New Agency", + "button_text": "Create Agency", } - return render(request, 'recruitment/agency_form.html', context) + return render(request, "recruitment/agency_form.html", context) -@login_required +@staff_user_required def agency_detail(request, slug): """View details of a specific hiring agency""" agency = get_object_or_404(HiringAgency, slug=slug) # Get candidates associated with this agency - candidates = Candidate.objects.filter(hiring_agency=agency).order_by('-created_at') + candidates = Application.objects.filter(hiring_agency=agency).order_by("-created_at") # Statistics total_candidates = candidates.count() - active_candidates = candidates.filter(stage__in=['Applied', 'Screening', 'Exam', 'Interview', 'Offer']).count() - hired_candidates = candidates.filter(stage='Hired').count() - rejected_candidates = candidates.filter(stage='Rejected').count() + active_candidates = candidates.filter( + stage__in=["Applied", "Screening", "Exam", "Interview", "Offer"] + ).count() + hired_candidates = candidates.filter(stage="Hired").count() + rejected_candidates = candidates.filter(stage="Rejected").count() context = { - 'agency': agency, - 'candidates': candidates[:10], # Show recent 10 candidates - 'total_candidates': total_candidates, - 'active_candidates': active_candidates, - 'hired_candidates': hired_candidates, - 'rejected_candidates': rejected_candidates, + "agency": agency, + "candidates": candidates[:10], # Show recent 10 candidates + "total_candidates": total_candidates, + "active_candidates": active_candidates, + "hired_candidates": hired_candidates, + "rejected_candidates": rejected_candidates, } - return render(request, 'recruitment/agency_detail.html', context) + return render(request, "recruitment/agency_detail.html", context) -@login_required +@staff_user_required def agency_update(request, slug): """Update an existing hiring agency""" agency = get_object_or_404(HiringAgency, slug=slug) - if request.method == 'POST': + if request.method == "POST": form = HiringAgencyForm(request.POST, instance=agency) if form.is_valid(): agency = form.save() messages.success(request, f'Agency "{agency.name}" updated successfully!') - return redirect('agency_detail', slug=agency.slug) + return redirect("agency_detail", slug=agency.slug) else: - messages.error(request, 'Please correct the errors below.') + messages.error(request, "Please correct the errors below.") else: form = HiringAgencyForm(instance=agency) context = { - 'form': form, - 'agency': agency, - 'title': f'Edit Agency: {agency.name}', - 'button_text': 'Update Agency', + "form": form, + "agency": agency, + "title": f"Edit Agency: {agency.name}", + "button_text": "Update Agency", } - return render(request, 'recruitment/agency_form.html', context) + return render(request, "recruitment/agency_form.html", context) -@login_required +@staff_user_required def agency_delete(request, slug): """Delete a hiring agency""" agency = get_object_or_404(HiringAgency, slug=slug) - if request.method == 'POST': + if request.method == "POST": agency_name = agency.name agency.delete() messages.success(request, f'Agency "{agency_name}" deleted successfully!') - return redirect('agency_list') + return redirect("agency_list") context = { - 'agency': agency, - 'title': 'Delete Agency', - 'message': f'Are you sure you want to delete the agency "{agency.name}"?', - 'cancel_url': reverse('agency_detail', kwargs={'slug': agency.slug}), + "agency": agency, + "title": "Delete Agency", + "message": f'Are you sure you want to delete the agency "{agency.name}"?', + "cancel_url": reverse("agency_detail", kwargs={"slug": agency.slug}), } - return render(request, 'recruitment/agency_confirm_delete.html', context) + return render(request, "recruitment/agency_confirm_delete.html", context) # Notification Views -# @login_required +# @staff_user_required # def notification_list(request): # """List all notifications for the current user""" # # Get filter parameters @@ -2830,7 +3284,7 @@ def agency_delete(request, slug): # return render(request, 'recruitment/notification_list.html', context) -# @login_required +# @staff_user_required # def notification_detail(request, notification_id): # """View details of a specific notification""" # notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) @@ -2846,7 +3300,7 @@ def agency_delete(request, slug): # return render(request, 'recruitment/notification_detail.html', context) -# @login_required +# @staff_user_required # def notification_mark_read(request, notification_id): # """Mark a notification as read""" # notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) @@ -2861,7 +3315,7 @@ def agency_delete(request, slug): # return redirect('notification_list') -# @login_required +# @staff_user_required # def notification_mark_unread(request, notification_id): # """Mark a notification as unread""" # notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) @@ -2876,7 +3330,7 @@ def agency_delete(request, slug): # return redirect('notification_list') -# @login_required +# @staff_user_required # def notification_delete(request, notification_id): # """Delete a notification""" # notification = get_object_or_404(Notification, id=notification_id, recipient=request.user) @@ -2896,7 +3350,7 @@ def agency_delete(request, slug): # return render(request, 'recruitment/notification_confirm_delete.html', context) -# @login_required +# @staff_user_required # def notification_mark_all_read(request): # """Mark all notifications as read for the current user""" # if request.method == 'POST': @@ -2923,7 +3377,7 @@ def agency_delete(request, slug): # return render(request, 'recruitment/notification_confirm_all_read.html', context) -# @login_required +# @staff_user_required # def api_notification_count(request): # """API endpoint to get unread notification count and recent notifications""" # # Get unread notifications @@ -2972,7 +3426,7 @@ def agency_delete(request, slug): # }) -# @login_required +# @staff_user_required # def notification_stream(request): # """SSE endpoint for real-time notifications""" # from django.http import StreamingHttpResponse @@ -3085,23 +3539,23 @@ def agency_delete(request, slug): # response['X-Accel-Buffering'] = 'no' # Disable buffering for nginx # response['Connection'] = 'keep-alive' - # context = { - # 'agency': agency, - # 'page_obj': page_obj, - # 'stage_filter': stage_filter, - # 'total_candidates': candidates.count(), - # } - # return render(request, 'recruitment/agency_candidates.html', context) +# context = { +# 'agency': agency, +# 'page_obj': page_obj, +# 'stage_filter': stage_filter, +# 'total_candidates': candidates.count(), +# } +# return render(request, 'recruitment/agency_candidates.html', context) -@login_required +@staff_user_required def agency_candidates(request, slug): """View all candidates from a specific agency""" agency = get_object_or_404(HiringAgency, slug=slug) - candidates = Candidate.objects.filter(hiring_agency=agency).order_by('-created_at') + candidates = Application.objects.filter(hiring_agency=agency).order_by("-created_at") # Filter by stage if provided - stage_filter = request.GET.get('stage') + stage_filter = request.GET.get("stage") if stage_filter: candidates = candidates.filter(stage=stage_filter) @@ -3110,35 +3564,33 @@ def agency_candidates(request, slug): # Pagination paginator = Paginator(candidates, 20) # Show 20 candidates per page - page_number = request.GET.get('page') + page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) context = { - 'agency': agency, - 'page_obj': page_obj, - 'stage_filter': stage_filter, - 'total_candidates': total_candidates, + "agency": agency, + "page_obj": page_obj, + "stage_filter": stage_filter, + "total_candidates": total_candidates, } - return render(request, 'recruitment/agency_candidates.html', context) - - + return render(request, "recruitment/agency_candidates.html", context) # Agency Portal Management Views -@login_required +@staff_user_required def agency_assignment_list(request): """List all agency job assignments""" - search_query = request.GET.get('q', '') - status_filter = request.GET.get('status', '') + search_query = request.GET.get("q", "") + status_filter = request.GET.get("status", "") - assignments = AgencyJobAssignment.objects.select_related( - 'agency', 'job' - ).order_by('-created_at') + assignments = AgencyJobAssignment.objects.select_related("agency", "job").order_by( + "-created_at" + ) if search_query: assignments = assignments.filter( - Q(agency__name__icontains=search_query) | - Q(job__title__icontains=search_query) + Q(agency__name__icontains=search_query) + | Q(job__title__icontains=search_query) ) if status_filter: @@ -3146,355 +3598,507 @@ def agency_assignment_list(request): # Pagination paginator = Paginator(assignments, 15) # Show 15 assignments per page - page_number = request.GET.get('page') + page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) context = { - 'page_obj': page_obj, - 'search_query': search_query, - 'status_filter': status_filter, - 'total_assignments': assignments.count(), + "page_obj": page_obj, + "search_query": search_query, + "status_filter": status_filter, + "total_assignments": assignments.count(), } - return render(request, 'recruitment/agency_assignment_list.html', context) + return render(request, "recruitment/agency_assignment_list.html", context) -@login_required -def agency_assignment_create(request,slug=None): +@staff_user_required +def agency_assignment_create(request, slug=None): """Create a new agency job assignment""" agency = HiringAgency.objects.get(slug=slug) if slug else None - if request.method == 'POST': + if request.method == "POST": form = AgencyJobAssignmentForm(request.POST) # if agency: # form.instance.agency = agency if form.is_valid(): assignment = form.save() - messages.success(request, f'Assignment created for {assignment.agency.name} - {assignment.job.title}!') - return redirect('agency_assignment_detail', slug=assignment.slug) + messages.success( + request, + f"Assignment created for {assignment.agency.name} - {assignment.job.title}!", + ) + return redirect("agency_assignment_detail", slug=assignment.slug) else: - messages.error(request, f'Please correct the errors below.{form.errors.as_text()}') + messages.error( + request, f"Please correct the errors below.{form.errors.as_text()}" + ) print(form.errors.as_json()) else: form = AgencyJobAssignmentForm() try: # from django.forms import HiddenInput - form.initial['agency'] = agency + form.initial["agency"] = agency # form.fields['agency'].widget = HiddenInput() except HiringAgency.DoesNotExist: pass context = { - 'form': form, - 'title': 'Create New Assignment', - 'button_text': 'Create Assignment', + "form": form, + "title": "Create New Assignment", + "button_text": "Create Assignment", } - return render(request, 'recruitment/agency_assignment_form.html', context) + return render(request, "recruitment/agency_assignment_form.html", context) -@login_required +@staff_user_required def agency_assignment_detail(request, slug): """View details of a specific agency assignment""" assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency', 'job'), - slug=slug + AgencyJobAssignment.objects.select_related("agency", "job"), slug=slug ) # Get candidates submitted by this agency for this job - candidates = Candidate.objects.filter( - hiring_agency=assignment.agency, - job=assignment.job - ).order_by('-created_at') + candidates = Application.objects.filter( + hiring_agency=assignment.agency, job=assignment.job + ).order_by("-created_at") # Get access link if exists - access_link = getattr(assignment, 'access_link', None) + access_link = getattr(assignment, "access_link", None) # Get messages for this assignment - total_candidates = candidates.count() max_candidates = assignment.max_candidates circumference = 326.73 # 2 * π * r where r=52 if max_candidates > 0: - progress_percentage = (total_candidates / max_candidates) + progress_percentage = total_candidates / max_candidates stroke_dashoffset = circumference - (circumference * progress_percentage) else: stroke_dashoffset = circumference context = { - 'assignment': assignment, - 'candidates': candidates, - 'access_link': access_link, - - 'total_candidates': candidates.count(), - 'stroke_dashoffset': stroke_dashoffset, + "assignment": assignment, + "candidates": candidates, + "access_link": access_link, + "total_candidates": candidates.count(), + "stroke_dashoffset": stroke_dashoffset, } - return render(request, 'recruitment/agency_assignment_detail.html', context) + return render(request, "recruitment/agency_assignment_detail.html", context) -@login_required +@staff_user_required def agency_assignment_update(request, slug): """Update an existing agency assignment""" assignment = get_object_or_404(AgencyJobAssignment, slug=slug) - if request.method == 'POST': + if request.method == "POST": form = AgencyJobAssignmentForm(request.POST, instance=assignment) if form.is_valid(): assignment = form.save() - messages.success(request, f'Assignment updated successfully!') - return redirect('agency_assignment_detail', slug=assignment.slug) + messages.success(request, f"Assignment updated successfully!") + return redirect("agency_assignment_detail", slug=assignment.slug) else: - messages.error(request, 'Please correct the errors below.') + messages.error(request, "Please correct the errors below.") else: form = AgencyJobAssignmentForm(instance=assignment) context = { - 'form': form, - 'assignment': assignment, - 'title': f'Edit Assignment: {assignment.agency.name} - {assignment.job.title}', - 'button_text': 'Update Assignment', + "form": form, + "assignment": assignment, + "title": f"Edit Assignment: {assignment.agency.name} - {assignment.job.title}", + "button_text": "Update Assignment", } - return render(request, 'recruitment/agency_assignment_form.html', context) + return render(request, "recruitment/agency_assignment_form.html", context) -@login_required +@staff_user_required def agency_access_link_create(request): """Create access link for agency assignment""" - if request.method == 'POST': + if request.method == "POST": form = AgencyAccessLinkForm(request.POST) if form.is_valid(): access_link = form.save() - messages.success(request, f'Access link created for {access_link.assignment.agency.name}!') - return redirect('agency_assignment_detail', slug=access_link.assignment.slug) + messages.success( + request, + f"Access link created for {access_link.assignment.agency.name}!", + ) + return redirect( + "agency_assignment_detail", slug=access_link.assignment.slug + ) else: - messages.error(request, 'Please correct the errors below.') + messages.error(request, "Please correct the errors below.") else: form = AgencyAccessLinkForm() context = { - 'form': form, - 'title': 'Create Access Link', - 'button_text': 'Create Link', + "form": form, + "title": "Create Access Link", + "button_text": "Create Link", } - return render(request, 'recruitment/agency_access_link_form.html', context) + return render(request, "recruitment/agency_access_link_form.html", context) -@login_required +@staff_user_required def agency_access_link_detail(request, slug): """View details of an access link""" access_link = get_object_or_404( - AgencyAccessLink.objects.select_related('assignment__agency', 'assignment__job'), - slug=slug + AgencyAccessLink.objects.select_related( + "assignment__agency", "assignment__job" + ), + slug=slug, ) - context = { - 'access_link': access_link, + "access_link": access_link, } - return render(request, 'recruitment/agency_access_link_detail.html', context) + return render(request, "recruitment/agency_access_link_detail.html", context) - - - - - - - - - - - - - - -@login_required +@staff_user_required def agency_assignment_extend_deadline(request, slug): """Extend deadline for an agency assignment""" assignment = get_object_or_404(AgencyJobAssignment, slug=slug) - if request.method == 'POST': - new_deadline = request.POST.get('new_deadline') + if request.method == "POST": + new_deadline = request.POST.get("new_deadline") if new_deadline: try: from datetime import datetime - new_deadline_dt = datetime.fromisoformat(new_deadline.replace('Z', '+00:00')) + + new_deadline_dt = datetime.fromisoformat( + new_deadline.replace("Z", "+00:00") + ) # Ensure the new deadline is timezone-aware if timezone.is_naive(new_deadline_dt): new_deadline_dt = timezone.make_aware(new_deadline_dt) if assignment.extend_deadline(new_deadline_dt): - messages.success(request, f'Deadline extended to {new_deadline_dt.strftime("%Y-%m-%d %H:%M")}!') + messages.success( + request, + f"Deadline extended to {new_deadline_dt.strftime('%Y-%m-%d %H:%M')}!", + ) else: - messages.error(request, 'New deadline must be later than current deadline.') + messages.error( + request, "New deadline must be later than current deadline." + ) except ValueError: - messages.error(request, 'Invalid date format.') + messages.error(request, "Invalid date format.") else: - messages.error(request, 'Please provide a new deadline.') + messages.error(request, "Please provide a new deadline.") - return redirect('agency_assignment_detail', slug=assignment.slug) + return redirect("agency_assignment_detail", slug=assignment.slug) # Agency Portal Views (for external agencies) +@agency_user_required def agency_portal_login(request): """Agency login page""" - if request.session.get('agency_assignment_id'): - return redirect('agency_portal_dashboard') - if request.method == 'POST': + # if request.session.get("agency_assignment_id"): + # return redirect("agency_portal_dashboard") + if request.method == "POST": form = AgencyLoginForm(request.POST) if form.is_valid(): # Check if validated_access_link attribute exists - if hasattr(form, 'validated_access_link'): - access_link = form.validated_access_link - access_link.record_access() + # if hasattr(form, "validated_access_link"): + # access_link = form.validated_access_link + # access_link.record_access() # Store assignment in session - request.session['agency_assignment_id'] = access_link.assignment.id - request.session['agency_name'] = access_link.assignment.agency.name + # request.session["agency_assignment_id"] = access_link.assignment.id + # request.session["agency_name"] = access_link.assignment.agency.name - messages.success(request, f'Welcome, {access_link.assignment.agency.name}!') - return redirect('agency_portal_dashboard') + messages.success(request, f"Welcome, {access_link.assignment.agency.name}!") + return redirect("agency_portal_dashboard") else: - messages.error(request, 'Invalid token or password.') + messages.error(request, "Invalid token or password.") else: form = AgencyLoginForm() context = { - 'form': form, + "form": form, } - return render(request, 'recruitment/agency_portal_login.html', context) + return render(request, "recruitment/agency_portal_login.html", context) +def portal_login(request): + """Unified portal login for agency and candidate""" + if request.user.is_authenticated: + if request.user.user_type == "agency": + return redirect("agency_portal_dashboard") + if request.user.user_type == "candidate": + return redirect("candidate_portal_dashboard") + + if request.method == "POST": + form = PortalLoginForm(request.POST) + + if form.is_valid(): + email = form.cleaned_data["email"] + password = form.cleaned_data["password"] + user_type = form.cleaned_data["user_type"] + + # Authenticate user + user = authenticate(request, username=email, password=password) + if user is not None: + # Check if user type matches + print(user.user_type) + if hasattr(user, "user_type") and user.user_type == user_type: + login(request, user) + return redirect("agency_portal_dashboard") + + # if user_type == "agency": + # # Check if user has agency profile + # if hasattr(user, "agency_profile") and user.agency_profile: + # messages.success( + # request, f"Welcome, {user.agency_profile.name}!" + # ) + # return redirect("agency_portal_dashboard") + # else: + # messages.error( + # request, "No agency profile found for this user." + # ) + # logout(request) + + # elif user_type == "candidate": + # # Check if user has candidate profile + # if ( + # hasattr(user, "candidate_profile") + # and user.candidate_profile + # ): + # messages.success( + # request, + # f"Welcome, {user.candidate_profile.first_name}!", + # ) + # return redirect("candidate_portal_dashboard") + # else: + # messages.error( + # request, "No candidate profile found for this user." + # ) + # logout(request) + else: + messages.error(request, "Invalid user type selected.") + else: + messages.error(request, "Invalid email or password.") + else: + messages.error(request, "Please correct the errors below.") + else: + form = PortalLoginForm() + + context = { + "form": form, + } + return render(request, "recruitment/portal_login.html", context) + + +@candidate_user_required +def candidate_portal_dashboard(request): + """Candidate portal dashboard""" + if not request.user.is_authenticated: + return redirect("portal_login") + + # Get candidate profile + try: + candidate = request.user.candidate_profile + except: + messages.error(request, "No candidate profile found.") + return redirect("portal_login") + + context = { + "candidate": candidate, + } + return render(request, "recruitment/candidate_portal_dashboard.html", context) + + +@agency_user_required +def agency_portal_persons_list(request): + """Agency portal page showing all persons who come through this agency""" + try: + agency = request.user.agency_profile + except Exception as e: + print(e) + messages.error(request, "No agency profile found.") + return redirect("portal_login") + + # Get all applications for this agency + persons = Person.objects.filter(agency=agency) + # persons = Application.objects.filter( + # hiring_agency=agency + # ).select_related("job").order_by("-created_at") + + # Search functionality + search_query = request.GET.get("q", "") + if search_query: + persons = persons.filter( + Q(first_name__icontains=search_query) | + Q(last_name__icontains=search_query) | + Q(email__icontains=search_query) | + Q(phone__icontains=search_query) | + Q(job__title__icontains=search_query) + ) + + # Filter by stage if provided + stage_filter = request.GET.get("stage", "") + if stage_filter: + persons = persons.filter(stage=stage_filter) + + # Pagination + paginator = Paginator(persons, 20) # Show 20 persons per page + page_number = request.GET.get("page") + page_obj = paginator.get_page(page_number) + + # Get stage choices for filter dropdown + stage_choices = Application.Stage.choices + person_form = PersonForm() + person_form.initial['agency'] = agency + + context = { + "agency": agency, + "page_obj": page_obj, + "search_query": search_query, + "stage_filter": stage_filter, + "stage_choices": stage_choices, + "total_persons": persons.count(), + "person_form": person_form, + } + return render(request, "recruitment/agency_portal_persons_list.html", context) + + +@agency_user_required def agency_portal_dashboard(request): """Agency portal dashboard showing all assignments for the agency""" - assignment_id = request.session.get('agency_assignment_id') - if not assignment_id: - return redirect('agency_portal_login') - # Get the current assignment to determine the agency - current_assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency'), - id=assignment_id - ) - - agency = current_assignment.agency + try: + agency = request.user.agency_profile + except Exception as e: + print(e) + messages.error(request, "No agency profile found.") + return redirect("portal_login") # Get ALL assignments for this agency - assignments = AgencyJobAssignment.objects.filter( - agency=agency - ).select_related('job').order_by('-created_at') + assignments = ( + AgencyJobAssignment.objects.filter(agency=agency) + .select_related("job") + .order_by("-created_at") + ) + current_assignment = assignments.filter(is_active=True).first() # Calculate statistics for each assignment assignment_stats = [] for assignment in assignments: - candidates = Candidate.objects.filter( - hiring_agency=agency, - job=assignment.job - ).order_by('-created_at') + candidates = Application.objects.filter( + hiring_agency=agency, job=assignment.job + ).order_by("-created_at") unread_messages = 0 - assignment_stats.append({ - 'assignment': assignment, - 'candidates': candidates, - 'candidate_count': candidates.count(), - 'unread_messages': unread_messages, - 'days_remaining': assignment.days_remaining, - 'is_active': assignment.is_currently_active, - 'can_submit': assignment.can_submit, - }) + assignment_stats.append( + { + "assignment": assignment, + "candidates": candidates, + "candidate_count": candidates.count(), + "unread_messages": unread_messages, + "days_remaining": assignment.days_remaining, + "is_active": assignment.is_currently_active, + "can_submit": assignment.can_submit, + } + ) # Get overall statistics - total_candidates = sum(stats['candidate_count'] for stats in assignment_stats) - total_unread_messages = sum(stats['unread_messages'] for stats in assignment_stats) - active_assignments = sum(1 for stats in assignment_stats if stats['is_active']) + total_candidates = sum(stats["candidate_count"] for stats in assignment_stats) + total_unread_messages = sum(stats["unread_messages"] for stats in assignment_stats) + active_assignments = sum(1 for stats in assignment_stats if stats["is_active"]) context = { - 'agency': agency, - 'current_assignment': current_assignment, - 'assignment_stats': assignment_stats, - 'total_assignments': assignments.count(), - 'active_assignments': active_assignments, - 'total_candidates': total_candidates, - 'total_unread_messages': total_unread_messages, + "agency": agency, + "current_assignment": current_assignment, + "assignment_stats": assignment_stats, + "total_assignments": assignments.count(), + "active_assignments": active_assignments, + "total_candidates": total_candidates, + "total_unread_messages": total_unread_messages, } - return render(request, 'recruitment/agency_portal_dashboard.html', context) + return render(request, "recruitment/agency_portal_dashboard.html", context) +@agency_user_required def agency_portal_submit_candidate_page(request, slug): """Dedicated page for submitting a candidate""" - assignment_id = request.session.get('agency_assignment_id') - if not assignment_id: - return redirect('agency_portal_login') + # assignment_id = request.session.get("agency_assignment_id") + # if not assignment_id: + # return redirect("agency_portal_login") # Get the specific assignment by slug and verify it belongs to the same agency - current_assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency'), - id=assignment_id - ) - + # current_assignment = get_object_or_404( + # AgencyJobAssignment.objects.select_related("agency"), slug=slug + # ) assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency', 'job'), - slug=slug + AgencyJobAssignment.objects.select_related("agency", "job"), slug=slug ) - - if assignment.is_full: - messages.error(request, 'Maximum candidate limit reached for this assignment.') - return redirect('agency_portal_assignment_detail', slug=assignment.slug) + messages.error(request, "Maximum candidate limit reached for this assignment.") + return redirect("agency_portal_assignment_detail", slug=assignment.slug) # Verify this assignment belongs to the same agency as the logged-in session - if assignment.agency.id != current_assignment.agency.id: - messages.error(request, 'Access denied: This assignment does not belong to your agency.') - return redirect('agency_portal_dashboard') + if assignment.agency.id != assignment.agency.id: + messages.error( + request, "Access denied: This assignment does not belong to your agency." + ) + return redirect("agency_portal_dashboard") # Check if assignment allows submission if not assignment.can_submit: - messages.error(request, 'Cannot submit candidates: Assignment is not active, expired, or full.') - return redirect('agency_portal_assignment_detail', slug=assignment.slug) + messages.error( + request, + "Cannot submit candidates: Assignment is not active, expired, or full.", + ) + return redirect("agency_portal_assignment_detail", slug=assignment.slug) # Get total submitted candidates for this assignment - total_submitted = Candidate.objects.filter( - hiring_agency=assignment.agency, - job=assignment.job + total_submitted = Application.objects.filter( + hiring_agency=assignment.agency, job=assignment.job ).count() - if request.method == 'POST': - form = AgencyCandidateSubmissionForm(assignment, request.POST, request.FILES) + if request.method == "POST": + form = AgencyApplicationSubmissionForm(assignment, request.POST, request.FILES) if form.is_valid(): candidate = form.save(commit=False) - candidate.hiring_source = 'AGENCY' + candidate.hiring_source = "AGENCY" candidate.hiring_agency = assignment.agency candidate.save() assignment.increment_submission_count() # Handle AJAX requests - if request.headers.get('X-Requested-With') == 'XMLHttpRequest': - return JsonResponse({ - 'success': True, - 'message': f'Candidate {candidate.name} submitted successfully!', - 'candidate_id': candidate.id - }) + if request.headers.get("X-Requested-With") == "XMLHttpRequest": + return JsonResponse( + { + "success": True, + "message": f"Candidate {candidate.name} submitted successfully!", + "candidate_id": candidate.id, + } + ) else: - messages.success(request, f'Candidate {candidate.name} submitted successfully!') - return redirect('agency_portal_assignment_detail', slug=assignment.slug) + messages.success( + request, f"Candidate {candidate.name} submitted successfully!" + ) + return redirect("agency_portal_assignment_detail", slug=assignment.slug) else: # Handle form validation errors for AJAX - if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + if request.headers.get("X-Requested-With") == "XMLHttpRequest": error_messages = [] for field, errors in form.errors.items(): for error in errors: - error_messages.append(f'{field}: {error}') - return JsonResponse({ - 'success': False, - 'message': 'Please correct the following errors: ' + '; '.join(error_messages) - }) + error_messages.append(f"{field}: {error}") + return JsonResponse( + { + "success": False, + "message": "Please correct the following errors: " + + "; ".join(error_messages), + } + ) else: - messages.error(request, 'Please correct errors below.') + messages.error(request, "Please correct errors below.") else: - form = AgencyCandidateSubmissionForm(assignment) + form = AgencyApplicationSubmissionForm(assignment) context = { 'form': form, @@ -3502,100 +4106,112 @@ def agency_portal_submit_candidate_page(request, slug): 'total_submitted': total_submitted, 'job':assignment.job } - return render(request, 'recruitment/agency_portal_submit_candidate.html', context) + return render(request, "recruitment/agency_portal_submit_candidate.html", context) +@agency_user_required def agency_portal_submit_candidate(request): """Handle candidate submission via AJAX (for embedded form)""" - assignment_id = request.session.get('agency_assignment_id') + assignment_id = request.session.get("agency_assignment_id") if not assignment_id: - return redirect('agency_portal_login') + return redirect("agency_portal_login") assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency', 'job'), - id=assignment_id + AgencyJobAssignment.objects.select_related("agency", "job"), id=assignment_id ) if assignment.is_full: - messages.error(request, 'Maximum candidate limit reached for this assignment.') - return redirect('agency_portal_assignment_detail', slug=assignment.slug) + messages.error(request, "Maximum candidate limit reached for this assignment.") + return redirect("agency_portal_assignment_detail", slug=assignment.slug) # Check if assignment allows submission if not assignment.can_submit: - messages.error(request, 'Cannot submit candidates: Assignment is not active, expired, or full.') - return redirect('agency_portal_dashboard') + messages.error( + request, + "Cannot submit candidates: Assignment is not active, expired, or full.", + ) + return redirect("agency_portal_dashboard") - if request.method == 'POST': - form = AgencyCandidateSubmissionForm(assignment, request.POST, request.FILES) + if request.method == "POST": + form = AgencyApplicationSubmissionForm(assignment, request.POST, request.FILES) if form.is_valid(): candidate = form.save(commit=False) - candidate.hiring_source = 'AGENCY' + candidate.hiring_source = "AGENCY" candidate.hiring_agency = assignment.agency candidate.save() # Increment the assignment's submitted count assignment.increment_submission_count() - if request.headers.get('X-Requested-With') == 'XMLHttpRequest': - return JsonResponse({'success': True, 'message': f'Candidate {candidate.name} submitted successfully!'}) + if request.headers.get("X-Requested-With") == "XMLHttpRequest": + return JsonResponse( + { + "success": True, + "message": f"Candidate {candidate.name} submitted successfully!", + } + ) else: - messages.success(request, f'Candidate {candidate.name} submitted successfully!') - return redirect('agency_portal_dashboard') + messages.success( + request, f"Candidate {candidate.name} submitted successfully!" + ) + return redirect("agency_portal_dashboard") else: - if request.headers.get('X-Requested-With') == 'XMLHttpRequest': - return JsonResponse({'success': False, 'message': 'Please correct the errors below.'}) + if request.headers.get("X-Requested-With") == "XMLHttpRequest": + return JsonResponse( + {"success": False, "message": "Please correct the errors below."} + ) else: - messages.error(request, 'Please correct errors below.') + messages.error(request, "Please correct errors below.") else: - form = AgencyCandidateSubmissionForm(assignment) + form = AgencyApplicationSubmissionForm(assignment) context = { - 'form': form, - 'assignment': assignment, - 'title': f'Submit Candidate for {assignment.job.title}', - 'button_text': 'Submit Candidate', + "form": form, + "assignment": assignment, + "title": f"Submit Candidate for {assignment.job.title}", + "button_text": "Submit Candidate", } - return render(request, 'recruitment/agency_portal_submit_candidate.html', context) - - + return render(request, "recruitment/agency_portal_submit_candidate.html", context) def agency_portal_assignment_detail(request, slug): """View details of a specific assignment - routes to admin or agency template""" - print(slug) # Check if this is an agency portal user (via session) - assignment_id = request.session.get('agency_assignment_id') - is_agency_user = bool(assignment_id) - return agency_assignment_detail_agency(request, slug, assignment_id) + # assignment_id = request.session.get("agency_assignment_id") + # is_agency_user = bool(assignment_id) + # return agency_assignment_detail_agency(request, slug, assignment_id) # if is_agency_user: # # Agency Portal User - Route to agency-specific template # else: # # Admin User - Route to admin template # return agency_assignment_detail_admin(request, slug) + assignment = get_object_or_404( + AgencyJobAssignment.objects.select_related("agency", "job"), slug=slug + ) +@agency_user_required def agency_assignment_detail_agency(request, slug, assignment_id): """Handle agency portal assignment detail view""" # Get the assignment by slug and verify it belongs to same agency assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency', 'job'), - slug=slug + AgencyJobAssignment.objects.select_related("agency", "job"), slug=slug ) # Verify this assignment belongs to the same agency as the logged-in session current_assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency'), - id=assignment_id + AgencyJobAssignment.objects.select_related("agency"), id=assignment_id ) if assignment.agency.id != current_assignment.agency.id: - messages.error(request, 'Access denied: This assignment does not belong to your agency.') - return redirect('agency_portal_dashboard') + messages.error( + request, "Access denied: This assignment does not belong to your agency." + ) + return redirect("agency_portal_dashboard") # Get candidates submitted by this agency for this job - candidates = Candidate.objects.filter( - hiring_agency=assignment.agency, - job=assignment.job - ).order_by('-created_at') + candidates = Application.objects.filter( + hiring_agency=assignment.agency, job=assignment.job + ).order_by("-created_at") # Get messages for this assignment messages = [] @@ -3605,12 +4221,12 @@ def agency_assignment_detail_agency(request, slug, assignment_id): # Pagination for candidates paginator = Paginator(candidates, 20) # Show 20 candidates per page - page_number = request.GET.get('page') + page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) # Pagination for messages message_paginator = Paginator(messages, 15) # Show 15 messages per page - message_page_number = request.GET.get('message_page') + message_page_number = request.GET.get("message_page") message_page_obj = message_paginator.get_page(message_page_number) # Calculate progress ring offset for circular progress indicator @@ -3619,258 +4235,568 @@ def agency_assignment_detail_agency(request, slug, assignment_id): circumference = 326.73 # 2 * π * r where r=52 if max_candidates > 0: - progress_percentage = (total_candidates / max_candidates) + progress_percentage = total_candidates / max_candidates stroke_dashoffset = circumference - (circumference * progress_percentage) else: stroke_dashoffset = circumference context = { - 'assignment': assignment, - 'page_obj': page_obj, - 'message_page_obj': message_page_obj, - 'total_candidates': total_candidates, - 'stroke_dashoffset': stroke_dashoffset, + "assignment": assignment, + "page_obj": page_obj, + "message_page_obj": message_page_obj, + "total_candidates": total_candidates, + "stroke_dashoffset": stroke_dashoffset, } - return render(request, 'recruitment/agency_portal_assignment_detail.html', context) + return render(request, "recruitment/agency_portal_assignment_detail.html", context) +@staff_user_required def agency_assignment_detail_admin(request, slug): """Handle admin assignment detail view""" assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency', 'job'), - slug=slug + AgencyJobAssignment.objects.select_related("agency", "job"), slug=slug ) # Get candidates submitted by this agency for this job - candidates = Candidate.objects.filter( - hiring_agency=assignment.agency, - job=assignment.job - ).order_by('-created_at') + candidates = Application.objects.filter( + hiring_agency=assignment.agency, job=assignment.job + ).order_by("-created_at") # Get access link if exists - access_link = getattr(assignment, 'access_link', None) + access_link = getattr(assignment, "access_link", None) # Get messages for this assignment messages = [] context = { - 'assignment': assignment, - 'candidates': candidates, - 'access_link': access_link, - 'total_candidates': candidates.count(), + "assignment": assignment, + "candidates": candidates, + "access_link": access_link, + "total_candidates": candidates.count(), } - return render(request, 'recruitment/agency_assignment_detail.html', context) + return render(request, "recruitment/agency_assignment_detail.html", context) +@agency_user_required def agency_portal_edit_candidate(request, candidate_id): """Edit a candidate for agency portal""" - assignment_id = request.session.get('agency_assignment_id') + assignment_id = request.session.get("agency_assignment_id") if not assignment_id: - return redirect('agency_portal_login') + return redirect("agency_portal_login") # Get current assignment to determine agency current_assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency'), - id=assignment_id + AgencyJobAssignment.objects.select_related("agency"), id=assignment_id ) agency = current_assignment.agency # Get candidate and verify it belongs to this agency - candidate = get_object_or_404(Candidate, id=candidate_id, hiring_agency=agency) + candidate = get_object_or_404(Application, id=candidate_id, hiring_agency=agency) - if request.method == 'POST': + if request.method == "POST": # Handle form submission - candidate.first_name = request.POST.get('first_name', candidate.first_name) - candidate.last_name = request.POST.get('last_name', candidate.last_name) - candidate.email = request.POST.get('email', candidate.email) - candidate.phone = request.POST.get('phone', candidate.phone) - candidate.address = request.POST.get('address', candidate.address) + candidate.first_name = request.POST.get("first_name", candidate.first_name) + candidate.last_name = request.POST.get("last_name", candidate.last_name) + candidate.email = request.POST.get("email", candidate.email) + candidate.phone = request.POST.get("phone", candidate.phone) + candidate.address = request.POST.get("address", candidate.address) # Handle resume upload if provided - if 'resume' in request.FILES: - candidate.resume = request.FILES['resume'] + if "resume" in request.FILES: + candidate.resume = request.FILES["resume"] try: candidate.save() - messages.success(request, f'Candidate {candidate.name} updated successfully!') - return redirect('agency_assignment_detail', slug=candidate.job.agencyjobassignment_set.first().slug) + messages.success( + request, f"Candidate {candidate.name} updated successfully!" + ) + return redirect( + "agency_assignment_detail", + slug=candidate.job.agencyjobassignment_set.first().slug, + ) except Exception as e: - messages.error(request, f'Error updating candidate: {e}') + messages.error(request, f"Error updating candidate: {e}") # For GET requests or POST errors, return JSON response for AJAX - if request.headers.get('X-Requested-With') == 'XMLHttpRequest': - return JsonResponse({ - 'success': True, - 'candidate': { - 'id': candidate.id, - 'first_name': candidate.first_name, - 'last_name': candidate.last_name, - 'email': candidate.email, - 'phone': candidate.phone, - 'address': candidate.address, + if request.headers.get("X-Requested-With") == "XMLHttpRequest": + return JsonResponse( + { + "success": True, + "candidate": { + "id": candidate.id, + "first_name": candidate.first_name, + "last_name": candidate.last_name, + "email": candidate.email, + "phone": candidate.phone, + "address": candidate.address, + }, } - }) + ) # Fallback for non-AJAX requests - return redirect('agency_portal_dashboard') + return redirect("agency_portal_dashboard") +@agency_user_required def agency_portal_delete_candidate(request, candidate_id): """Delete a candidate for agency portal""" - assignment_id = request.session.get('agency_assignment_id') + assignment_id = request.session.get("agency_assignment_id") if not assignment_id: - return redirect('agency_portal_login') + return redirect("agency_portal_login") # Get current assignment to determine agency current_assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency'), - id=assignment_id + AgencyJobAssignment.objects.select_related("agency"), id=assignment_id ) agency = current_assignment.agency # Get candidate and verify it belongs to this agency - candidate = get_object_or_404(Candidate, id=candidate_id, hiring_agency=agency) + candidate = get_object_or_404(Application, id=candidate_id, hiring_agency=agency) - if request.method == 'POST': + if request.method == "POST": try: candidate_name = candidate.name candidate.delete() current_assignment.candidates_submitted -= 1 current_assignment.status = current_assignment.AssignmentStatus.ACTIVE - current_assignment.save(update_fields=['candidates_submitted','status']) + current_assignment.save(update_fields=["candidates_submitted", "status"]) - messages.success(request, f'Candidate {candidate_name} removed successfully!') - return JsonResponse({'success': True}) + messages.success( + request, f"Candidate {candidate_name} removed successfully!" + ) + return JsonResponse({"success": True}) except Exception as e: - return JsonResponse({'success': False, 'error': str(e)}) + return JsonResponse({"success": False, "error": str(e)}) # For GET requests, return error + return JsonResponse({"success": False, "error": "Method not allowed"}) + + +# Message Views +@staff_user_required +def message_list(request): + """List all messages for the current user""" + # Get filter parameters + status_filter = request.GET.get("status", "") + message_type_filter = request.GET.get("type", "") + search_query = request.GET.get("q", "") + + # Base queryset - get messages where user is either sender or recipient + message_list = Message.objects.filter( + Q(sender=request.user) | Q(recipient=request.user) + ).select_related("sender", "recipient", "job").order_by("-created_at") + + # Apply filters + if status_filter: + if status_filter == "read": + message_list = message_list.filter(is_read=True) + elif status_filter == "unread": + message_list = message_list.filter(is_read=False) + + if message_type_filter: + message_list = message_list.filter(message_type=message_type_filter) + + if search_query: + message_list = message_list.filter( + Q(subject__icontains=search_query) | + Q(content__icontains=search_query) + ) + + # Pagination + paginator = Paginator(message_list, 20) # Show 20 messages per page + page_number = request.GET.get("page") + page_obj = paginator.get_page(page_number) + + # Statistics + total_messages = message_list.count() + unread_messages = message_list.filter(is_read=False).count() + + context = { + "page_obj": page_obj, + "total_messages": total_messages, + "unread_messages": unread_messages, + "status_filter": status_filter, + "type_filter": message_type_filter, + "search_query": search_query, + } + return render(request, "messages/message_list.html", context) + + +@login_required +def message_detail(request, message_id): + """View details of a specific message""" + message = get_object_or_404( + Message.objects.select_related("sender", "recipient", "job"), + id=message_id + ) + + # Check if user has permission to view this message + if message.sender != request.user and message.recipient != request.user: + messages.error(request, "You don't have permission to view this message.") + return redirect("message_list") + + # Mark as read if it was unread and user is the recipient + if not message.is_read and message.recipient == request.user: + message.is_read = True + message.read_at = timezone.now() + message.save(update_fields=["is_read", "read_at"]) + + context = { + "message": message, + } + return render(request, "messages/message_detail.html", context) + + +@login_required +def message_create(request): + """Create a new message""" + if request.method == "POST": + form = MessageForm(request.user, request.POST) + if form.is_valid(): + message = form.save(commit=False) + message.sender = request.user + message.save() + + messages.success(request, "Message sent successfully!") + return redirect("message_list") + else: + messages.error(request, "Please correct the errors below.") + else: + form = MessageForm(request.user) + + context = { + "form": form, + } + return render(request, "messages/message_form.html", context) + + +@login_required +def message_reply(request, message_id): + """Reply to a message""" + parent_message = get_object_or_404( + Message.objects.select_related("sender", "recipient", "job"), + id=message_id + ) + + # Check if user has permission to reply to this message + if parent_message.recipient != request.user and parent_message.sender != request.user: + messages.error(request, "You don't have permission to reply to this message.") + return redirect("message_list") + + if request.method == "POST": + form = MessageForm(request.user, request.POST) + if form.is_valid(): + message = form.save(commit=False) + message.sender = request.user + message.parent_message = parent_message + # Set recipient as the original sender + message.recipient = parent_message.sender + message.save() + + messages.success(request, "Reply sent successfully!") + return redirect("message_detail", message_id=parent_message.id) + else: + messages.error(request, "Please correct the errors below.") + else: + # Pre-fill form with reply context + form = MessageForm(request.user) + form.initial["subject"] = f"Re: {parent_message.subject}" + form.initial["recipient"] = parent_message.sender + if parent_message.job: + form.initial["job"] = parent_message.job + form.initial["message_type"] = Message.MessageType.JOB_RELATED + + context = { + "form": form, + "parent_message": parent_message, + } + return render(request, "messages/message_form.html", context) + + +@login_required +def message_mark_read(request, message_id): + """Mark a message as read""" + message = get_object_or_404( + Message.objects.select_related("sender", "recipient"), + id=message_id + ) + + # Check if user has permission to mark this message as read + if message.recipient != request.user: + messages.error(request, "You can only mark messages you received as read.") + return redirect("message_list") + + # Mark as read + message.is_read = True + message.read_at = timezone.now() + message.save(update_fields=["is_read", "read_at"]) + + messages.success(request, "Message marked as read.") + + # Handle HTMX requests + if "HX-Request" in request.headers: + return HttpResponse(status=200) # HTMX success response + + return redirect("message_list") + + +@login_required +def message_mark_unread(request, message_id): + """Mark a message as unread""" + message = get_object_or_404( + Message.objects.select_related("sender", "recipient"), + id=message_id + ) + + # Check if user has permission to mark this message as unread + if message.recipient != request.user: + messages.error(request, "You can only mark messages you received as unread.") + return redirect("message_list") + + # Mark as unread + message.is_read = False + message.read_at = None + message.save(update_fields=["is_read", "read_at"]) + + messages.success(request, "Message marked as unread.") + + # Handle HTMX requests + if "HX-Request" in request.headers: + return HttpResponse(status=200) # HTMX success response + + return redirect("message_list") + + +@login_required +def message_delete(request, message_id): + """Delete a message""" + message = get_object_or_404( + Message.objects.select_related("sender", "recipient"), + id=message_id + ) + + # Check if user has permission to delete this message + if message.sender != request.user and message.recipient != request.user: + messages.error(request, "You don't have permission to delete this message.") + return redirect("message_list") + + if request.method == "POST": + message.delete() + messages.success(request, "Message deleted successfully.") + + # Handle HTMX requests + if "HX-Request" in request.headers: + return HttpResponse(status=200) # HTMX success response + + return redirect("message_list") + + # For GET requests, show confirmation page + context = { + "message": message, + "title": "Delete Message", + "message": f'Are you sure you want to delete this message from {message.sender.get_full_name() or message.sender.username}?', + "cancel_url": reverse("message_detail", kwargs={"message_id": message_id}), + } + return render(request, "messages/message_confirm_delete.html", context) + + +@login_required +def api_unread_count(request): + """API endpoint to get unread message count""" + unread_count = Message.objects.filter( + recipient=request.user, + is_read=False + ).count() + + return JsonResponse({"unread_count": unread_count}) + + +# Document Views +@login_required +def document_upload(request, application_id): + """Upload a document for an application""" + application = get_object_or_404(Application, pk=application_id) + + if request.method == "POST": + if request.FILES.get('file'): + document = Document.objects.create( + content_object=application, # Use Generic Foreign Key to link to Application + file=request.FILES['file'], + document_type=request.POST.get('document_type', 'other'), + description=request.POST.get('description', ''), + uploaded_by=request.user, + ) + messages.success(request, f'Document "{document.get_document_type_display()}" uploaded successfully!') + return redirect('candidate_detail', slug=application.job.slug) + + +@login_required +def document_delete(request, document_id): + """Delete a document""" + document = get_object_or_404(Document, id=document_id) + + # Check permission - document is now linked to Application via Generic Foreign Key + if hasattr(document.content_object, 'job'): + if document.content_object.job.assigned_to != request.user and not request.user.is_superuser: + messages.error(request, "You don't have permission to delete this document.") + return JsonResponse({'success': False, 'error': 'Permission denied'}) + job_slug = document.content_object.job.slug + else: + # Handle other content object types + messages.error(request, "You don't have permission to delete this document.") + return JsonResponse({'success': False, 'error': 'Permission denied'}) + + if request.method == "POST": + file_name = document.file.name if document.file else "Unknown" + document.delete() + messages.success(request, f'Document "{file_name}" deleted successfully!') + + # Handle AJAX requests + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return JsonResponse({'success': True, 'message': 'Document deleted successfully!'}) + else: + return redirect('candidate_detail', slug=job_slug) + return JsonResponse({'success': False, 'error': 'Method not allowed'}) -def agency_portal_logout(request): - """Logout from agency portal""" - if 'agency_assignment_id' in request.session: - del request.session['agency_assignment_id'] - if 'agency_name' in request.session: - del request.session['agency_name'] +@login_required +def document_download(request, document_id): + """Download a document""" + document = get_object_or_404(Document, id=document_id) - messages.success(request, 'You have been logged out.') - return redirect('agency_portal_login') + # Check permission - document is now linked to Application via Generic Foreign Key + if hasattr(document.content_object, 'job'): + if document.content_object.job.assigned_to != request.user and not request.user.is_superuser: + messages.error(request, "You don't have permission to download this document.") + return JsonResponse({'success': False, 'error': 'Permission denied'}) + else: + # Handle other content object types + messages.error(request, "You don't have permission to download this document.") + return JsonResponse({'success': False, 'error': 'Permission denied'}) + + if document.file: + response = HttpResponse(document.file.read(), content_type='application/octet-stream') + response['Content-Disposition'] = f'attachment; filename="{document.file.name}"' + return response + + return JsonResponse({'success': False, 'error': 'File not found'}) + + +@login_required +def portal_logout(request): + """Logout from portal""" + logout(request) + + messages.success(request, "You have been logged out.") + return redirect("portal_login") @login_required def agency_access_link_deactivate(request, slug): """Deactivate an agency access link""" access_link = get_object_or_404( - AgencyAccessLink.objects.select_related('assignment__agency', 'assignment__job'), - slug=slug + AgencyAccessLink.objects.select_related( + "assignment__agency", "assignment__job" + ), + slug=slug, ) - if request.method == 'POST': + if request.method == "POST": access_link.is_active = False - access_link.save(update_fields=['is_active']) + access_link.save(update_fields=["is_active"]) messages.success( request, - f'Access link for {access_link.assignment.agency.name} - {access_link.assignment.job.title} has been deactivated.' + f"Access link for {access_link.assignment.agency.name} - {access_link.assignment.job.title} has been deactivated.", ) # Handle HTMX requests - if 'HX-Request' in request.headers: + if "HX-Request" in request.headers: return HttpResponse(status=200) # HTMX success response - return redirect('agency_assignment_detail', slug=access_link.assignment.slug) + return redirect("agency_assignment_detail", slug=access_link.assignment.slug) # For GET requests, show confirmation page context = { - 'access_link': access_link, - 'title': 'Deactivate Access Link', - 'message': f'Are you sure you want to deactivate the access link for {access_link.assignment.agency.name}?', - 'cancel_url': reverse('agency_assignment_detail', kwargs={'slug': access_link.assignment.slug}), + "access_link": access_link, + "title": "Deactivate Access Link", + "message": f"Are you sure you want to deactivate the access link for {access_link.assignment.agency.name}?", + "cancel_url": reverse( + "agency_assignment_detail", kwargs={"slug": access_link.assignment.slug} + ), } - return render(request, 'recruitment/agency_access_link_confirm.html', context) + return render(request, "recruitment/agency_access_link_confirm.html", context) @login_required def agency_access_link_reactivate(request, slug): """Reactivate an agency access link""" access_link = get_object_or_404( - AgencyAccessLink.objects.select_related('assignment__agency', 'assignment__job'), - slug=slug + AgencyAccessLink.objects.select_related( + "assignment__agency", "assignment__job" + ), + slug=slug, ) - if request.method == 'POST': + if request.method == "POST": access_link.is_active = True - access_link.save(update_fields=['is_active']) + access_link.save(update_fields=["is_active"]) messages.success( request, - f'Access link for {access_link.assignment.agency.name} - {access_link.assignment.job.title} has been reactivated.' + f"Access link for {access_link.assignment.agency.name} - {access_link.assignment.job.title} has been reactivated.", ) # Handle HTMX requests - if 'HX-Request' in request.headers: + if "HX-Request" in request.headers: return HttpResponse(status=200) # HTMX success response - return redirect('agency_assignment_detail', slug=access_link.assignment.slug) + return redirect("agency_assignment_detail", slug=access_link.assignment.slug) # For GET requests, show confirmation page context = { - 'access_link': access_link, - 'title': 'Reactivate Access Link', - 'message': f'Are you sure you want to reactivate the access link for {access_link.assignment.agency.name}?', - 'cancel_url': reverse('agency_assignment_detail', kwargs={'slug': access_link.assignment.slug}), + "access_link": access_link, + "title": "Reactivate Access Link", + "message": f"Are you sure you want to reactivate the access link for {access_link.assignment.agency.name}?", + "cancel_url": reverse( + "agency_assignment_detail", kwargs={"slug": access_link.assignment.slug} + ), } - return render(request, 'recruitment/agency_access_link_confirm.html', context) - - - - - - - - - - - - - - - + return render(request, "recruitment/agency_access_link_confirm.html", context) +@agency_user_required def api_candidate_detail(request, candidate_id): """API endpoint to get candidate details for agency portal""" try: # Get candidate from session-based agency access - assignment_id = request.session.get('agency_assignment_id') + assignment_id = request.session.get("agency_assignment_id") if not assignment_id: - return JsonResponse({'success': False, 'error': 'Access denied'}) + return JsonResponse({"success": False, "error": "Access denied"}) # Get current assignment to determine agency current_assignment = get_object_or_404( - AgencyJobAssignment.objects.select_related('agency'), - id=assignment_id + AgencyJobAssignment.objects.select_related("agency"), id=assignment_id ) agency = current_assignment.agency # Get candidate and verify it belongs to this agency - candidate = get_object_or_404(Candidate, id=candidate_id, hiring_agency=agency) + candidate = get_object_or_404(Application, id=candidate_id, hiring_agency=agency) # Return candidate data response_data = { - 'success': True, - 'id': candidate.id, - 'first_name': candidate.first_name, - 'last_name': candidate.last_name, - 'email': candidate.email, - 'phone': candidate.phone, - 'address': candidate.address, + "success": True, + "id": candidate.id, + "first_name": candidate.first_name, + "last_name": candidate.last_name, + "email": candidate.email, + "phone": candidate.phone, + "address": candidate.address, } return JsonResponse(response_data) @@ -3879,38 +4805,80 @@ def api_candidate_detail(request, candidate_id): return JsonResponse({'success': False, 'error': str(e)}) - -@login_required -def compose_candidate_email(request, job_slug): +@staff_user_required +def compose_candidate_email(request, job_slug, candidate_slug): """Compose email to participants about a candidate""" from .email_service import send_bulk_email job = get_object_or_404(JobPosting, slug=job_slug) + candidate = get_object_or_404(Application, slug=candidate_slug, job=job) + if request.method == "POST": + form = CandidateEmailForm(job, candidate, request.POST) candidate_ids=request.GET.getlist('candidate_ids') - candidates=Candidate.objects.filter(id__in=candidate_ids) + candidates=Application.objects.filter(id__in=candidate_ids) if request.method == 'POST': print("........................................................inside candidate conpose.............") candidate_ids = request.POST.getlist('candidate_ids') - candidates=Candidate.objects.filter(id__in=candidate_ids) + candidates=Application.objects.filter(id__in=candidate_ids) form = CandidateEmailForm(job, candidates, request.POST) if form.is_valid(): print("form is valid ...") # Get email addresses email_addresses = form.get_email_addresses() + if not email_addresses: + messages.error( + request, "No valid email addresses found for selected recipients." + ) + return render( + request, + "includes/email_compose_form.html", + {"form": form, "job": job, "candidate": candidate}, + ) + + # Check if this is an interview invitation + subject = form.cleaned_data.get("subject", "").lower() + is_interview_invitation = "interview" in subject or "meeting" in subject + + if is_interview_invitation: + # Use HTML template for interview invitations + meeting_details = None + if form.cleaned_data.get("include_meeting_details"): + # Try to get meeting details from candidate + meeting_details = { + "topic": f"Interview for {job.title}", + "date_time": getattr( + candidate, "interview_date", "To be scheduled" + ), + "duration": "60 minutes", + "join_url": getattr(candidate, "meeting_url", ""), + } + + from .email_service import send_interview_invitation_email + + email_result = send_interview_invitation_email( + candidate=candidate, + job=job, + meeting_details=meeting_details, + recipient_list=email_addresses, + ) + else: + # Get formatted message for regular emails + message = form.get_formatted_message() + subject = form.cleaned_data.get("subject") print(email_addresses) - - + + if not email_addresses: messages.error(request, 'No email selected') referer = request.META.get('HTTP_REFERER') - + if referer: # Redirect back to the referring page return redirect(referer) else: - + return redirect('dashboard') @@ -3918,7 +4886,7 @@ def compose_candidate_email(request, job_slug): subject = form.cleaned_data.get('subject') # Send emails using email service (no attachments, synchronous to avoid pickle issues) - + email_result = send_bulk_email( subject=subject, message=message, @@ -3929,16 +4897,45 @@ def compose_candidate_email(request, job_slug): from_interview=False ) - if email_result['success']: - messages.success(request, f'Email sent successfully to {len(email_addresses)} recipient(s).') + if email_result["success"]: + messages.success( + request, + f"Email sent successfully to {len(email_addresses)} recipient(s).", + ) - + # For HTMX requests, return success response + if "HX-Request" in request.headers: + return JsonResponse( + { + "success": True, + "message": f"Email sent successfully to {len(email_addresses)} recipient(s).", + } + ) - return redirect('candidate_interview_view', slug=job.slug) + return redirect("candidate_interview_view", slug=job.slug) else: - messages.error(request, f'Failed to send email: {email_result.get("message", "Unknown error")}') + messages.error( + request, + f"Failed to send email: {email_result.get('message', 'Unknown error')}", + ) + + # For HTMX requests, return error response + if "HX-Request" in request.headers: + return JsonResponse( + { + "success": False, + "error": email_result.get( + "message", "Failed to send email" + ), + } + ) + + return render( + request, + "includes/email_compose_form.html", + {"form": form, "job": job, "candidate": candidate}, + ) - return render(request, 'includes/email_compose_form.html', { 'form': form, 'job': job, @@ -3946,160 +4943,175 @@ def compose_candidate_email(request, job_slug): }) # except Exception as e: - + # logger.error(f"Error sending candidate email: {e}") + # messages.error(request, f'An error occurred while sending the email: {str(e)}') + + # # For HTMX requests, return error response + # if 'HX-Request' in request.headers: + # return JsonResponse({ + # 'success': False, + # 'error': f'An error occurred while sending the email: {str(e)}' + # }) + + # return render(request, 'includes/email_compose_form.html', { + # 'form': form, + # 'job': job, + # 'candidate': candidate + # }) else: # Form validation errors print('form is not valid') print(form.errors) - messages.error(request, 'Please correct the errors below.') + messages.error(request, "Please correct the errors below.") # For HTMX requests, return error response - if 'HX-Request' in request.headers: - return JsonResponse({ - 'success': False, - 'error': 'Please correct the form errors and try again.' - }) - - return render(request, 'includes/email_compose_form.html', { - 'form': form, - 'job': job, - 'candidates': candidates - }) + if "HX-Request" in request.headers: + return JsonResponse( + { + "success": False, + "error": "Please correct the form errors and try again.", + } + ) - else: + return render( + request, + "includes/email_compose_form.html", + {"form": form, "job": job, "candidates": candidate},s + ) + else: # GET request - show the form - form = CandidateEmailForm(job, candidates) - - print("GET request made for candidate email form") + form = CandidateEmailForm(job, candidate) - return render(request, 'includes/email_compose_form.html', { - 'form': form, - 'job': job, - 'candidates':candidates - - - }) + return render( + request, + "includes/email_compose_form.html", + {"form": form, "job": job, "candidate": candidate}, + ) # Source CRUD Views -@login_required +@staff_user_required def source_list(request): """List all sources with search and pagination""" - search_query = request.GET.get('q', '') + search_query = request.GET.get("q", "") sources = Source.objects.all() if search_query: sources = sources.filter( - Q(name__icontains=search_query) | - Q(source_type__icontains=search_query) | - Q(description__icontains=search_query) + Q(name__icontains=search_query) + | Q(source_type__icontains=search_query) + | Q(description__icontains=search_query) ) # Order by most recently created - sources = sources.order_by('-created_at') + sources = sources.order_by("-created_at") # Pagination paginator = Paginator(sources, 15) # Show 15 sources per page - page_number = request.GET.get('page') + page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) context = { - 'page_obj': page_obj, - 'search_query': search_query, - 'total_sources': sources.count(), + "page_obj": page_obj, + "search_query": search_query, + "total_sources": sources.count(), } - return render(request, 'recruitment/source_list.html', context) + return render(request, "recruitment/source_list.html", context) -@login_required +@staff_user_required def source_create(request): """Create a new source""" - if request.method == 'POST': + if request.method == "POST": form = SourceForm(request.POST) if form.is_valid(): source = form.save() messages.success(request, f'Source "{source.name}" created successfully!') - return redirect('source_detail', slug=source.slug) + return redirect("source_detail", slug=source.slug) else: - messages.error(request, 'Please correct the errors below.') + messages.error(request, "Please correct the errors below.") else: form = SourceForm() context = { - 'form': form, - 'title': 'Create New Source', - 'button_text': 'Create Source', + "form": form, + "title": "Create New Source", + "button_text": "Create Source", } - return render(request, 'recruitment/source_form.html', context) + return render(request, "recruitment/source_form.html", context) -@login_required +@staff_user_required def source_detail(request, slug): """View details of a specific source""" source = get_object_or_404(Source, slug=slug) # Get integration logs for this source - integration_logs = source.integration_logs.order_by('-created_at')[:10] # Show recent 10 logs + integration_logs = source.integration_logs.order_by("-created_at")[ + :10 + ] # Show recent 10 logs # Statistics total_logs = source.integration_logs.count() - successful_logs = source.integration_logs.filter(method='POST').count() - failed_logs = source.integration_logs.filter(method='POST', status_code__gte=400).count() + successful_logs = source.integration_logs.filter(method="POST").count() + failed_logs = source.integration_logs.filter( + method="POST", status_code__gte=400 + ).count() context = { - 'source': source, - 'integration_logs': integration_logs, - 'total_logs': total_logs, - 'successful_logs': successful_logs, - 'failed_logs': failed_logs, + "source": source, + "integration_logs": integration_logs, + "total_logs": total_logs, + "successful_logs": successful_logs, + "failed_logs": failed_logs, } - return render(request, 'recruitment/source_detail.html', context) + return render(request, "recruitment/source_detail.html", context) -@login_required +@staff_user_required def source_update(request, slug): """Update an existing source""" source = get_object_or_404(Source, slug=slug) - if request.method == 'POST': + if request.method == "POST": form = SourceForm(request.POST, instance=source) if form.is_valid(): source = form.save() messages.success(request, f'Source "{source.name}" updated successfully!') - return redirect('source_detail', slug=source.slug) + return redirect("source_detail", slug=source.slug) else: - messages.error(request, 'Please correct the errors below.') + messages.error(request, "Please correct the errors below.") else: form = SourceForm(instance=source) context = { - 'form': form, - 'source': source, - 'title': f'Edit Source: {source.name}', - 'button_text': 'Update Source', + "form": form, + "source": source, + "title": f"Edit Source: {source.name}", + "button_text": "Update Source", } - return render(request, 'recruitment/source_form.html', context) + return render(request, "recruitment/source_form.html", context) -@login_required +@staff_user_required def source_delete(request, slug): """Delete a source""" source = get_object_or_404(Source, slug=slug) - if request.method == 'POST': + if request.method == "POST": source_name = source.name source.delete() messages.success(request, f'Source "{source_name}" deleted successfully!') - return redirect('source_list') + return redirect("source_list") context = { - 'source': source, - 'title': 'Delete Source', - 'message': f'Are you sure you want to delete the source "{source.name}"?', - 'cancel_url': reverse('source_detail', kwargs={'slug': source.slug}), + "source": source, + "title": "Delete Source", + "message": f'Are you sure you want to delete the source "{source.name}"?', + "cancel_url": reverse("source_detail", kwargs={"slug": source.slug}), } - return render(request, 'recruitment/source_confirm_delete.html', context) + return render(request, "recruitment/source_confirm_delete.html", context) @login_required @@ -4107,24 +5119,25 @@ def source_generate_keys(request, slug): """Generate new API keys for a source""" source = get_object_or_404(Source, slug=slug) - if request.method == 'POST': + if request.method == "POST": # Generate new API key and secret from .forms import generate_api_key, generate_api_secret + source.api_key = generate_api_key() source.api_secret = generate_api_secret() - source.save(update_fields=['api_key', 'api_secret']) + source.save(update_fields=["api_key", "api_secret"]) messages.success(request, f'New API keys generated for "{source.name}"!') - return redirect('source_detail', slug=source.slug) + return redirect("source_detail", slug=source.slug) # For GET requests, show confirmation page context = { - 'source': source, - 'title': 'Generate New API Keys', - 'message': f'Are you sure you want to generate new API keys for "{source.name}"? This will invalidate the existing keys.', - 'cancel_url': reverse('source_detail', kwargs={'slug': source.slug}), + "source": source, + "title": "Generate New API Keys", + "message": f'Are you sure you want to generate new API keys for "{source.name}"? This will invalidate the existing keys.', + "cancel_url": reverse("source_detail", kwargs={"slug": source.slug}), } - return render(request, 'recruitment/source_confirm_generate_keys.html', context) + return render(request, "recruitment/source_confirm_generate_keys.html", context) @login_required @@ -4132,21 +5145,39 @@ def source_toggle_status(request, slug): """Toggle active status of a source""" source = get_object_or_404(Source, slug=slug) - if request.method == 'POST': + if request.method == "POST": source.is_active = not source.is_active - source.save(update_fields=['is_active']) + source.save(update_fields=["is_active"]) - status_text = 'activated' if source.is_active else 'deactivated' + status_text = "activated" if source.is_active else "deactivated" messages.success(request, f'Source "{source.name}" has been {status_text}!') # Handle HTMX requests - if 'HX-Request' in request.headers: + if "HX-Request" in request.headers: return HttpResponse(status=200) # HTMX success response - return redirect('source_detail', slug=source.slug) + return redirect("source_detail", slug=source.slug) # For GET requests, return error - return JsonResponse({'success': False, 'error': 'Method not allowed'}) + return JsonResponse({"success": False, "error": "Method not allowed"}) + + +def candidate_signup(request,slug): + from .forms import CandidateSignupForm + + job = get_object_or_404(JobPosting, slug=slug) + if request.method == "POST": + form = CandidateSignupForm(request.POST) + if form.is_valid(): + try: + application = form.save(job) + return redirect("application_success", slug=job.slug) + except Exception as e: + messages.error(request, f"Error creating application: {str(e)}") + return render(request, "recruitment/candidate_signup.html", {"form": form, "job": job}) + + form = CandidateSignupForm() + return render(request, "recruitment/candidate_signup.html", {"form": form, "job": job}) @@ -4162,20 +5193,20 @@ def create_interview_participants(request,slug): candidate = form.save(commit=False) candidate.save() # This is important for ManyToMany fields: save the many-to-many data - form.save_m2m() + form.save_m2m() return redirect('meeting_details',slug=interview_slug) # Redirect to a success page else: form = InterviewParticpantsForm(instance=schedule_interview) - + return render(request, 'interviews/interview_participants_form.html', {'form': form}) from django.core.mail import send_mail def send_interview_email(request, slug): from .email_service import send_bulk_email - + interview = get_object_or_404(ScheduledInterview, slug=slug) - + # 2. Retrieve the required data for the form's constructor candidate = interview.candidate job=interview.job @@ -4190,16 +5221,16 @@ def send_interview_email(request, slug): # --- POST REQUEST HANDLING --- if request.method == 'POST': - + form = InterviewEmailForm( - request.POST, + request.POST, candidate=candidate, external_participants=external_participants, system_participants=system_participants, - meeting=meeting, + meeting=meeting, job=job ) - + if form.is_valid(): # 4. Extract cleaned data subject = form.cleaned_data['subject'] @@ -4213,7 +5244,7 @@ def send_interview_email(request, slug): subject, msg_agency, settings.DEFAULT_FROM_EMAIL, - [candidate.hiring_agency.email], + [candidate.hiring_agency.email], fail_silently=False, ) else: @@ -4221,11 +5252,11 @@ def send_interview_email(request, slug): subject, msg_candidate, settings.DEFAULT_FROM_EMAIL, - [candidate.email], + [candidate.email], fail_silently=False, ) - + email_result = send_bulk_email( subject=subject, message=msg_participants, @@ -4235,7 +5266,7 @@ def send_interview_email(request, slug): async_task_=True, # Changed to False to avoid pickle issues, from_interview=True ) - + if email_result['success']: messages.success(request, f'Email sent successfully to {total_recipients} recipient(s).') @@ -4243,9 +5274,9 @@ def send_interview_email(request, slug): else: messages.error(request, f'Failed to send email: {email_result.get("message", "Unknown error")}') return redirect('list_meetings') - - + + # def schedule_interview_location_form(request,slug): # schedule=get_object_or_404(InterviewSchedule,slug=slug) # if request.method=='POST': @@ -4255,7 +5286,7 @@ def send_interview_email(request, slug): # else: # form=InterviewScheduleLocationForm(instance=schedule) # return render(request,'interviews/schedule_interview_location_form.html',{'form':form,'schedule':schedule}) - + def onsite_interview_list_view(request): diff --git a/recruitment/views_frontend.py b/recruitment/views_frontend.py index ae8fb18..ada854a 100644 --- a/recruitment/views_frontend.py +++ b/recruitment/views_frontend.py @@ -30,6 +30,9 @@ from django.utils import timezone from datetime import timedelta import json +# Add imports for user type restrictions +from recruitment.decorators import StaffRequiredMixin, staff_user_required + from datastar_py.django import ( DatastarResponse, @@ -39,7 +42,7 @@ from datastar_py.django import ( # from rich import print from rich.markdown import CodeBlock -class JobListView(LoginRequiredMixin, ListView): +class JobListView(LoginRequiredMixin, StaffRequiredMixin, ListView): model = models.JobPosting template_name = 'jobs/job_list.html' context_object_name = 'jobs' @@ -47,7 +50,6 @@ class JobListView(LoginRequiredMixin, ListView): def get_queryset(self): queryset = super().get_queryset().order_by('-created_at') - # Handle search search_query = self.request.GET.get('search', '') if search_query: @@ -58,24 +60,23 @@ class JobListView(LoginRequiredMixin, ListView): ) # Filter for non-staff users - if not self.request.user.is_staff: - queryset = queryset.filter(status='Published') + # if not self.request.user.is_staff: + # queryset = queryset.filter(status='Published') - status=self.request.GET.get('status') + status = self.request.GET.get('status') if status: - queryset=queryset.filter(status=status) + queryset = queryset.filter(status=status) return queryset def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) context['search_query'] = self.request.GET.get('search', '') context['lang'] = get_language() return context -class JobCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): +class JobCreateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, CreateView): model = models.JobPosting form_class = forms.JobPostingForm template_name = 'jobs/create_job.html' @@ -83,7 +84,7 @@ class JobCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): success_message = 'Job created successfully.' -class JobUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): +class JobUpdateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, UpdateView): model = models.JobPosting form_class = forms.JobPostingForm template_name = 'jobs/edit_job.html' @@ -92,27 +93,25 @@ class JobUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): slug_url_kwarg = 'slug' -class JobDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): +class JobDeleteView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, DeleteView): model = models.JobPosting template_name = 'jobs/partials/delete_modal.html' success_url = reverse_lazy('job_list') success_message = 'Job deleted successfully.' slug_url_kwarg = 'slug' -class JobCandidatesListView(LoginRequiredMixin, ListView): - model = models.Candidate +class JobApplicationListView(LoginRequiredMixin, StaffRequiredMixin, ListView): + model = models.Application template_name = 'jobs/job_candidates_list.html' - context_object_name = 'candidates' + context_object_name = 'applications' paginate_by = 10 - - def get_queryset(self): # Get the job by slug self.job = get_object_or_404(models.JobPosting, slug=self.kwargs['slug']) # Filter candidates for this specific job - queryset = models.Candidate.objects.filter(job=self.job) + queryset = models.Application.objects.filter(job=self.job) if self.request.GET.get('stage'): stage=self.request.GET.get('stage') @@ -132,7 +131,7 @@ class JobCandidatesListView(LoginRequiredMixin, ListView): # Filter for non-staff users if not self.request.user.is_staff: - return models.Candidate.objects.none() # Restrict for non-staff + return models.Application.objects.none() # Restrict for non-staff return queryset.order_by('-created_at') @@ -143,10 +142,10 @@ class JobCandidatesListView(LoginRequiredMixin, ListView): return context -class CandidateListView(LoginRequiredMixin, ListView): - model = models.Candidate +class ApplicationListView(LoginRequiredMixin, StaffRequiredMixin, ListView): + model = models.Application template_name = 'recruitment/candidate_list.html' - context_object_name = 'candidates' + context_object_name = 'applications' paginate_by = 100 def get_queryset(self): @@ -156,22 +155,22 @@ class CandidateListView(LoginRequiredMixin, ListView): search_query = self.request.GET.get('search', '') job = self.request.GET.get('job', '') stage = self.request.GET.get('stage', '') - if search_query: - queryset = queryset.filter( - Q(first_name__icontains=search_query) | - Q(last_name__icontains=search_query) | - Q(email__icontains=search_query) | - Q(phone__icontains=search_query) | - Q(stage__icontains=search_query) | - Q(job__title__icontains=search_query) - ) + # if search_query: + # queryset = queryset.filter( + # Q(first_name__icontains=search_query) | + # Q(last_name__icontains=search_query) | + # Q(email__icontains=search_query) | + # Q(phone__icontains=search_query) | + # Q(stage__icontains=search_query) | + # Q(job__title__icontains=search_query) + # ) if job: queryset = queryset.filter(job__slug=job) if stage: queryset = queryset.filter(stage=stage) # Filter for non-staff users - if not self.request.user.is_staff: - return models.Candidate.objects.none() # Restrict for non-staff + # if not self.request.user.is_staff: + # return models.Application.objects.none() # Restrict for non-staff return queryset.order_by('-created_at') @@ -184,9 +183,9 @@ class CandidateListView(LoginRequiredMixin, ListView): return context -class CandidateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): - model = models.Candidate - form_class = forms.CandidateForm +class ApplicationCreateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, CreateView): + model = models.Application + form_class = forms.ApplicationForm template_name = 'recruitment/candidate_create.html' success_url = reverse_lazy('candidate_list') success_message = 'Candidate created successfully.' @@ -204,18 +203,23 @@ class CandidateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): form.instance.job = job return super().form_valid(form) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + if self.request.method == 'GET': + context['person_form'] = forms.PersonForm() + return context -class CandidateUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): - model = models.Candidate - form_class = forms.CandidateForm +class ApplicationUpdateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, UpdateView): + model = models.Application + form_class = forms.ApplicationForm template_name = 'recruitment/candidate_update.html' success_url = reverse_lazy('candidate_list') success_message = 'Candidate updated successfully.' slug_url_kwarg = 'slug' -class CandidateDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): - model = models.Candidate +class ApplicationDeleteView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, DeleteView): + model = models.Application template_name = 'recruitment/candidate_delete.html' success_url = reverse_lazy('candidate_list') success_message = 'Candidate deleted successfully.' @@ -225,28 +229,30 @@ class CandidateDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): def retry_scoring_view(request,slug): from django_q.tasks import async_task - candidate = get_object_or_404(models.Candidate, slug=slug) - + application = get_object_or_404(models.Application, slug=slug) + async_task( 'recruitment.tasks.handle_reume_parsing_and_scoring', - candidate.pk, + application.pk, hook='recruitment.hooks.callback_ai_parsing', sync=True, - ) - return redirect('candidate_detail', slug=candidate.slug) + ) + return redirect('candidate_detail', slug=application.slug) @login_required +@staff_user_required def training_list(request): materials = models.TrainingMaterial.objects.all().order_by('-created_at') return render(request, 'recruitment/training_list.html', {'materials': materials}) @login_required +@staff_user_required def candidate_detail(request, slug): from rich.json import JSON - candidate = get_object_or_404(models.Candidate, slug=slug) + candidate = get_object_or_404(models.Application, slug=slug) try: parsed = ast.literal_eval(candidate.parsed_summary) except: @@ -255,9 +261,10 @@ def candidate_detail(request, slug): # Create stage update form for staff users stage_form = None if request.user.is_staff: - stage_form = forms.CandidateStageForm() - - + stage_form = forms.ApplicationStageForm() + + + # parsed = JSON(json.dumps(parsed), indent=2, highlight=True, skip_keys=False, ensure_ascii=False, check_circular=True, allow_nan=True, default=None, sort_keys=False) # parsed = json_to_markdown_table([parsed]) return render(request, 'recruitment/candidate_detail.html', { @@ -268,31 +275,33 @@ def candidate_detail(request, slug): @login_required +@staff_user_required def candidate_resume_template_view(request, slug): """Display formatted resume template for a candidate""" - candidate = get_object_or_404(models.Candidate, slug=slug) + application = get_object_or_404(models.Application, slug=slug) if not request.user.is_staff: messages.error(request, _("You don't have permission to view this page.")) return redirect('candidate_list') return render(request, 'recruitment/candidate_resume_template.html', { - 'candidate': candidate + 'application': application }) @login_required +@staff_user_required def candidate_update_stage(request, slug): """Handle HTMX stage update requests""" - candidate = get_object_or_404(models.Candidate, slug=slug) - form = forms.CandidateStageForm(request.POST, instance=candidate) + application = get_object_or_404(models.Application, slug=slug) + form = forms.ApplicationStageForm(request.POST, instance=application) if form.is_valid(): stage_value = form.cleaned_data['stage'] - candidate.stage = stage_value - candidate.save(update_fields=['stage']) - messages.success(request,"Candidate Stage Updated") - return redirect("candidate_detail",slug=candidate.slug) + application.stage = stage_value + application.save(update_fields=['stage']) + messages.success(request,"application Stage Updated") + return redirect("candidate_detail",slug=application.slug) -class TrainingListView(LoginRequiredMixin, ListView): +class TrainingListView(LoginRequiredMixin, StaffRequiredMixin, ListView): model = models.TrainingMaterial template_name = 'recruitment/training_list.html' context_object_name = 'materials' @@ -320,7 +329,7 @@ class TrainingListView(LoginRequiredMixin, ListView): return context -class TrainingCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): +class TrainingCreateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, CreateView): model = models.TrainingMaterial form_class = forms.TrainingMaterialForm template_name = 'recruitment/training_create.html' @@ -332,7 +341,7 @@ class TrainingCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): return super().form_valid(form) -class TrainingUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): +class TrainingUpdateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, UpdateView): model = models.TrainingMaterial form_class = forms.TrainingMaterialForm template_name = 'recruitment/training_update.html' @@ -341,13 +350,13 @@ class TrainingUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): slug_url_kwarg = 'slug' -class TrainingDetailView(LoginRequiredMixin, DetailView): +class TrainingDetailView(LoginRequiredMixin, StaffRequiredMixin, DetailView): model = models.TrainingMaterial template_name = 'recruitment/training_detail.html' context_object_name = 'material' slug_url_kwarg = 'slug' -class TrainingDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): +class TrainingDeleteView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, DeleteView): model = models.TrainingMaterial template_name = 'recruitment/training_delete.html' success_url = reverse_lazy('training_list') @@ -355,7 +364,7 @@ class TrainingDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): # IMPORTANT: Ensure 'models' correctly refers to your Django models file -# Example: from . import models +# Example: from . import models # --- Constants --- SCORE_PATH = 'ai_analysis_data__analysis_data__match_score' @@ -365,27 +374,28 @@ TARGET_TIME_TO_HIRE_DAYS = 45 # Used for the template visualization @login_required +@staff_user_required def dashboard_view(request): - + selected_job_pk = request.GET.get('selected_job_pk') today = timezone.now().date() - + # --- 1. BASE QUERYSETS & GLOBAL METRICS (UNFILTERED) --- - + all_jobs_queryset = models.JobPosting.objects.all().order_by('-created_at') - all_candidates_queryset = models.Candidate.objects.all() + all_candidates_queryset = models.Application.objects.all() # Global KPI Card Metrics total_jobs_global = all_jobs_queryset.count() total_participants = models.Participants.objects.count() total_jobs_posted_linkedin = all_jobs_queryset.filter(linkedin_post_id__isnull=False).count() - + # Data for Job App Count Chart (always for ALL jobs) job_titles = [job.title for job in all_jobs_queryset] - job_app_counts = [job.candidates.count() for job in all_jobs_queryset] + job_app_counts = [job.applications.count() for job in all_jobs_queryset] # --- 2. TIME SERIES: GLOBAL DAILY APPLICANTS --- - + # Group ALL candidates by creation date global_daily_applications_qs = all_candidates_queryset.annotate( date=TruncDate('created_at') @@ -398,22 +408,22 @@ def dashboard_view(request): # --- 3. FILTERING LOGIC: Determine the scope for scoped metrics --- - + candidate_queryset = all_candidates_queryset job_scope_queryset = all_jobs_queryset interview_queryset = models.ScheduledInterview.objects.all() - + current_job = None if selected_job_pk: # Filter all base querysets candidate_queryset = candidate_queryset.filter(job__pk=selected_job_pk) interview_queryset = interview_queryset.filter(job__pk=selected_job_pk) - + try: current_job = all_jobs_queryset.get(pk=selected_job_pk) job_scope_queryset = models.JobPosting.objects.filter(pk=selected_job_pk) except models.JobPosting.DoesNotExist: - pass + pass # --- 4. TIME SERIES: SCOPED DAILY APPLICANTS --- @@ -426,15 +436,15 @@ def dashboard_view(request): ).values('date').annotate( count=Count('pk') ).order_by('date') - + scoped_dates = [item['date'].strftime('%Y-%m-%d') for item in scoped_daily_applications_qs] scoped_counts = [item['count'] for item in scoped_daily_applications_qs] # --- 5. SCOPED CORE AGGREGATIONS (FILTERED OR ALL) --- - + total_candidates = candidate_queryset.count() - + candidates_with_score_query = candidate_queryset.filter( is_resume_parsed=True ).annotate( @@ -448,11 +458,11 @@ def dashboard_view(request): total_active_jobs = job_scope_queryset.filter(status="ACTIVE").count() last_week = timezone.now() - timedelta(days=7) new_candidates_7days = candidate_queryset.filter(created_at__gte=last_week).count() - + open_positions_agg = job_scope_queryset.filter(status="ACTIVE").aggregate(total_open=Sum('open_positions')) total_open_positions = open_positions_agg['total_open'] or 0 average_applications_result = job_scope_queryset.annotate( - candidate_count=Count('candidates', distinct=True) + candidate_count=Count('applications', distinct=True) ).aggregate(avg_apps=Avg('candidate_count'))['avg_apps'] average_applications = round(average_applications_result or 0, 2) @@ -463,21 +473,24 @@ def dashboard_view(request): ) lst=[c.time_to_hire_days for c in hired_candidates] - + time_to_hire_query = hired_candidates.annotate( time_diff=ExpressionWrapper( - F('hired_date') - F('created_at__date'), + F('join_date') - F('created_at__date'), output_field=fields.DurationField() ) ).aggregate(avg_time_to_hire=Avg('time_diff')) - - + print(time_to_hire_query) + + + avg_time_to_hire_days = ( - time_to_hire_query.get('avg_time_to_hire').days + time_to_hire_query.get('avg_time_to_hire').days if time_to_hire_query.get('avg_time_to_hire') else 0 ) - + print(avg_time_to_hire_days) + applied_count = candidate_queryset.filter(stage='Applied').count() advanced_count = candidate_queryset.filter(stage__in=['Exam', 'Interview', 'Offer']).count() screening_pass_rate = round( (advanced_count / applied_count) * 100, 1 ) if applied_count > 0 else 0 @@ -493,8 +506,8 @@ def dashboard_view(request): meetings_scheduled_this_week = interview_queryset.filter( interview_date__week=current_week, interview_date__year=current_year ).count() - avg_match_score_result = candidates_with_score_query.aggregate(avg_score=Avg('annotated_match_score'))['avg_score'] - avg_match_score = round(avg_match_score_result or 0, 1) + avg_match_score_result = candidates_with_score_query.aggregate(avg_score=Avg('annotated_match_score'))['avg_score'] + avg_match_score = round(avg_match_score_result or 0, 1) high_potential_count = candidates_with_score_query.filter(annotated_match_score__gte=HIGH_POTENTIAL_THRESHOLD).count() high_potential_ratio = round( (high_potential_count / total_candidates) * 100, 1 ) if total_candidates > 0 else 0 total_scored_candidates = candidates_with_score_query.count() @@ -506,15 +519,15 @@ def dashboard_view(request): # A. Pipeline Funnel (Scoped) stage_counts = candidate_queryset.values('stage').annotate(count=Count('stage')) stage_map = {item['stage']: item['count'] for item in stage_counts} - candidate_stage = ['Applied', 'Exam', 'Interview', 'Offer', 'Hired'] + candidate_stage = ['Applied', 'Exam', 'Interview', 'Offer', 'Hired'] candidates_count = [ - stage_map.get('Applied', 0), stage_map.get('Exam', 0), stage_map.get('Interview', 0), + stage_map.get('Applied', 0), stage_map.get('Exam', 0), stage_map.get('Interview', 0), stage_map.get('Offer', 0), stage_map.get('Hired',0) ] - + # --- 7. GAUGE CHART CALCULATION (Time-to-Hire) --- - + current_days = avg_time_to_hire_days rotation_percent = current_days / MAX_TIME_TO_HIRE_DAYS if MAX_TIME_TO_HIRE_DAYS > 0 else 0 rotation_degrees = rotation_percent * 180 @@ -524,20 +537,20 @@ def dashboard_view(request): hiring_source_counts = candidate_queryset.values('hiring_source').annotate(count=Count('stage')) source_map= {item['hiring_source']: item['count'] for item in hiring_source_counts} candidates_count_in_each_source = [ - source_map.get('Public', 0), source_map.get('Internal', 0), source_map.get('Agency', 0), - + source_map.get('Public', 0), source_map.get('Internal', 0), source_map.get('Agency', 0), + ] - all_hiring_sources=["Public", "Internal", "Agency"] + all_hiring_sources=["Public", "Internal", "Agency"] # --- 8. CONTEXT RETURN --- - + context = { # Global KPIs 'total_jobs_global': total_jobs_global, 'total_participants': total_participants, 'total_jobs_posted_linkedin': total_jobs_posted_linkedin, - + # Scoped KPIs 'total_active_jobs': total_active_jobs, 'total_candidates': total_candidates, @@ -549,16 +562,16 @@ def dashboard_view(request): 'offers_accepted_rate': offers_accepted_rate, 'vacancy_fill_rate': vacancy_fill_rate, 'meetings_scheduled_this_week': meetings_scheduled_this_week, - 'avg_match_score': avg_match_score, + 'avg_match_score': avg_match_score, 'high_potential_count': high_potential_count, 'high_potential_ratio': high_potential_ratio, 'scored_ratio': scored_ratio, - + # Chart Data 'candidate_stage': json.dumps(candidate_stage), 'candidates_count': json.dumps(candidates_count), - 'job_titles': json.dumps(job_titles), - 'job_app_counts': json.dumps(job_app_counts), + 'job_titles': json.dumps(job_titles), + 'job_app_counts': json.dumps(job_app_counts), # 'source_volume_chart_data' is intentionally REMOVED # Time Series Data @@ -572,7 +585,7 @@ def dashboard_view(request): 'gauge_max_days': MAX_TIME_TO_HIRE_DAYS, 'gauge_target_days': TARGET_TIME_TO_HIRE_DAYS, 'gauge_rotation_degrees': rotation_degrees_final, - + # UI Control 'jobs': all_jobs_queryset, 'current_job_id': selected_job_pk, @@ -582,11 +595,12 @@ def dashboard_view(request): 'candidates_count_in_each_source': json.dumps(candidates_count_in_each_source), 'all_hiring_sources': json.dumps(all_hiring_sources), } - + return render(request, 'recruitment/dashboard.html', context) @login_required +@staff_user_required def candidate_offer_view(request, slug): """View for candidates in the Offer stage""" job = get_object_or_404(models.JobPosting, slug=slug) @@ -616,6 +630,7 @@ def candidate_offer_view(request, slug): @login_required +@staff_user_required def candidate_hired_view(request, slug): """View for hired candidates""" job = get_object_or_404(models.JobPosting, slug=slug) @@ -645,13 +660,16 @@ def candidate_hired_view(request, slug): @login_required +@staff_user_required def update_candidate_status(request, job_slug, candidate_slug, stage_type, status): """Handle exam/interview/offer status updates""" from django.utils import timezone job = get_object_or_404(models.JobPosting, slug=job_slug) - candidate = get_object_or_404(models.Candidate, slug=candidate_slug, job=job) - + candidate = get_object_or_404(models.Application, slug=candidate_slug, job=job) + print(stage_type) + print(status) + print(request.method) if request.method == "POST": if stage_type == 'exam': candidate.exam_status = status @@ -709,6 +727,7 @@ STAGE_CONFIG = { @login_required +@staff_user_required def export_candidates_csv(request, job_slug, stage): """Export candidates for a specific stage as CSV""" job = get_object_or_404(models.JobPosting, slug=job_slug) @@ -722,9 +741,9 @@ def export_candidates_csv(request, job_slug, stage): # Filter candidates based on stage if stage == 'hired': - candidates = job.candidates.filter(**config['filter']) + candidates = job.applications.filter(**config['filter']) else: - candidates = job.candidates.filter(**config['filter']) + candidates = job.applications.filter(**config['filter']) # Handle search if provided search_query = request.GET.get('search', '') @@ -848,6 +867,7 @@ def export_candidates_csv(request, job_slug, stage): @login_required +@staff_user_required def sync_hired_candidates(request, job_slug): """Sync hired candidates to external sources using Django-Q""" from django_q.tasks import async_task @@ -886,6 +906,7 @@ def sync_hired_candidates(request, job_slug): @login_required +@staff_user_required def test_source_connection(request, source_id): """Test connection to an external source""" from .candidate_sync_service import CandidateSyncService @@ -920,6 +941,7 @@ def test_source_connection(request, source_id): @login_required +@staff_user_required def sync_task_status(request, task_id): """Check the status of a sync task""" from django_q.models import Task @@ -971,6 +993,7 @@ def sync_task_status(request, task_id): @login_required +@staff_user_required def sync_history(request, job_slug=None): """View sync history and logs""" from .models import IntegrationLog @@ -1005,7 +1028,7 @@ def sync_history(request, job_slug=None): #participants views -class ParticipantsListView(LoginRequiredMixin, ListView): +class ParticipantsListView(LoginRequiredMixin, StaffRequiredMixin, ListView): model = models.Participants template_name = 'participants/participants_list.html' context_object_name = 'participants' @@ -1034,13 +1057,13 @@ class ParticipantsListView(LoginRequiredMixin, ListView): context = super().get_context_data(**kwargs) context['search_query'] = self.request.GET.get('search', '') return context -class ParticipantsDetailView(LoginRequiredMixin, DetailView): +class ParticipantsDetailView(LoginRequiredMixin, StaffRequiredMixin, DetailView): model = models.Participants template_name = 'participants/participants_detail.html' context_object_name = 'participant' slug_url_kwarg = 'slug' -class ParticipantsCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): +class ParticipantsCreateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, CreateView): model = models.Participants form_class = forms.ParticipantsForm template_name = 'participants/participants_create.html' @@ -1054,9 +1077,9 @@ class ParticipantsCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateVie # initial['jobs'] = [job] # return initial - - -class ParticipantsUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): + + +class ParticipantsUpdateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, UpdateView): model = models.Participants form_class = forms.ParticipantsForm template_name = 'participants/participants_create.html' @@ -1064,9 +1087,9 @@ class ParticipantsUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateVie success_message = 'Participant updated successfully.' slug_url_kwarg = 'slug' -class ParticipantsDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): +class ParticipantsDeleteView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, DeleteView): model = models.Participants - + success_url = reverse_lazy('participants_list') # Redirect to the participants list after success success_message = 'Participant deleted successfully.' - slug_url_kwarg = 'slug' \ No newline at end of file + slug_url_kwarg = 'slug' diff --git a/templates/base.html b/templates/base.html index 404faaa..7ac566d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -122,6 +122,11 @@ {% endif %} {% endcomment %} + + @@ -340,6 +353,7 @@ + + + + {% comment %} {% if request.user.is_authenticated and request.user.is_staff %} diff --git a/templates/includes/paginator.html b/templates/includes/paginator.html index b56c3ca..d11e555 100644 --- a/templates/includes/paginator.html +++ b/templates/includes/paginator.html @@ -4,24 +4,24 @@ {# Helper to build the query string while excluding the 'page' parameter #} {% load url_extras %} - + {# Build a string of all current filters (e.g., &department=IT&type=FULL_TIME) #} {% add_get_params request.GET as filter_params %} {% with filter_params=filter_params %} {% if page_obj.has_previous %} - + {# First Page Link #}
  • - First
  • - + {# Previous Page Link #}
  • - Previous @@ -36,26 +36,26 @@
  • {% if page_obj.has_next %} - + {# Next Page Link #}
  • - Next
  • - + {# Last Page Link #}
  • - Last
  • {% endif %} - + {% endwith %} - + {% endif %} \ No newline at end of file diff --git a/templates/interviews/preview_schedule.html b/templates/interviews/preview_schedule.html index eb37dff..1d520ae 100644 --- a/templates/interviews/preview_schedule.html +++ b/templates/interviews/preview_schedule.html @@ -65,7 +65,7 @@ padding: 10px 15px; background-color: var(--kaauh-teal-light); } - + /* FullCalendar Customization */ #calendar { font-size: 0.9em; @@ -87,7 +87,7 @@ {% block content %}
    - +

    Interview Schedule Preview: **{{ job.title }}** @@ -98,13 +98,13 @@

    {% trans "Schedule Parameters" %}

    - +

    Working Hours: {{ start_time|time:"g:i A" }} to {{ end_time|time:"g:i A" }}

    Interview Duration: {{ interview_duration }} minutes

    Buffer Time: {{ buffer_time }} minutes

    - +

    Interview Period: {{ start_date|date:"F j, Y" }} — {{ end_date|date:"F j, Y" }}

    Active Days: @@ -122,7 +122,7 @@

    Interview Type: {{interview_type}}

    - +
    {% trans "Daily Break Times" %}
    {% if breaks %}
    @@ -162,9 +162,9 @@ {% for item in schedule %} {{ item.date|date:"F j, Y" }} - {{ item.time|time:"g:i A" }} - {{ item.candidate.name }} - {{ item.candidate.email }} + {{ item.time|time:"g:i A" }} + {{ item.applications.name }} + {{ item.applications.email }} {% endfor %} @@ -204,7 +204,7 @@ document.addEventListener('DOMContentLoaded', function() { start: '{{ item.date|date:"Y-m-d" }}T{{ item.time|time:"H:i:s" }}', url: '#', // Use the theme color for candidate events - color: 'var(--kaauh-teal-dark)', + color: 'var(--kaauh-teal-dark)', extendedProps: { email: '{{ item.candidate.email }}', time: '{{ item.time|time:"g:i A" }}' @@ -214,7 +214,7 @@ document.addEventListener('DOMContentLoaded', function() { {% for break in breaks %} { title: 'Break', - // FullCalendar requires a specific date for breaks, using start_date as a placeholder for daily breaks. + // FullCalendar requires a specific date for breaks, using start_date as a placeholder for daily breaks. // Note: Breaks displayed on the monthly grid will only show on start_date, but weekly/daily view should reflect it daily if implemented correctly in the backend or using recurring events. start: '{{ start_date|date:"Y-m-d" }}T{{ break.start_time|time:"H:i:s" }}', end: '{{ start_date|date:"Y-m-d" }}T{{ break.end_time|time:"H:i:s" }}', diff --git a/templates/interviews/schedule_interviews.html b/templates/interviews/schedule_interviews.html index a50e158..31239ba 100644 --- a/templates/interviews/schedule_interviews.html +++ b/templates/interviews/schedule_interviews.html @@ -130,9 +130,9 @@ - {{ form.candidates }} - {% if form.candidates.errors %} -
    {{ form.candidates.errors }}
    + {{ form.applications }} + {% if form.applications.errors %} +
    {{ form.applications.errors }}
    {% endif %}
    diff --git a/templates/jobs/job_list.html b/templates/jobs/job_list.html index 1a78fda..68d7c86 100644 --- a/templates/jobs/job_list.html +++ b/templates/jobs/job_list.html @@ -285,7 +285,7 @@ {% trans "Offer" %} - + {% for job in jobs %} diff --git a/templates/meetings/list_meetings.html b/templates/meetings/list_meetings.html index c0a2972..b435c9c 100644 --- a/templates/meetings/list_meetings.html +++ b/templates/meetings/list_meetings.html @@ -319,7 +319,7 @@ {{ meeting.topic }} {% if meeting.interview %} - {{ meeting.interview.candidate.name }} + {{ meeting.interview.candidate.name }} {% else %} +

    + +
    + + + + +{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/templates/messages/message_list.html b/templates/messages/message_list.html new file mode 100644 index 0000000..e506f70 --- /dev/null +++ b/templates/messages/message_list.html @@ -0,0 +1,230 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Messages{% endblock %} + +{% block content %} +
    +
    +
    +
    +

    Messages

    + + Compose Message + +
    + + +
    +
    +
    +
    + + +
    +
    + + +
    +
    + +
    + + +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    Total Messages
    +

    {{ total_messages }}

    +
    +
    +
    +
    +
    +
    +
    Unread Messages
    +

    {{ unread_messages }}

    +
    +
    +
    +
    + + +
    +
    + {% if page_obj %} +
    + + + + + + + + + + + + + + {% for message in page_obj %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
    SubjectSenderRecipientTypeStatusCreatedActions
    + + {{ message.subject }} + + {% if message.parent_message %} + Reply + {% endif %} + {{ message.sender.get_full_name|default:message.sender.username }}{{ message.recipient.get_full_name|default:message.recipient.username }} + + {{ message.get_message_type_display }} + + + {% if message.is_read %} + Read + {% else %} + Unread + {% endif %} + {{ message.created_at|date:"M d, Y H:i" }} +
    + + + + {% if not message.is_read and message.recipient == request.user %} + + + + {% endif %} + + + + + + +
    +
    + +

    No messages found.

    +

    Try adjusting your filters or compose a new message.

    +
    +
    + + + {% if page_obj.has_other_pages %} + + {% endif %} + {% else %} +
    + +

    No messages found.

    +

    Try adjusting your filters or compose a new message.

    + + Compose Message + +
    + {% endif %} +
    +
    +
    +
    +
    +{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/templates/people/create_person.html b/templates/people/create_person.html new file mode 100644 index 0000000..fbc6867 --- /dev/null +++ b/templates/people/create_person.html @@ -0,0 +1,444 @@ +{% extends "base.html" %} +{% load static i18n crispy_forms_tags %} + +{% block title %}Create Person - {{ block.super }}{% endblock %} + +{% block customCSS %} + +{% endblock %} + +{% block content %} +
    +
    + + + + +
    +

    + {% trans "Create New Person" %} +

    + + {% trans "Back to List" %} + +
    + + +
    +
    + {% if form.non_field_errors %} + + {% endif %} + +
    + {% csrf_token %} + + +
    +
    +
    +
    + +
    {% trans "Upload Profile Photo" %}
    +

    {% trans "Click to browse or drag and drop" %}

    +
    + +
    +
    +
    + + +
    +
    +
    + {% trans "Personal Information" %} +
    +
    +
    + {{ form.first_name|as_crispy_field }} +
    +
    + {{ form.middle_name|as_crispy_field }} +
    +
    + {{ form.last_name|as_crispy_field }} +
    +
    + + +
    +
    +
    + {% trans "Contact Information" %} +
    +
    +
    + {{ form.email|as_crispy_field }} +
    +
    + {{ form.phone|as_crispy_field }} +
    +
    + + +
    +
    +
    + {% trans "Additional Information" %} +
    +
    +
    + {{ form.date_of_birth|as_crispy_field }} +
    +
    + {{ form.nationality|as_crispy_field }} +
    +
    + {{ form.gender|as_crispy_field }} +
    +
    + + +
    +
    +
    + {% trans "Address" %} +
    +
    +
    + {{ form.address }} +
    +
    + + + {% comment %}
    +
    +
    + {% trans "Professional Profile" %} +
    +
    +
    +
    + + + + {% trans "Optional: Add LinkedIn profile URL" %} + +
    +
    +
    {% endcomment %} + + +
    +
    +
    + + {% trans "Cancel" %} + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block customJS %} + + + + + + + + +{% endblock %} diff --git a/templates/people/person_detail.html b/templates/people/person_detail.html new file mode 100644 index 0000000..79c115a --- /dev/null +++ b/templates/people/person_detail.html @@ -0,0 +1,607 @@ +{% extends "base.html" %} +{% load static i18n %} + +{% block title %}{{ person.get_full_name }} - {{ block.super }}{% endblock %} + +{% block customCSS %} + +{% endblock %} + +{% block content %} +
    + + + + +
    +
    +
    + {% if person.profile_image %} + {{ person.get_full_name }} + {% else %} +
    + +
    + {% endif %} +
    +
    +

    {{ person.get_full_name }}

    + {% if person.email %} +

    + {{ person.email }} +

    + {% endif %} +
    + {% if person.nationality %} + + {{ person.nationality }} + + {% endif %} + {% if person.gender %} + + {% if person.gender == 'M' %}{% trans "Male" %}{% else %}{% trans "Female" %}{% endif %} + + {% endif %} + {% if person.user %} + + {% trans "User Account" %} + + {% endif %} +
    + {% if user.is_staff %} +
    + + {% trans "Edit Person" %} + + +
    + {% endif %} +
    +
    +
    + +
    + +
    +
    +
    +
    +
    {% trans "Personal Information" %}
    + +
    + + {% trans "Full Name" %}: + {{ person.get_full_name }} +
    + + {% if person.first_name %} +
    + + {% trans "First Name" %}: + {{ person.first_name }} +
    + {% endif %} + + {% if person.middle_name %} +
    + + {% trans "Middle Name" %}: + {{ person.middle_name }} +
    + {% endif %} + + {% if person.last_name %} +
    + + {% trans "Last Name" %}: + {{ person.last_name }} +
    + {% endif %} + + {% if person.date_of_birth %} +
    + + {% trans "Date of Birth" %}: + {{ person.date_of_birth }} +
    + {% endif %} + + {% if person.gender %} +
    + + {% trans "Gender" %}: + + {% if person.gender == 'M' %}{% trans "Male" %}{% else %}{% trans "Female" %}{% endif %} + +
    + {% endif %} + + {% if person.nationality %} +
    + + {% trans "Nationality" %}: + {{ person.nationality }} +
    + {% endif %} +
    +
    +
    +
    + + +
    +
    +
    +
    +
    {% trans "Contact Information" %}
    + + {% if person.email %} +
    + + {% trans "Email" %}: + + + {{ person.email }} + + +
    + {% endif %} + + {% if person.phone %} +
    + + {% trans "Phone" %}: + + + {{ person.phone }} + + +
    + {% endif %} + + {% if person.address %} +
    + + {% trans "Address" %}: + {{ person.address|linebreaksbr }} +
    + {% endif %} + + {% if person.linkedin_profile %} +
    + + {% trans "LinkedIn" %}: + + + {% trans "View Profile" %} + + + +
    + {% endif %} +
    +
    +
    +
    +
    + + +
    + +
    +
    +
    +
    + {% trans "Applications" %} + {{ person.applications.count }} +
    + + {% if person.applications %} + {% for application in person.applications.all %} + + {% endfor %} + {% else %} +
    + +

    {% trans "No applications found" %}

    +
    + {% endif %} +
    +
    +
    + + +
    +
    +
    +
    + {% trans "Documents" %} + {{ person.documents.count }} +
    + + {% if person.documents %} + {% for document in person.documents %} + + {% endfor %} + {% else %} +
    + +

    {% trans "No documents found" %}

    +
    + {% endif %} +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + {% trans "System Information" %} +
    +
    +
    +
    + + {% trans "Created" %}: + {{ person.created_at|date:"d M Y H:i" }} +
    +
    +
    +
    + + {% trans "Last Updated" %}: + {{ person.updated_at|date:"d M Y H:i" }} +
    +
    + {% if person.user %} +
    +
    + + {% trans "User Account" %}: + + + {{ person.user.username }} + + +
    +
    + {% endif %} +
    +
    +
    +
    +
    + + +
    +
    +
    + + {% trans "Back to People" %} + + {% if user.is_staff %} +
    + + {% trans "Edit Person" %} + + +
    + {% endif %} +
    +
    +
    +
    +{% endblock %} + +{% block customJS %} + +{% endblock %} diff --git a/templates/people/person_list.html b/templates/people/person_list.html new file mode 100644 index 0000000..a212326 --- /dev/null +++ b/templates/people/person_list.html @@ -0,0 +1,411 @@ +{% extends "base.html" %} +{% load static i18n %} + +{% block title %}People - {{ block.super }}{% endblock %} + +{% block customCSS %} + +{% endblock %} + +{% block content %} +
    + +
    +

    + {% trans "People Directory" %} +

    + + {% trans "Add New Person" %} + +
    + + +
    +
    +
    +
    + +
    +
    + + +
    +
    +
    + +
    +
    + {% if request.GET.q %}{% endif %} + +
    + + +
    + +
    + + +
    + +
    +
    + + {% if request.GET.q or request.GET.nationality or request.GET.gender %} + + {% trans "Clear" %} + + {% endif %} +
    +
    +
    +
    +
    +
    +
    + + {% if people_list %} +
    + + {% include "includes/_list_view_switcher.html" with list_id="person-list" %} + + +
    +
    + + + + + + + + + + + + + + + + {% for person in people_list %} + + + + + + + + + + + + {% endfor %} + +
    {% trans "Photo" %}{% trans "Name" %}{% trans "Email" %}{% trans "Phone" %}{% trans "Nationality" %}{% trans "Gender" %}{% trans "Agency" %}{% trans "Created" %}{% trans "Actions" %}
    + {% if person.profile_image %} + {{ person.get_full_name }} + {% else %} +
    + +
    + {% endif %} +
    + + {{ person.full_name }} + + {{ person.email|default:"N/A" }}{{ person.phone|default:"N/A" }} + {% if person.nationality %} + {{ person.nationality }} + {% else %} + N/A + {% endif %} + + {% if person.gender %} + + {% if person.gender == 'M' %}{% trans "Male" %}{% else %}{% trans "Female" %}{% endif %} + + {% else %} + N/A + {% endif %} + {{ person.agency.name|default:"N/A" }}{{ person.created_at|date:"d-m-Y" }} +
    + + + + {% if user.is_staff %} + + + + + {% endif %} +
    +
    +
    +
    + + +
    + {% for person in people_list %} +
    +
    +
    +
    +
    + {% if person.profile_image %} + {{ person.get_full_name }} + {% else %} +
    + +
    + {% endif %} +
    +
    +
    + + {{ person.get_full_name }} + +
    +

    {{ person.email|default:"N/A" }}

    +
    +
    + +
    + {% if person.phone %} +
    + {{ person.phone }} +
    + {% endif %} + {% if person.nationality %} +
    + + {{ person.nationality }} +
    + {% endif %} + {% if person.gender %} +
    + + + {% if person.gender == 'M' %}{% trans "Male" %}{% else %}{% trans "Female" %}{% endif %} + +
    + {% endif %} + {% if person.date_of_birth %} +
    + {{ person.date_of_birth }} +
    + {% endif %} +
    + +
    +
    + + {% trans "View" %} + + {% if user.is_staff %} + + {% trans "Edit" %} + + + {% endif %} +
    +
    +
    +
    +
    + {% endfor %} +
    +
    + + + {% include "includes/paginator.html" %} + {% else %} + +
    +
    + +

    {% trans "No people found" %}

    +

    {% trans "Create your first person record." %}

    + {% if user.is_staff %} + + {% trans "Add Person" %} + + {% endif %} +
    +
    + {% endif %} +
    +{% endblock %} diff --git a/templates/people/update_person.html b/templates/people/update_person.html new file mode 100644 index 0000000..788cb56 --- /dev/null +++ b/templates/people/update_person.html @@ -0,0 +1,572 @@ +{% extends "base.html" %} +{% load static i18n crispy_forms_tags %} + +{% block title %}Update {{ person.get_full_name }} - {{ block.super }}{% endblock %} + +{% block customCSS %} + +{% endblock %} + +{% block content %} +
    +
    + + + + +
    +

    + {% trans "Update Person" %} +

    + +
    + + +
    +
    +
    +
    {% trans "Currently Editing" %}
    +
    + {% if person.profile_image %} + {{ person.get_full_name }} + {% else %} +
    + +
    + {% endif %} +
    +
    {{ person.get_full_name }}
    + {% if person.email %} +

    {{ person.email }}

    + {% endif %} + + {% trans "Created" %}: {{ person.created_at|date:"d M Y" }} • + {% trans "Last Updated" %}: {{ person.updated_at|date:"d M Y" }} + +
    +
    +
    +
    +
    + + +
    +
    + {% if form.non_field_errors %} + + {% endif %} + + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + +
    + {% csrf_token %} + + +
    +
    +
    +
    + {% if person.profile_image %} + Current Profile +
    {% trans "Click to change photo" %}
    +

    {% trans "Current photo will be replaced" %}

    + {% else %} + +
    {% trans "Upload Profile Photo" %}
    +

    {% trans "Click to browse or drag and drop" %}

    + {% endif %} +
    + +
    + {% if person.profile_image %} +
    + + {% trans "Leave empty to keep current photo" %} + +
    + {% endif %} +
    +
    + + +
    +
    +
    + {% trans "Personal Information" %} +
    +
    +
    + {{ form.first_name|as_crispy_field }} +
    +
    + {{ form.middle_name|as_crispy_field }} +
    +
    + {{ form.last_name|as_crispy_field }} +
    +
    + + +
    +
    +
    + {% trans "Contact Information" %} +
    +
    +
    + {{ form.email|as_crispy_field }} +
    +
    + {{ form.phone|as_crispy_field }} +
    +
    + + +
    +
    +
    + {% trans "Additional Information" %} +
    +
    +
    + {{ form.date_of_birth|as_crispy_field }} +
    +
    + {{ form.nationality|as_crispy_field }} +
    +
    + {{ form.gender|as_crispy_field }} +
    +
    + + +
    +
    +
    + {% trans "Address Information" %} +
    +
    +
    + {{ form.address|as_crispy_field }} +
    +
    + + +
    +
    +
    + {% trans "Professional Profile" %} +
    +
    +
    +
    + + + + {% trans "Optional: Add LinkedIn profile URL" %} + +
    +
    +
    + + +
    +
    +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block customJS %} + +{% endblock %} diff --git a/templates/agency_base.html b/templates/portal_base.html similarity index 83% rename from templates/agency_base.html rename to templates/portal_base.html index 03b71e3..7386ebf 100644 --- a/templates/agency_base.html +++ b/templates/portal_base.html @@ -49,16 +49,23 @@ - + {# Using inline style for nav background color - replace with a dedicated CSS class (e.g., .bg-kaauh-nav) if defined in main.css #} -
    +
    @@ -146,7 +175,7 @@ - + {# JavaScript (Left unchanged as it was mostly correct) #} + + +{% endblock %} diff --git a/templates/recruitment/agency_portal_submit_candidate.html b/templates/recruitment/agency_portal_submit_candidate.html index 42b4793..7b1fc5c 100644 --- a/templates/recruitment/agency_portal_submit_candidate.html +++ b/templates/recruitment/agency_portal_submit_candidate.html @@ -1,4 +1,4 @@ -{% extends 'agency_base.html' %} +{% extends 'portal_base.html' %} {% load static i18n %} {% block title %}{% trans "Submit Candidate" %} - {{ assignment.job.title }} - Agency Portal{% endblock %} diff --git a/templates/recruitment/candidate_create.html b/templates/recruitment/candidate_create.html index 185ea9f..c31161b 100644 --- a/templates/recruitment/candidate_create.html +++ b/templates/recruitment/candidate_create.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% load static i18n crispy_forms_tags %} -{% block title %}Create Candidate - {{ block.super }}{% endblock %} +{% block title %}Create Application - {{ block.super }}{% endblock %} {% block customCSS %} +{% endblock %} + +{% block content %} + +{% endblock %} + +{% block customJS %} + +{% endblock %} \ No newline at end of file diff --git a/translate_all_batches.py b/translate_all_batches.py new file mode 100644 index 0000000..6ea5d65 --- /dev/null +++ b/translate_all_batches.py @@ -0,0 +1,444 @@ +#!/usr/bin/env python3 +""" +Comprehensive script to translate all remaining batch files with Arabic translations +""" + +import re +import os + +# Comprehensive Arabic translation dictionary +TRANSLATIONS = { + # Email and Authentication + "The date and time this notification is scheduled to be sent.": "التاريخ والوقت المحدد لإرسال هذا الإشعار.", + "Send Attempts": "محاولات الإرسال", + "Failed to start the job posting process. Please try again.": "فشل في بدء عملية نشر الوظيفة. يرجى المحاولة مرة أخرى.", + "You don't have permission to view this page.": "ليس لديك إذن لعرض هذه الصفحة.", + "Account Inactive": "الحساب غير نشط", + "Princess Nourah bint Abdulrahman University": "جامعة الأميرة نورة بنت عبدالرحمن", + "Manage your personal details and security.": "إدارة تفاصيلك الشخصية والأمان.", + "Primary": "أساسي", + "Verified": "موثق", + "Unverified": "غير موثق", + "Make Primary": "جعل أساسي", + "Remove": "إزالة", + "Add Email Address": "إضافة عنوان بريد إلكتروني", + "Hello,": "مرحباً،", + "Confirm My KAAUH ATS Email": "تأكيد بريدي الإلكتروني في نظام توظيف جامعة نورة", + "Alternatively, copy and paste this link into your browser:": "بدلاً من ذلك، انسخ والصق هذا الرابط في متصفحك:", + "Password Reset Request": "طلب إعادة تعيين كلمة المرور", + "Click Here to Reset Your Password": "اضغط هنا لإعادة تعيين كلمة المرور", + "This link is only valid for a limited time.": "هذا الرابط صالح لفترة محدودة فقط.", + "Thank you,": "شكراً لك،", + "KAAUH ATS Team": "فريق نظام توظيف جامعة نورة", + "Confirm Email Address": "تأكيد عنوان البريد الإلكتروني", + "Account Verification": "التحقق من الحساب", + "Verify your email to secure your account and unlock full features.": "تحقق من بريدك الإلكتروني لتأمين حسابك وإلغاء قفل جميع الميزات.", + "Confirm Your Email Address": "تأكيد عنوان بريدك الإلكتروني", + "Verification Failed": "فشل التحقق", + "The email confirmation link is expired or invalid.": "رابط تأكيد البريد الإلكتروني منتهي الصلاحية أو غير صالح.", + "Keep me signed in": "ابق مسجلاً للدخول", + "Return to Profile": "العودة إلى الملف الشخصي", + "Enter your e-mail address to reset your password.": "أدخل عنوان بريدك الإلكتروني لإعادة تعيين كلمة المرور.", + "Remember your password?": "تتذكر كلمة المرور؟", + "Log In": "تسجيل الدخول", + "Password Reset Sent": "تم إرسال إعادة تعيين كلمة المرور", + "Return to Login": "العودة إلى تسجيل الدخول", + "Please enter your new password below.": "يرجى إدخال كلمة المرور الجديدة أدناه.", + + # Common UI Elements + "Save": "حفظ", + "Cancel": "إلغاء", + "Delete": "حذف", + "Edit": "تحرير", + "View": "عرض", + "Create": "إنشاء", + "Update": "تحديث", + "Submit": "إرسال", + "Search": "بحث", + "Filter": "تصفية", + "Sort": "ترتيب", + "Export": "تصدير", + "Import": "استيراد", + "Download": "تنزيل", + "Upload": "رفع", + "Close": "إغلاق", + "Back": "رجوع", + "Next": "التالي", + "Previous": "السابق", + "First": "الأول", + "Last": "الأخير", + "Home": "الرئيسية", + "Dashboard": "لوحة التحكم", + "Profile": "الملف الشخصي", + "Settings": "الإعدادات", + "Help": "المساعدة", + "About": "حول", + "Contact": "اتصال", + "Logout": "تسجيل الخروج", + "Login": "تسجيل الدخول", + "Register": "التسجيل", + "Sign Up": "إنشاء حساب", + "Sign In": "تسجيل الدخول", + + # Status Messages + "Active": "نشط", + "Inactive": "غير نشط", + "Pending": "في الانتظار", + "Completed": "مكتمل", + "Failed": "فشل", + "Success": "نجح", + "Error": "خطأ", + "Warning": "تحذير", + "Info": "معلومات", + "Loading": "جاري التحميل", + "Processing": "جاري المعالجة", + "Ready": "جاهز", + "Not Ready": "غير جاهز", + "Available": "متاح", + "Unavailable": "غير متاح", + "Online": "متصل", + "Offline": "غير متصل", + "Connected": "متصل", + "Disconnected": "منقطع", + "Enabled": "مفعل", + "Disabled": "معطل", + "Required": "مطلوب", + "Optional": "اختياري", + "Yes": "نعم", + "No": "لا", + "True": "صحيح", + "False": "خطأ", + "On": "مفعل", + "Off": "معطل", + "Open": "مفتوح", + "Closed": "مغلق", + "Locked": "مقفل", + "Unlocked": "غير مقفل", + + # Form Fields + "Name": "الاسم", + "Email": "البريد الإلكتروني", + "Phone": "الهاتف", + "Address": "العنوان", + "City": "المدينة", + "Country": "البلد", + "State": "الولاية", + "Zip Code": "الرمز البريدي", + "Password": "كلمة المرور", + "Confirm Password": "تأكيد كلمة المرور", + "Username": "اسم المستخدم", + "First Name": "الاسم الأول", + "Last Name": "اسم العائلة", + "Full Name": "الاسم الكامل", + "Company": "الشركة", + "Position": "المنصب", + "Department": "القسم", + "Title": "العنوان", + "Description": "الوصف", + "Comments": "التعليقات", + "Notes": "ملاحظات", + "Date": "التاريخ", + "Time": "الوقت", + "Start Date": "تاريخ البدء", + "End Date": "تاريخ الانتهاء", + "Created": "تم الإنشاء", + "Modified": "تم التعديل", + "Updated": "تم التحديث", + + # Messages + "Please select an option": "يرجى اختيار خيار", + "This field is required": "هذا الحقل مطلوب", + "Invalid email address": "عنوان بريد إلكتروني غير صالح", + "Password must be at least 8 characters": "يجب أن تكون كلمة المرور 8 أحرف على الأقل", + "Passwords do not match": "كلمات المرور غير متطابقة", + "Email already exists": "البريد الإلكتروني موجود بالفعل", + "User not found": "المستخدم غير موجود", + "Invalid credentials": "بيانات الاعتماد غير صالحة", + "Access denied": "الوصول مرفوض", + "Permission denied": "الإذن مرفوض", + "Operation successful": "تمت العملية بنجاح", + "Operation failed": "فشلت العملية", + "Data saved successfully": "تم حفظ البيانات بنجاح", + "Data deleted successfully": "تم حذف البيانات بنجاح", + "Are you sure you want to delete this item?": "هل أنت متأكد من أنك تريد حذف هذا العنصر؟", + "This action cannot be undone": "لا يمكن التراجع عن هذا الإجراء", + + # Navigation + "Menu": "القائمة", + "Home": "الرئيسية", + "Dashboard": "لوحة التحكم", + "Profile": "الملف الشخصي", + "Settings": "الإعدادات", + "Admin": "المسؤول", + "Users": "المستخدمون", + "Reports": "التقارير", + "Analytics": "التحليلات", + "Messages": "الرسائل", + "Notifications": "الإشعارات", + "Tasks": "المهام", + "Calendar": "التقويم", + "Documents": "المستندات", + "Files": "الملفات", + "Media": "الوسائط", + "Help": "المساعدة", + "Support": "الدعم", + "FAQ": "الأسئلة الشائعة", + "Terms": "الشروط", + "Privacy": "الخصوصية", + "Legal": "قانوني", + + # Common Actions + "Add": "إضافة", + "Remove": "إزالة", + "Edit": "تحرير", + "Update": "تحديث", + "Delete": "حذف", + "View": "عرض", + "Show": "إظهار", + "Hide": "إخفاء", + "Enable": "تفعيل", + "Disable": "تعطيل", + "Activate": "تنشيط", + "Deactivate": "إلغاء تنشيط", + "Approve": "موافقة", + "Reject": "رفض", + "Accept": "قبول", + "Decline": "رفض", + "Send": "إرسال", + "Receive": "استلام", + "Download": "تنزيل", + "Upload": "رفع", + "Import": "استيراد", + "Export": "تصدير", + "Print": "طباعة", + "Copy": "نسخ", + "Move": "نقل", + "Rename": "إعادة تسمية", + "Share": "مشاركة", + "Subscribe": "اشتراك", + "Unsubscribe": "إلغاء الاشتراك", + "Follow": "متابعة", + "Unfollow": "إلغاء المتابعة", + "Like": "إعجاب", + "Unlike": "إلغاء الإعجاب", + "Comment": "تعليق", + "Rate": "تقييم", + "Review": "مراجعة", + "Bookmark": "إشارة مرجعية", + "Favorite": "مفضل", + "Archive": "أرشفة", + "Restore": "استعادة", + "Backup": "نسخ احتياطي", + "Recover": "استرداد", + "Reset": "إعادة تعيين", + "Refresh": "تحديث", + "Reload": "إعادة تحميل", + "Sync": "مزامنة", + "Connect": "اتصال", + "Disconnect": "قطع الاتصال", + "Link": "ربط", + "Unlink": "فك الربط", + "Attach": "إرفاق", + "Detach": "فصل", + "Merge": "دمج", + "Split": "تقسيم", + "Combine": "دمج", + "Separate": "فصل", + "Group": "تجميع", + "Ungroup": "فك التجميع", + "Sort": "ترتيب", + "Filter": "تصفية", + "Search": "بحث", + "Find": "بحث", + "Replace": "استبدال", + "Clear": "مسح", + "Clean": "تنظيف", + "Empty": "فارغ", + "Full": "ممتلئ", + "All": "الكل", + "None": "لا شيء", + "Some": "بعض", + "Any": "أي", + "Other": "آخر", + "More": "المزيد", + "Less": "أقل", + "New": "جديد", + "Old": "قديم", + "Recent": "الحديث", + "Latest": "الأحدث", + "Previous": "السابق", + "Next": "التالي", + "First": "الأول", + "Last": "الأخير", + "Current": "الحالي", + "Today": "اليوم", + "Yesterday": "أمس", + "Tomorrow": "غداً", + "This week": "هذا الأسبوع", + "Last week": "الأسبوع الماضي", + "Next week": "الأسبوع القادم", + "This month": "هذا الشهر", + "Last month": "الشهر الماضي", + "Next month": "الشهر القادم", + "This year": "هذا العام", + "Last year": "العام الماضي", + "Next year": "العام القادم", +} + +def translate_batch_file(batch_file_path): + """ + Translate a single batch file and return the translations + """ + translations = {} + + with open(batch_file_path, 'r', encoding='utf-8') as f: + content = f.read() + + lines = content.split('\n') + i = 0 + while i < len(lines): + line = lines[i].strip() + if line.startswith('msgid: "'): + # Extract msgid content, removing the extra quote at the beginning + msgid = line[8:-1] # Extract msgid content (skip the extra quote) + + # Skip empty msgid or already Arabic text + if not msgid or msgid.strip() == "" or is_arabic_text(msgid): + i += 1 + continue + + # Find translation + translation = TRANSLATIONS.get(msgid, "") + if translation: + translations[msgid] = translation + print(f"✓ Found translation: '{msgid}' -> '{translation}'") + else: + print(f"✗ No translation found: '{msgid}'") + i += 1 + + return translations + +def is_arabic_text(text): + """Check if text contains Arabic characters""" + arabic_chars = set('ابتثجحخدذرزسشصضطظعغفقكلمنهويءآأؤإئابةة') + return any(char in arabic_chars for char in text) + +def process_all_batches(): + """ + Process all batch files and create a comprehensive translation file + """ + all_translations = {} + + # Process batches 02-35 (batch 01 already done) + for batch_num in range(2, 36): + batch_file = f"translation_batch_{batch_num:02d}.txt" + if os.path.exists(batch_file): + print(f"\n=== Processing {batch_file} ===") + batch_translations = translate_batch_file(batch_file) + all_translations.update(batch_translations) + print(f"Found {len(batch_translations)} translations in {batch_file}") + else: + print(f"⚠️ {batch_file} not found") + + return all_translations + +def create_translation_script(all_translations): + """ + Create a script to apply all translations to the main django.po file + """ + script_content = '''#!/usr/bin/env python3 +""" +Script to apply all batch translations to the main django.po file +""" + +import re + +def apply_all_translations(): + """ + Apply all translations to the main django.po file + """ + # All translations from batches 02-35 + translations = { +''' + + for english, arabic in all_translations.items(): + script_content += f' "{english}": "{arabic}",\n' + + script_content += ''' } + + main_po_file = "locale/ar/LC_MESSAGES/django.po" + + # Read the main django.po file + with open(main_po_file, 'r', encoding='utf-8') as f: + main_content = f.read() + + # Apply translations to main file + updated_content = main_content + applied_count = 0 + + for english, arabic in translations.items(): + # Pattern to find msgid followed by empty msgstr + pattern = rf'(msgid "{re.escape(english)}"\\s*\\nmsgstr) ""' + replacement = rf'\\1 "{arabic}"' + + if re.search(pattern, updated_content): + updated_content = re.sub(pattern, replacement, updated_content) + applied_count += 1 + print(f"✓ Applied: '{english}' -> '{arabic}'") + else: + print(f"✗ Not found: '{english}'") + + # Write updated content back to main file + with open(main_po_file, 'w', encoding='utf-8') as f: + f.write(updated_content) + + print(f"\\nApplied {applied_count} translations to {main_po_file}") + return applied_count + +def main(): + """Main function to apply all translations""" + print("Applying all batch translations to main django.po file...") + applied_count = apply_all_translations() + + if applied_count > 0: + print(f"\\n✅ Successfully applied {applied_count} translations!") + print("Next steps:") + print("1. Run: python manage.py compilemessages") + print("2. Test the translations in the application") + else: + print("\\n❌ No translations were applied.") + +if __name__ == "__main__": + main() +''' + + with open("apply_all_translations.py", 'w', encoding='utf-8') as f: + f.write(script_content) + + print("Created apply_all_translations.py script") + +def main(): + """Main function to process all batches""" + print("🚀 Starting comprehensive translation process...") + + # Process all batch files + all_translations = process_all_batches() + + print(f"\n📊 Summary:") + print(f"Total translations found: {len(all_translations)}") + + if all_translations: + # Create the application script + create_translation_script(all_translations) + + print(f"\n✅ Translation processing complete!") + print(f"📝 Created apply_all_translations.py with {len(all_translations)} translations") + print(f"\n🎯 Next steps:") + print(f"1. Run: python apply_all_translations.py") + print(f"2. Run: python manage.py compilemessages") + print(f"3. Test the translations in the application") + else: + print("\n❌ No translations found to process.") + +if __name__ == "__main__": + main() diff --git a/translate_batch_01.py b/translate_batch_01.py new file mode 100644 index 0000000..aa7cf53 --- /dev/null +++ b/translate_batch_01.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +""" +Script to add Arabic translations for batch 01 +""" + +# Arabic translations for batch 01 +translations = { + "": "", # Line 7 - empty string, keep as is + "Website": "الموقع الإلكتروني", + "Admin Notes": "ملاحظات المسؤول", + "Save Assignment": "حفظ التكليف", + "Assignment": "التكليف", + "Expires At": "ينتهي في", + "Access Token": "رمز الوصول", + "Subject": "الموضوع", + "Recipients": "المستلمون", + "Internal staff involved in the recruitment process for this job": "الموظفون الداخليون المشاركون في عملية التوظيف لهذه الوظيفة", + "External Participant": "مشارك خارجي", + "External participants involved in the recruitment process for this job": "المشاركون الخارجيون المشاركون في عملية التوظيف لهذه الوظيفة", + "Reason for canceling the job posting": "سبب إلغاء نشر الوظيفة", + "Name of person who cancelled this job": "اسم الشخص الذي ألغى هذه الوظيفة", + "Hired": "تم التوظيف", + "Author": "المؤلف", + "Endpoint URL for sending candidate data (for outbound sync)": "عنوان URL لنقطة النهاية لإرسال بيانات المرشح (للمزامنة الصادرة)", + "HTTP method for outbound sync requests": "طريقة HTTP لطلبات المزامنة الصادرة", + "HTTP method for connection testing": "طريقة HTTP لاختبار الاتصال", + "Custom Headers": "رؤوس مخصصة", + "JSON object with custom HTTP headers for sync requests": "كائن JSON يحتوي على رؤوس HTTP مخصصة لطلبات المزامنة", + "Supports Outbound Sync": "يدعم المزامنة الصادرة", + "Whether this source supports receiving candidate data from ATS": "ما إذا كان هذا المصدر يدعم استقبال بيانات المرشح من نظام تتبع المتقدمين", + "Expired": "منتهي الصلاحية", + "Maximum candidates agency can submit for this job": "الحد الأقصى للمرشحين الذين يمكن للوكالة تقديمهم لهذه الوظيفة" +} + +def update_batch_file(): + """Update the batch file with Arabic translations""" + input_file = "translation_batch_01.txt" + output_file = "translation_batch_01_completed.txt" + + with open(input_file, 'r', encoding='utf-8') as f: + content = f.read() + + # Replace empty msgstr with translations + for english, arabic in translations.items(): + if english: # Skip empty string + # Find the pattern and replace + old_pattern = f'msgid: "{english}"\nmsgstr: ""\n\nArabic Translation: \nmsgstr: ""' + new_pattern = f'msgid: "{english}"\nmsgstr: ""\n\nArabic Translation: \nmsgstr: "{arabic}"' + content = content.replace(old_pattern, new_pattern) + + with open(output_file, 'w', encoding='utf-8') as f: + f.write(content) + + print(f"Updated batch file saved as: {output_file}") + print("Arabic translations added for batch 01") + +if __name__ == "__main__": + update_batch_file() diff --git a/translation_batch_01.txt b/translation_batch_01.txt new file mode 100644 index 0000000..7032a25 --- /dev/null +++ b/translation_batch_01.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 01 === +Translations 1-25 of 843 +============================================================ + +Line 7: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1041: +msgid: "Number of candidates submitted so far" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1052: +msgid: "Deadline for agency to submit candidates" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1068: +msgid: "Original deadline before extensions" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1078: +msgid: "Agency Job Assignment" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1082: +msgid: "Agency Job Assignments" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1086: +msgid: "Deadline date must be in the future" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1090: +msgid: "Maximum candidates must be greater than 0" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1094: +msgid: "Candidates submitted cannot exceed maximum candidates" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1098: +msgid: "Unique Token" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1108: +msgid: "Password for agency access" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1119: +msgid: "When this access link expires" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1124: +msgid: "Last Accessed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1128: +msgid: "Access Count" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1132: +msgid: "Agency Access Link" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1136: +msgid: "Agency Access Links" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1140: +msgid: "Expiration date must be in the future" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1190: +msgid: "In-App" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1194: +msgid: "Pending" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1198: +msgid: "Sent" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1202: +msgid: "Read" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1206: +msgid: "Retrying" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1210: +msgid: "Recipient" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1214: +msgid: "Notification Message" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1218: +msgid: "Notification Type" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_01_completed.txt b/translation_batch_01_completed.txt new file mode 100644 index 0000000..517cd48 --- /dev/null +++ b/translation_batch_01_completed.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 01 === +Translations 1-25 of 867 +============================================================ + +Line 7: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 320: +msgid: "Website" +msgstr: "" + +Arabic Translation: +msgstr: "الموقع الإلكتروني" +---------------------------------------- + +Line 406: +msgid: "Admin Notes" +msgstr: "" + +Arabic Translation: +msgstr: "ملاحظات المسؤول" +---------------------------------------- + +Line 410: +msgid: "Save Assignment" +msgstr: "" + +Arabic Translation: +msgstr: "حفظ التكليف" +---------------------------------------- + +Line 416: +msgid: "Assignment" +msgstr: "" + +Arabic Translation: +msgstr: "التكليف" +---------------------------------------- + +Line 422: +msgid: "Expires At" +msgstr: "" + +Arabic Translation: +msgstr: "ينتهي في" +---------------------------------------- + +Line 449: +msgid: "Access Token" +msgstr: "" + +Arabic Translation: +msgstr: "رمز الوصول" +---------------------------------------- + +Line 474: +msgid: "Subject" +msgstr: "" + +Arabic Translation: +msgstr: "الموضوع" +---------------------------------------- + +Line 485: +msgid: "Recipients" +msgstr: "" + +Arabic Translation: +msgstr: "المستلمون" +---------------------------------------- + +Line 525: +msgid: "Internal staff involved in the recruitment process for this job" +msgstr: "" + +Arabic Translation: +msgstr: "الموظفون الداخليون المشاركون في عملية التوظيف لهذه الوظيفة" +---------------------------------------- + +Line 529: +msgid: "External Participant" +msgstr: "" + +Arabic Translation: +msgstr: "مشارك خارجي" +---------------------------------------- + +Line 533: +msgid: "External participants involved in the recruitment process for this job" +msgstr: "" + +Arabic Translation: +msgstr: "المشاركون الخارجيون المشاركون في عملية التوظيف لهذه الوظيفة" +---------------------------------------- + +Line 541: +msgid: "Reason for canceling the job posting" +msgstr: "" + +Arabic Translation: +msgstr: "سبب إلغاء نشر الوظيفة" +---------------------------------------- + +Line 551: +msgid: "Name of person who cancelled this job" +msgstr: "" + +Arabic Translation: +msgstr: "اسم الشخص الذي ألغى هذه الوظيفة" +---------------------------------------- + +Line 595: +msgid: "Hired" +msgstr: "" + +Arabic Translation: +msgstr: "تم التوظيف" +---------------------------------------- + +Line 782: +msgid: "Author" +msgstr: "" + +Arabic Translation: +msgstr: "المؤلف" +---------------------------------------- + +Line 877: +msgid: "Endpoint URL for sending candidate data (for outbound sync)" +msgstr: "" + +Arabic Translation: +msgstr: "عنوان URL لنقطة النهاية لإرسال بيانات المرشح (للمزامنة الصادرة)" +---------------------------------------- + +Line 887: +msgid: "HTTP method for outbound sync requests" +msgstr: "" + +Arabic Translation: +msgstr: "طريقة HTTP لطلبات المزامنة الصادرة" +---------------------------------------- + +Line 897: +msgid: "HTTP method for connection testing" +msgstr: "" + +Arabic Translation: +msgstr: "طريقة HTTP لاختبار الاتصال" +---------------------------------------- + +Line 901: +msgid: "Custom Headers" +msgstr: "" + +Arabic Translation: +msgstr: "رؤوس مخصصة" +---------------------------------------- + +Line 905: +msgid: "JSON object with custom HTTP headers for sync requests" +msgstr: "" + +Arabic Translation: +msgstr: "كائن JSON يحتوي على رؤوس HTTP مخصصة لطلبات المزامنة" +---------------------------------------- + +Line 909: +msgid: "Supports Outbound Sync" +msgstr: "" + +Arabic Translation: +msgstr: "يدعم المزامنة الصادرة" +---------------------------------------- + +Line 913: +msgid: "Whether this source supports receiving candidate data from ATS" +msgstr: "" + +Arabic Translation: +msgstr: "ما إذا كان هذا المصدر يدعم استقبال بيانات المرشح من نظام تتبع المتقدمين" +---------------------------------------- + +Line 1026: +msgid: "Expired" +msgstr: "" + +Arabic Translation: +msgstr: "منتهي الصلاحية" +---------------------------------------- + +Line 1030: +msgid: "Maximum candidates agency can submit for this job" +msgstr: "" + +Arabic Translation: +msgstr: "الحد الأقصى للمرشحين الذين يمكن للوكالة تقديمهم لهذه الوظيفة" +---------------------------------------- + diff --git a/translation_batch_02.txt b/translation_batch_02.txt new file mode 100644 index 0000000..1e3053d --- /dev/null +++ b/translation_batch_02.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 02 === +Translations 26-50 of 843 +============================================================ + +Line 1234: +msgid: "The date and time this notification is scheduled to be sent." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1238: +msgid: "Send Attempts" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1275: +msgid: "Failed to start the job posting process. Please try again." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1291: +msgid: "Model Changes (CRUD)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1295: +msgid: "You don't have permission to view this page." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1300: +msgid: "Account Inactive" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1307: +msgid: "جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1314: +msgid: "ومستشفى الملك عبدالله بن عبدالعزيز التخصصي" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1321: +msgid: "Princess Nourah bint Abdulrahman University" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1334: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1339: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1367: +msgid: "Manage your personal details and security." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1399: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1405: +msgid: "Primary" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1409: +msgid: "Verified" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1413: +msgid: "Unverified" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1417: +msgid: "Make Primary" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1428: +msgid: "Remove" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1438: +msgid: "Add Email Address" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1451: +msgid: "Hello," +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1456: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1463: +msgid: "Confirm My KAAUH ATS Email" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1468: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1474: +msgid: "Alternatively, copy and paste this link into your browser:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1479: +msgid: "Password Reset Request" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_03.txt b/translation_batch_03.txt new file mode 100644 index 0000000..a40b9a1 --- /dev/null +++ b/translation_batch_03.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 03 === +Translations 51-75 of 843 +============================================================ + +Line 1484: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1491: +msgid: "Click Here to Reset Your Password" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1496: +msgid: "This link is only valid for a limited time." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1501: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1508: +msgid: "Thank you," +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1513: +msgid: "KAAUH ATS Team" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1518: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1524: +msgid: "Confirm Email Address" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1528: +msgid: "Account Verification" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1532: +msgid: "Verify your email to secure your account and unlock full features." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1536: +msgid: "Confirm Your Email Address" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1541: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1553: +msgid: "Verification Failed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1557: +msgid: "The email confirmation link is expired or invalid." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1561: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1603: +msgid: "Keep me signed in" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1649: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1654: +msgid: "Return to Profile" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1658: +msgid: "Enter your e-mail address to reset your password." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1674: +msgid: "Remember your password?" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1678: +msgid: "Log In" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1683: +msgid: "Password Reset Sent" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1695: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1701: +msgid: "Return to Login" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1712: +msgid: "Please enter your new password below." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_04.txt b/translation_batch_04.txt new file mode 100644 index 0000000..19660e1 --- /dev/null +++ b/translation_batch_04.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 04 === +Translations 76-100 of 843 +============================================================ + +Line 1716: +msgid: "You can then log in." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1720: +msgid: "Password Reset Failed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1724: +msgid: "The password reset link is invalid or has expired." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1728: +msgid: "Request New Reset Link" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1744: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1756: +msgid: "Verify Your Email Address" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1768: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1774: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1780: +msgid: "Change or Resend Email" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1789: +msgid: "Django site admin" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1799: +msgid: "KAAUH Agency Portal" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1807: +msgid: "kaauh logo green bg" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1827: +msgid: "Logout" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1941: +msgid: "Ready to Apply?" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1945: +msgid: "Review the job details, then apply below." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1950: +msgid: "Apply for this Position" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1969: +msgid: "Not specified" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 1992: +msgid: "JOB ID:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2062: +msgid: "Submission Metadata" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2066: +msgid: "Submission ID:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2077: +msgid: "Form:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2099: +msgid: "Field Property" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2133: +msgid: "Field Required" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2141: +msgid: "Yes" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2149: +msgid: "No" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_05.txt b/translation_batch_05.txt new file mode 100644 index 0000000..73aa83c --- /dev/null +++ b/translation_batch_05.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 05 === +Translations 101-125 of 843 +============================================================ + +Line 2153: +msgid: "No response fields were found for this submission." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2157: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2180: +msgid: "Submissions" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2184: +msgid: "All Submissions Table" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2188: +msgid: "All Submissions for" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2193: +msgid: "Submission ID" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2232: +msgid: "Page" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2238: +msgid: "of" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2250: +msgid: "There are no submissions for this form template yet." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2254: +msgid: "Submissions for" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2460: +msgid: "Careers" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2473: +msgid: "AI Score" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2484: +msgid: "Top Keywords" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2488: +msgid: "Experience" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2492: +msgid: "years" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2496: +msgid: "Recent Role:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2500: +msgid: "Skills" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2504: +msgid: "Soft Skills:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2508: +msgid: "Industry Match:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2513: +msgid: "Recommendation" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2518: +msgid: "Strengths" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2523: +msgid: "Weaknesses" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2528: +msgid: "Criteria Assessment" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2541: +msgid: "Met" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2547: +msgid: "Not Met" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_06.txt b/translation_batch_06.txt new file mode 100644 index 0000000..5491588 --- /dev/null +++ b/translation_batch_06.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 06 === +Translations 126-150 of 843 +============================================================ + +Line 2558: +msgid: "Screening Rating" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2563: +msgid: "Language Fluency" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2569: +msgid: "Success" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2574: +msgid: "Copied \"%(text)s\" to clipboard!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2584: +msgid: "System Audit Logs" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2588: +msgid: "Viewing Logs" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2592: +msgid: "Displaying" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2596: +msgid: "total records." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2614: +msgid: "User" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2618: +msgid: "Model" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2622: +msgid: "Object PK" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2648: +msgid: "Path" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2652: +msgid: "CREATE" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2656: +msgid: "UPDATE" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2660: +msgid: "DELETE" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2664: +msgid: "Login" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2674: +msgid: "No logs found for this section or the database is empty." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2685: +msgid: "Email will be sent to all selected recipients" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2700: +msgid: "Loading..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2704: +msgid: "Sending email..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2708: +msgid: "Sending..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2727: +msgid: "Meeting Details (will appear after scheduling):" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2743: +msgid: "Click here to join meeting" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2772: +msgid: "Processing..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2776: +msgid: "An unknown error occurred." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_07.txt b/translation_batch_07.txt new file mode 100644 index 0000000..35e4734 --- /dev/null +++ b/translation_batch_07.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 07 === +Translations 151-175 of 843 +============================================================ + +Line 2780: +msgid: "An error occurred while processing your request." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2788: +msgid: "Bulk Interview Scheduling" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2792: +msgid: "Configure time slots for:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2813: +msgid: "Candidates to Schedule (Hold Ctrl/Cmd to select multiple)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2868: +msgid: "Thank You!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2872: +msgid: "Your application has been submitted successfully" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2876: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2883: +msgid: "Return to Job Listings" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2887: +msgid: "Job ID#" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 2919: +msgid: "Link" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3005: +msgid: "Hashtags (For Promotion/Search on Linkedin)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3065: +msgid: "Search by name, email, phone, or stage..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3069: +msgid: "Filter Results" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3074: +msgid: "Clear Filters" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3167: +msgid: "JOB ID: " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3171: +msgid: "Share Public Link" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3177: +msgid: "Copied!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3215: +msgid: "Tracking" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3249: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3269: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3330: +msgid: "Candidate Categories & Scores" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3334: +msgid: "Key Performance Indicators" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3338: +msgid: "Avg. AI Score" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3343: +msgid: "High Potential" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3353: +msgid: "Avg. Exam Review" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_08.txt b/translation_batch_08.txt new file mode 100644 index 0000000..cdab7ba --- /dev/null +++ b/translation_batch_08.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 08 === +Translations 176-200 of 843 +============================================================ + +Line 3357: +msgid: "Vacancy Fill Rate" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3373: +msgid: "Status form not available. Please check your view." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3380: +msgid: "Save Changes" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3396: +msgid: "Search by Title or Department" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3425: +msgid: "Archived" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3440: +msgid: "Clear" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3450: +msgid: "Max Apps" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3481: +msgid: "All" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3486: +msgid: "Screened" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3496: +msgid: "Form" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3500: +msgid: "N/A" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3520: +msgid: "Create your first job posting to get started or adjust your filters." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3554: +msgid: "Search by Topic" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3596: +msgid: "Create your first meeting or adjust your filters." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3641: +msgid: "minutes" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3655: +msgid: "Assigned Participants" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3665: +msgid: "External Participants" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3669: +msgid: "System User" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3673: +msgid: "Comments" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3696: +msgid: "No comments yet. Be the first to comment!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3712: +msgid: "You must be logged in to add a comment." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3730: +msgid: "You are updating the existing meeting schedule." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3748: +msgid: "Candidate has upcoming interviews. Updating existing schedule." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3752: +msgid: "e.g., Technical Screening, HR Interview" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3762: +msgid: "Save" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_09.txt b/translation_batch_09.txt new file mode 100644 index 0000000..52a9db0 --- /dev/null +++ b/translation_batch_09.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 09 === +Translations 201-225 of 843 +============================================================ + +Line 3858: +msgid: "This participant is not currently assigned to any job." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3862: +msgid: "Metadata" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3873: +msgid: "at" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3877: +msgid: "Total Assigned Jobs" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3896: +msgid: "This action cannot be undone." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3913: +msgid: "Search by Name or Email" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3917: +msgid: "Filter by Assigned Job" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3935: +msgid: "Create your first participant record or adjust your filters." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3952: +msgid: "Secure access link for agency candidate submissions" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3984: +msgid: "Access Credentials" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 3996: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4002: +msgid: "Usage Statistics" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4006: +msgid: "Total Accesses" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4010: +msgid: "Never" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4014: +msgid: "View Assignment" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4032: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4045: +msgid: "Generate a secure access link for agency to submit candidates" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4057: +msgid: "Select the agency job assignment" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4061: +msgid: "When will this access link expire?" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4065: +msgid: "Max Submissions" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4069: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4074: +msgid: "Whether this access link is currently active" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4078: +msgid: "Notes" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4088: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4094: +msgid: "Assignment Details and Management" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_10.txt b/translation_batch_10.txt new file mode 100644 index 0000000..3b3b778 --- /dev/null +++ b/translation_batch_10.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 10 === +Translations 226-250 of 843 +============================================================ + +Line 4099: +msgid: "Edit Assignment" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4166: +msgid: "Submission Progress" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4194: +msgid: "Recent Messages" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4201: +msgid: "From" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4207: +msgid: "New" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4218: +msgid: "Extend Assignment Deadline" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4234: +msgid: "Token copied to clipboard!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4238: +msgid: "Assign a job to an external hiring agency" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4242: +msgid: "Maximum number of candidates the agency can submit" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4246: +msgid: "Date and time when submission period ends" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4263: +msgid: "Total Assignments:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4267: +msgid: "New Assignment" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4271: +msgid: "Search by agency or job title..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4281: +msgid: "Assignments pagination" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4291: +msgid: "Create your first agency assignment to get started." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4295: +msgid: "Create Assignment" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4306: +msgid: "You are about to delete a hiring agency. This action cannot be undone." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4316: +msgid: "Warning: This action cannot be undone!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4320: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4326: +msgid: "Agency to be Deleted" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4336: +msgid: "candidate(s) are associated with this agency." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4340: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4346: +msgid: "What will happen when you delete this agency?" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4350: +msgid: "The agency profile and all its information will be permanently deleted" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4360: +msgid: "Associated candidates will lose their agency reference" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_11.txt b/translation_batch_11.txt new file mode 100644 index 0000000..09f595b --- /dev/null +++ b/translation_batch_11.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 11 === +Translations 251-275 of 843 +============================================================ + +Line 4364: +msgid: "Historical data linking candidates to this agency will be lost" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4368: +msgid: "This action cannot be undone under any circumstances" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4372: +msgid: "Type the agency name to confirm deletion:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4376: +msgid: "This is required to prevent accidental deletions." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4380: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4386: +msgid: "Delete Agency Permanently" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4404: +msgid: "Hiring Agency Details and Candidate Management" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4408: +msgid: "Assign job" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4467: +msgid: "This agency hasn't submitted any candidates yet." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4477: +msgid: "Total" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4487: +msgid: "Visit Website" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4509: +msgid: "Update the hiring agency information below." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4513: +msgid: "Fill in the details to add a new hiring agency." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4517: +msgid: "Please correct the errors below:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4531: +msgid: "Quick Tips" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4535: +msgid: "Provide accurate contact information for better communication" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4539: +msgid: "Include a valid website URL if available" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4543: +msgid: "Add a detailed description to help identify the agency" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4547: +msgid: "All fields marked with * are required" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4575: +msgid: "Search by name, contact person, email, or country..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4579: +msgid: "Agency pagination" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4589: +msgid: "No hiring agencies have been added yet." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4593: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4644: +msgid: "Submit candidates using the form above to get started." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4662: +msgid: "Assignment Info" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_12.txt b/translation_batch_12.txt new file mode 100644 index 0000000..118b18b --- /dev/null +++ b/translation_batch_12.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 12 === +Translations 276-300 of 843 +============================================================ + +Line 4666: +msgid: "Days Remaining" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4671: +msgid: "days" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4675: +msgid: "Submission Rate" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4679: +msgid: "Send Message to Admin" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4683: +msgid: "Priority" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4687: +msgid: "Low" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4691: +msgid: "Medium" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4695: +msgid: "High" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4718: +msgid: "Error loading candidate data. Please try again." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4723: +msgid: "Error updating candidate. Please try again." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4728: +msgid: "Error removing candidate. Please try again." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4739: +msgid: "Welcome back" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4743: +msgid: "Total Assignments" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4747: +msgid: "Active Assignments" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4764: +msgid: "Your Job Assignments" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4768: +msgid: "assignments" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4772: +msgid: "days left" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4776: +msgid: "days overdue" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4780: +msgid: "Submissions Closed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4784: +msgid: "No Job Assignments Found" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4788: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4794: +msgid: "Agency Portal Login" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4804: +msgid: "Enter the access token provided by the hiring organization" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4808: +msgid: "Enter the password for this access token" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4812: +msgid: "Access Portal" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_13.txt b/translation_batch_13.txt new file mode 100644 index 0000000..cdde206 --- /dev/null +++ b/translation_batch_13.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 13 === +Translations 301-325 of 843 +============================================================ + +Line 4816: +msgid: "Need Help?" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4826: +msgid: "Reach out to your hiring contact" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4836: +msgid: "View user guides and tutorials" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4840: +msgid: "Security Notice" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4844: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4850: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4856: +msgid: "Please enter your access token." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4860: +msgid: "Please enter your password." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4876: +msgid: "Days Remaining:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4927: +msgid: "Click to upload or drag and drop" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4931: +msgid: "Accepted formats: PDF, DOC, DOCX (Maximum 5MB)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4935: +msgid: "Remove File" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4939: +msgid: "Additional Notes" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4943: +msgid: "Notes (Optional)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4947: +msgid: "Any additional information about the candidate" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4951: +msgid: "Submitted candidates will be reviewed by the hiring team." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4961: +msgid: "This assignment has expired. Submissions are no longer accepted." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4965: +msgid: "Maximum candidate limit reached for this assignment." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4969: +msgid: "This assignment is not currently active." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4973: +msgid: "Submitting candidate..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4977: +msgid: "Please wait while we process your submission." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4981: +msgid: "Please upload a PDF, DOC, or DOCX file." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 4985: +msgid: "File size must be less than 5MB." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5001: +msgid: "Error submitting candidate. Please try again." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5005: +msgid: "Network error. Please check your connection and try again." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_14.txt b/translation_batch_14.txt new file mode 100644 index 0000000..427a618 --- /dev/null +++ b/translation_batch_14.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 14 === +Translations 326-350 of 843 +============================================================ + +Line 5041: +msgid: "Journey Timeline" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5087: +msgid: "AI Analysis Report" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5091: +msgid: "Match Score" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5095: +msgid: "Category" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5099: +msgid: "Job Fit Narrative" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5109: +msgid: "Years of Experience:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5113: +msgid: "Most Recent Job Title:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5117: +msgid: "Experience Industry Match:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5121: +msgid: "Soft Skills Score:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5131: +msgid: "Minimum Requirements Met:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5135: +msgid: "Screening Stage Rating:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5139: +msgid: "Resume is being parsed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5143: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5175: +msgid: "View Resume AI Overview" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5179: +msgid: "Time to Hire: " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5183: +msgid: "Unable to Parse Resume , click to retry" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5193: +msgid: "Candidates in Exam Stage:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5197: +msgid: "Export exam candidates to CSV" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5205: +msgid: "Export CSV" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5222: +msgid: "Screening Stage" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5232: +msgid: "No candidates are currently in the Exam stage for this job." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5236: +msgid: "Candidate Details & Exam Update" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5253: +msgid: "Successfully Hired:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5257: +msgid: "Sync hired candidates to external sources" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5268: +msgid: "Export hired candidates to CSV" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_15.txt b/translation_batch_15.txt new file mode 100644 index 0000000..503125f --- /dev/null +++ b/translation_batch_15.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 15 === +Translations 351-375 of 843 +============================================================ + +Line 5272: +msgid: "Congratulations!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5276: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5303: +msgid: "Loading content..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5313: +msgid: "Syncing candidates..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5317: +msgid: "Syncing hired candidates..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5321: +msgid: "Please wait while we sync candidates to external sources." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5325: +msgid: "Syncing..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5329: +msgid: "An unexpected error occurred during sync." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5345: +msgid: "Successful:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5387: +msgid: "Sync task failed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5391: +msgid: "Failed to check sync status" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5395: +msgid: "Sync timed out after 5 minutes" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5399: +msgid: "Sync in progress..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5415: +msgid: "Candidates in Interview Stage:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5419: +msgid: "Export interview candidates to CSV" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5465: +msgid: "Minutes" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5469: +msgid: "No candidates are currently in the Interview stage for this job." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5475: +msgid: "Candidate Details / Bulk Action Form" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5479: +msgid: "Manage all participants" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5484: +msgid: "Users" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5488: +msgid: "Loading email form..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5500: +msgid: "Filter by Job" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5504: +msgid: "Major" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5538: +msgid: "Candidates in Offer Stage:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5542: +msgid: "Export offer candidates to CSV" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_16.txt b/translation_batch_16.txt new file mode 100644 index 0000000..7841ba3 --- /dev/null +++ b/translation_batch_16.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 16 === +Translations 376-400 of 843 +============================================================ + +Line 5546: +msgid: "To Hired" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5556: +msgid: "No candidates are currently in the Offer stage for this job." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5566: +msgid: "Job:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5570: +msgid: "Export screening candidates to CSV" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5574: +msgid: "AI Scoring & Top Candidate Filter" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5578: +msgid: "Min AI Score" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5582: +msgid: "Min Years Exp" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5586: +msgid: "Any Rating" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5614: +msgid: "Is Qualified?" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5618: +msgid: "Professional Category" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5622: +msgid: "Top 3 Skills" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5626: +msgid: "AI scoring.." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5630: +msgid: "No candidates match the current stage and filter criteria." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5666: +msgid: "Recruitment Analytics" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5670: +msgid: "Data Scope: " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5674: +msgid: "Data Scope: All Jobs" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5684: +msgid: "All Jobs (Default View)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5688: +msgid: "Daily Candidate Applications Trend" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5698: +msgid: "Pipeline Funnel: " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5702: +msgid: "Total Pipeline Funnel (All Jobs)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5706: +msgid: "Time-to-Hire Target Check" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5716: +msgid: "Top 5 Most Applied Jobs" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5738: +msgid: "Daily Applications (Last 30 Days)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5754: +msgid: "Mark All as Read" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5758: +msgid: "What this will do" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_17.txt b/translation_batch_17.txt new file mode 100644 index 0000000..b199877 --- /dev/null +++ b/translation_batch_17.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 17 === +Translations 401-425 of 843 +============================================================ + +Line 5781: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5787: +msgid: "All caught up!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5791: +msgid: "You don't have any unread notifications to mark as read." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5795: +msgid: "Yes, Mark All as Read" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5812: +msgid: "Notification Preview" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5829: +msgid: "View notification details and manage your preferences" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5834: +msgid: "Mark as Read" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5839: +msgid: "Mark as Unread" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5867: +msgid: "Delivery Attempts" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5919: +msgid: "Mark All Read" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5930: +msgid: "Unread" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5934: +msgid: "All Types" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5938: +msgid: "Filter" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5942: +msgid: "Total Notifications" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5946: +msgid: "Email Notifications" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5956: +msgid: "Mark as read" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5960: +msgid: "Mark as unread" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5970: +msgid: "Notifications pagination" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5980: +msgid: "Try adjusting your filters to see more notifications." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5990: +msgid: "Name / Contact" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 5994: +msgid: "View Details and Score Breakdown" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6004: +msgid: "Move to Next Stage" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6008: +msgid: "Move to" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6024: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6031: +msgid: "Days" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_18.txt b/translation_batch_18.txt new file mode 100644 index 0000000..defcbca --- /dev/null +++ b/translation_batch_18.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 18 === +Translations 426-450 of 843 +============================================================ + +Line 6035: +msgid: "Target:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6039: +msgid: "Max Scale:" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6049: +msgid: "Home" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6065: +msgid: "All Active & Drafted Positions (Global)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6073: +msgid: "Currently Open Requisitions (Scoped)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6077: +msgid: "Total Profiles in Current Scope" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6081: +msgid: "Total Slots to be Filled (Scoped)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6091: +msgid: "Total Recruiters/Interviewers (Global)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6101: +msgid: "Total Job Posts Sent to LinkedIn (Global)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6105: +msgid: "New Apps (7 Days)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6109: +msgid: "Incoming applications last week" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6113: +msgid: "Avg. Apps per Job" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6117: +msgid: "Average Applications per Job (Scoped)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6127: +msgid: "Avg. Days (Application to Hired)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6131: +msgid: "Avg. Match Score" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6135: +msgid: "Average AI Score (Current Scope)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6140: +msgid: "Score ≥ 75%% Profiles" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6150: +msgid: "Scheduled Interviews (Current Week)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6154: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6166: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6172: +msgid: "Please select a date and time for the interview." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6193: +msgid: "Search by Title or Creator" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6253: +msgid: "Admin Settings Dashboard" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6257: +msgid: "Staff User List" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6273: +msgid: "Last Login" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_19.txt b/translation_batch_19.txt new file mode 100644 index 0000000..01b2cbc --- /dev/null +++ b/translation_batch_19.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 19 === +Translations 451-475 of 843 +============================================================ + +Line 6277: +msgid: "Deactivate User" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6318: +msgid: "Manage email addresses" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6322: +msgid: "Security" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6338: +msgid: "Username" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6342: +msgid: "Date Joined" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6347: +msgid: "{editor}: Editing failed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6352: +msgid: "{editor}: Editing failed: {e}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6358: +msgid: "{text} {deprecated_message}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6381: +msgid: "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6386: +msgid: "Aborted!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6391: +msgid: "Commands" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6395: +msgid: "Missing command." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6399: +msgid: "No such command {name!r}." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6403: +msgid: "Value must be an iterable." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6418: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6425: +msgid: "env var: {var}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6432: +msgid: "default: {default}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6437: +msgid: "(dynamic)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6442: +msgid: "%(prog)s, version %(version)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6446: +msgid: "Show the version and exit." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6450: +msgid: "Show this message and exit." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6462: +msgid: "Try '{command} {option}' for help." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6467: +msgid: "Invalid value: {message}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6472: +msgid: "Invalid value for {param_hint}: {message}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6476: +msgid: "Missing argument" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_20.txt b/translation_batch_20.txt new file mode 100644 index 0000000..6739771 --- /dev/null +++ b/translation_batch_20.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 20 === +Translations 476-500 of 843 +============================================================ + +Line 6486: +msgid: "Missing parameter" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6491: +msgid: "Missing {param_type}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6496: +msgid: "Missing parameter: {param_name}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6501: +msgid: "No such option: {name}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6516: +msgid: "unknown error" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6520: +msgid: "Could not open file {filename!r}: {message}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6530: +msgid: "Argument {name!r} takes {nargs} values." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6534: +msgid: "Option {name!r} does not take a value." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6548: +msgid: "Shell completion is not supported for Bash versions older than 4.4." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6552: +msgid: "Couldn't detect Bash version, shell completion is not supported." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6562: +msgid: "Error: The value you entered was invalid." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6572: +msgid: "Error: The two entered values do not match." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6576: +msgid: "Error: invalid input" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6580: +msgid: "Press any key to continue..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6585: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6611: +msgid: "{value!r} is not a valid {number_type}." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6616: +msgid: "{value} is not in the range {range}." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6620: +msgid: "{value!r} is not a valid boolean. Recognized values: {states}" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6624: +msgid: "{value!r} is not a valid UUID." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6634: +msgid: "directory" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6638: +msgid: "path" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6642: +msgid: "{name} {filename!r} does not exist." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6646: +msgid: "{name} {filename!r} is a file." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6650: +msgid: "{name} {filename!r} is a directory." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6654: +msgid: "{name} {filename!r} is not readable." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_21.txt b/translation_batch_21.txt new file mode 100644 index 0000000..913f353 --- /dev/null +++ b/translation_batch_21.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 21 === +Translations 501-525 of 843 +============================================================ + +Line 6658: +msgid: "{name} {filename!r} is not writable." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6662: +msgid: "{name} {filename!r} is not executable." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6677: +msgid: "RoW" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6682: +msgid: "GLO" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6686: +msgid: "RoE" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6696: +msgid: "Site Maps" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6700: +msgid: "Static Files" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6712: +msgid: "…" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6716: +msgid: "That page number is not an integer" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6720: +msgid: "That page number is less than 1" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6724: +msgid: "That page contains no results" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6728: +msgid: "Enter a valid value." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6739: +msgid: "Enter a valid URL." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6743: +msgid: "Enter a valid integer." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6747: +msgid: "Enter a valid email address." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6752: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6757: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6767: +msgid: "Enter a valid %(protocol)s address." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6771: +msgid: "IPv4" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6776: +msgid: "IPv6" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6780: +msgid: "IPv4 or IPv6" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6784: +msgid: "Enter only digits separated by commas." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6789: +msgid: "Ensure this value is %(limit_value)s (it is %(show_value)s)." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6794: +msgid: "Ensure this value is less than or equal to %(limit_value)s." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6799: +msgid: "Ensure this value is greater than or equal to %(limit_value)s." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_22.txt b/translation_batch_22.txt new file mode 100644 index 0000000..5d17700 --- /dev/null +++ b/translation_batch_22.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 22 === +Translations 526-550 of 843 +============================================================ + +Line 6804: +msgid: "Ensure this value is a multiple of step size %(limit_value)s." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6809: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6889: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6895: +msgid: "Null characters are not allowed." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6901: +msgid: "and" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6906: +msgid: "%(model_name)s with this %(field_labels)s already exists." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6911: +msgid: "Constraint “%(name)s” is violated." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6916: +msgid: "Value %(value)r is not a valid choice." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6920: +msgid: "This field cannot be null." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6924: +msgid: "This field cannot be blank." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6929: +msgid: "%(model_name)s with this %(field_label)s already exists." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6936: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6942: +msgid: "Field of type: %(field_type)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6947: +msgid: "“%(value)s” value must be either True or False." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6952: +msgid: "“%(value)s” value must be either True, False, or None." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6956: +msgid: "Boolean (Either True or False)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6961: +msgid: "String (up to %(max_length)s)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6965: +msgid: "String (unlimited)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6976: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6984: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6990: +msgid: "Date (without time)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 6995: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7002: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7008: +msgid: "Date (with time)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7013: +msgid: "“%(value)s” value must be a decimal number." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_23.txt b/translation_batch_23.txt new file mode 100644 index 0000000..b7e9d95 --- /dev/null +++ b/translation_batch_23.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 23 === +Translations 551-575 of 843 +============================================================ + +Line 7017: +msgid: "Decimal number" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7022: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7041: +msgid: "“%(value)s” value must be a float." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7052: +msgid: "“%(value)s” value must be an integer." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7062: +msgid: "Big (8 byte) integer" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7066: +msgid: "Small integer" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7084: +msgid: "“%(value)s” value must be either None, True or False." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7088: +msgid: "Boolean (Either True, False or None)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7092: +msgid: "Positive big integer" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7096: +msgid: "Positive integer" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7100: +msgid: "Positive small integer" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7105: +msgid: "Slug (up to %(max_length)s)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7109: +msgid: "Text" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7114: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7121: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7137: +msgid: "URL" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7141: +msgid: "Raw binary data" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7146: +msgid: "“%(value)s” is not a valid UUID." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7150: +msgid: "Universally unique identifier" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7154: +msgid: "Image" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7158: +msgid: "A JSON object" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7162: +msgid: "Value must be valid JSON." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7167: +msgid: "%(model)s instance with %(field)s %(value)r is not a valid choice." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7171: +msgid: "Foreign Key (type determined by related field)" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7175: +msgid: "One-to-one relationship" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_24.txt b/translation_batch_24.txt new file mode 100644 index 0000000..3d7a5b4 --- /dev/null +++ b/translation_batch_24.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 24 === +Translations 576-600 of 843 +============================================================ + +Line 7180: +msgid: "%(from)s-%(to)s relationship" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7185: +msgid: "%(from)s-%(to)s relationships" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7189: +msgid: "Many-to-many relationship" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7195: +msgid: ":?.!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7199: +msgid: "This field is required." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7223: +msgid: "Enter a valid date/time." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7234: +msgid: "The number of days must be between {min_days} and {max_days}." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7238: +msgid: "No file was submitted. Check the encoding type on the form." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7242: +msgid: "No file was submitted." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7246: +msgid: "The submitted file is empty." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7262: +msgid: "Please either submit a file or check the clear checkbox, not both." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7266: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7275: +msgid: "Select a valid choice. %(value)s is not one of the available choices." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7293: +msgid: "Enter a valid UUID." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7297: +msgid: "Enter a valid JSON." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7302: +msgid: ":" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7307: +msgid: "(Hidden field %(name)s) %(error)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7312: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7341: +msgid: "Order" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7346: +msgid: "Please correct the duplicate data for %(field)s." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7351: +msgid: "Please correct the duplicate data for %(field)s, which must be unique." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7356: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7362: +msgid: "Please correct the duplicate values below." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7366: +msgid: "The inline value did not match the parent instance." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7370: +msgid: "Select a valid choice. That choice is not one of the available choices." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_25.txt b/translation_batch_25.txt new file mode 100644 index 0000000..a155a8d --- /dev/null +++ b/translation_batch_25.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 25 === +Translations 601-625 of 843 +============================================================ + +Line 7375: +msgid: "“%(pk)s” is not a valid value." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7380: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7386: +msgid: "Currently" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7400: +msgid: "Unknown" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7405: +msgid: "yes,no,maybe" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7422: +msgid: "%s KB" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7427: +msgid: "%s MB" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7432: +msgid: "%s GB" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7437: +msgid: "%s TB" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7442: +msgid: "%s PB" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7446: +msgid: "p.m." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7450: +msgid: "a.m." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7454: +msgid: "PM" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7458: +msgid: "AM" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7462: +msgid: "midnight" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7466: +msgid: "noon" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7470: +msgid: "Monday" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7474: +msgid: "Tuesday" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7478: +msgid: "Wednesday" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7482: +msgid: "Thursday" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7486: +msgid: "Friday" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7490: +msgid: "Saturday" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7494: +msgid: "Sunday" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7498: +msgid: "Mon" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7502: +msgid: "Tue" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_26.txt b/translation_batch_26.txt new file mode 100644 index 0000000..9bfcfcd --- /dev/null +++ b/translation_batch_26.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 26 === +Translations 626-650 of 843 +============================================================ + +Line 7506: +msgid: "Wed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7510: +msgid: "Thu" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7514: +msgid: "Fri" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7524: +msgid: "Sun" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7528: +msgid: "January" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7532: +msgid: "February" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7542: +msgid: "April" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7546: +msgid: "May" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7550: +msgid: "June" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7554: +msgid: "July" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7558: +msgid: "August" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7562: +msgid: "September" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7566: +msgid: "October" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7570: +msgid: "November" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7574: +msgid: "December" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7578: +msgid: "jan" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7582: +msgid: "feb" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7592: +msgid: "apr" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7602: +msgid: "jun" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7606: +msgid: "jul" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7616: +msgid: "sep" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7620: +msgid: "oct" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7624: +msgid: "nov" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7628: +msgid: "dec" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7633: +msgid: "Jan." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_27.txt b/translation_batch_27.txt new file mode 100644 index 0000000..f511b02 --- /dev/null +++ b/translation_batch_27.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 27 === +Translations 651-675 of 843 +============================================================ + +Line 7638: +msgid: "Feb." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7650: +msgid: "April" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7655: +msgid: "May" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7660: +msgid: "June" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7665: +msgid: "July" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7670: +msgid: "Aug." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7675: +msgid: "Sept." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7680: +msgid: "Oct." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7685: +msgid: "Nov." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7690: +msgid: "Dec." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7695: +msgid: "January" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7700: +msgid: "February" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7712: +msgid: "April" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7717: +msgid: "May" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7722: +msgid: "June" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7727: +msgid: "July" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7732: +msgid: "August" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7737: +msgid: "September" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7742: +msgid: "October" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7747: +msgid: "November" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7752: +msgid: "December" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7756: +msgid: "This is not a valid IPv6 address." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7762: +msgid: "%(truncated_text)s…" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7774: +msgid: ", " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7844: +msgid: "Forbidden" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_28.txt b/translation_batch_28.txt new file mode 100644 index 0000000..bb31ea4 --- /dev/null +++ b/translation_batch_28.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 28 === +Translations 676-700 of 843 +============================================================ + +Line 7848: +msgid: "CSRF verification failed. Request aborted." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7860: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7876: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7883: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7889: +msgid: "More information is available with DEBUG=True." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7893: +msgid: "No year specified" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7899: +msgid: "Date out of range" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7903: +msgid: "No month specified" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7907: +msgid: "No day specified" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7911: +msgid: "No week specified" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7917: +msgid: "No %(verbose_name_plural)s available" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7922: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7929: +msgid: "Invalid date string “%(datestr)s” given format “%(format)s”" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7934: +msgid: "No %(verbose_name)s found matching the query" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7938: +msgid: "Page is not “last”, nor can it be converted to an int." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7943: +msgid: "Invalid page (%(page_number)s): %(message)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7948: +msgid: "Empty list and “%(class_name)s.allow_empty” is False." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7952: +msgid: "Directory indexes are not allowed here." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7957: +msgid: "“%(path)s” does not exist" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7964: +msgid: "Index of %(directory)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7969: +msgid: "The install worked successfully! Congratulations!" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7974: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7989: +msgid: "Django Documentation" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7993: +msgid: "Topics, references, & how-to’s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 7997: +msgid: "Tutorial: A Polling App" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_29.txt b/translation_batch_29.txt new file mode 100644 index 0000000..4b8349a --- /dev/null +++ b/translation_batch_29.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 29 === +Translations 701-725 of 843 +============================================================ + +Line 8001: +msgid: "Get started with Django" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8005: +msgid: "Django Community" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8009: +msgid: "Connect, get help, or contribute" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8013: +msgid: "You do not have permission to upload files." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8017: +msgid: "You must be logged in to upload files." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8022: +msgid: "File should be at most %(max_size)s MB." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8026: +msgid: "Invalid form data" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8030: +msgid: "Check the correct settings.CKEDITOR_5_CONFIGS " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8034: +msgid: "Only POST method is allowed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8038: +msgid: "Attachment module is disabled" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8042: +msgid: "Only authenticated users are allowed" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8046: +msgid: "No files were requested" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8050: +msgid: "File size exceeds the limit allowed and cannot be saved" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8054: +msgid: "Failed to save attachment" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8059: +msgid: "Attempting to connect to qpid with SASL mechanism %s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8064: +msgid: "Connected to qpid with SASL mechanism %s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8069: +msgid: "Unable to connect to qpid with SASL mechanism %s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8074: +msgid: "required" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8079: +msgid: "Arguments" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8089: +msgid: "[default: {}]" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8093: +msgid: "[env var: {}]" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8097: +msgid: "[required]" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8101: +msgid: "Aborted." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8106: +msgid: "Try [blue]'{command_path} {help_option}'[/] for help." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8123: +msgid: "Collapse" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_30.txt b/translation_batch_30.txt new file mode 100644 index 0000000..7a17878 --- /dev/null +++ b/translation_batch_30.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 30 === +Translations 726-750 of 843 +============================================================ + +Line 8128: +msgid: "Value" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8132: +msgid: "Default" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8137: +msgid: "Code" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8141: +msgid: "Modified" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8145: +msgid: "Reset to default" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8166: +msgid: " By %(filter_title)s " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8171: +msgid: "To" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8175: +msgid: "Date from" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8179: +msgid: "Date to" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8195: +msgid: "Paragraph" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8199: +msgid: "Underlined" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8203: +msgid: "Bold" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8207: +msgid: "Italic" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8211: +msgid: "Strike" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8218: +msgid: "Heading" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8222: +msgid: "Quote" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8226: +msgid: "Unordered list" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8230: +msgid: "Ordered list" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8234: +msgid: "Indent increase" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8238: +msgid: "Indent decrease" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8242: +msgid: "Undo" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8246: +msgid: "Redo" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8256: +msgid: "Unlink" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8260: +msgid: "Object permissions" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8267: +msgid: "Object" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_31.txt b/translation_batch_31.txt new file mode 100644 index 0000000..2237175 --- /dev/null +++ b/translation_batch_31.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 31 === +Translations 751-775 of 843 +============================================================ + +Line 8272: +msgid: "Group" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8276: +msgid: "Group permissions" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8286: +msgid: "User permissions" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8297: +msgid: "Export" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8301: +msgid: "Import" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8322: +msgid: "This exporter will export the following fields" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8326: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8344: +msgid: "Skipped" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8348: +msgid: "Some rows failed to validate" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8352: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8359: +msgid: "Row" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8370: +msgid: "Non field specific" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8374: +msgid: "This exporter will export the following fields: " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8378: +msgid: "This importer will import the following fields: " +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8385: +msgid: "%(class_name)s %(instance)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8390: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8396: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8415: +msgid: "This object doesn't have a change history." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8419: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8424: +msgid: "Press the 'Change History' button below to edit the history." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8431: +msgid: "Date/time" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8449: +msgid: "None" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8453: +msgid: "Revert" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8469: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8483: +msgid: "Run the selected action" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_32.txt b/translation_batch_32.txt new file mode 100644 index 0000000..5fd9a66 --- /dev/null +++ b/translation_batch_32.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 32 === +Translations 776-800 of 843 +============================================================ + +Line 8487: +msgid: "Run" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8491: +msgid: "Click here to select the objects across all pages" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8496: +msgid: "Select all %(total_count)s %(module_name)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8500: +msgid: "Clear selection" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8506: +msgid: "Models in the %(name)s application" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8512: +msgid: "You don’t have permission to view or edit anything." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8516: +msgid: "After you've created a user, you’ll be able to edit more user options." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8521: +msgid: "Enter a new password for the user %(username)s." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8533: +msgid: "History" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8545: +msgid: "Filters" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8549: +msgid: "Select all rows" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8559: +msgid: "Remove from sorting" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8564: +msgid: "Sorting priority: %(priority_number)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8569: +msgid: "Expand row" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8574: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8582: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8589: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8596: +msgid: "Objects" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8601: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8609: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8616: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8626: +msgid: "Welcome back to" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8631: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8637: +msgid: "Log in" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8641: +msgid: "Forgotten your password or username?" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_33.txt b/translation_batch_33.txt new file mode 100644 index 0000000..dbab128 --- /dev/null +++ b/translation_batch_33.txt @@ -0,0 +1,204 @@ +=== TRANSLATION BATCH 33 === +Translations 801-825 of 843 +============================================================ + +Line 8645: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8653: +msgid: "Show all" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8658: +msgid: "Type to search" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8662: +msgid: "Save and continue editing" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8666: +msgid: "Save and view" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8670: +msgid: "Save and add another" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8674: +msgid: "Save as new" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8678: +msgid: "You have been successfully logged out from the administration" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8682: +msgid: "Thanks for spending some quality time with the web site today." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8686: +msgid: "Log in again" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8690: +msgid: "Your password was changed." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8694: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8721: +msgid: "Add %(name)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8731: +msgid: "Add" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8735: +msgid: "True" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8739: +msgid: "False" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8743: +msgid: "Hide counts" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8753: +msgid: "Clear all filters" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8757: +msgid: "Recent searches" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8761: +msgid: "No recent searches" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8771: +msgid: "Loading more results..." +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8792: +msgid: "No, take me back" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8796: +msgid: "Yes, I’m sure" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8800: +msgid: "Record picture" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8816: +msgid: "" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_34.txt b/translation_batch_34.txt new file mode 100644 index 0000000..7ccfebd --- /dev/null +++ b/translation_batch_34.txt @@ -0,0 +1,148 @@ +=== TRANSLATION BATCH 34 === +Translations 826-843 of 843 +============================================================ + +Line 8821: +msgid: "Reset filters" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8826: +msgid: "Go back" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8836: +msgid: "Unknown content" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8870: +msgid: "%(full_result_count)s total" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8888: +msgid: "Django administration" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8892: +msgid: "General" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8896: +msgid: "Dark" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8900: +msgid: "Light" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8904: +msgid: "System" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8908: +msgid: "Return to site" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8919: +msgid: "Choose file to upload" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8924: +msgid: "Change selected %(model)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8929: +msgid: "Add another %(model)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8934: +msgid: "View selected %(model)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8939: +msgid: "Delete selected %(model)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8943: +msgid: "Add row" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8947: +msgid: "Welcome" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8951: +msgid: "Select all objects on this page for an action" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + diff --git a/translation_batch_35.txt b/translation_batch_35.txt new file mode 100644 index 0000000..bd38fd6 --- /dev/null +++ b/translation_batch_35.txt @@ -0,0 +1,140 @@ +=== TRANSLATION BATCH 35 === +Translations 851-867 of 867 +============================================================ + +Line 8826: +msgid: "Go back" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8836: +msgid: "Unknown content" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8870: +msgid: "%(full_result_count)s total" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8888: +msgid: "Django administration" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8892: +msgid: "General" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8896: +msgid: "Dark" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8900: +msgid: "Light" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8904: +msgid: "System" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8908: +msgid: "Return to site" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8919: +msgid: "Choose file to upload" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8924: +msgid: "Change selected %(model)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8929: +msgid: "Add another %(model)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8934: +msgid: "View selected %(model)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8939: +msgid: "Delete selected %(model)s" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8943: +msgid: "Add row" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8947: +msgid: "Welcome" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- + +Line 8951: +msgid: "Select all objects on this page for an action" +msgstr: "" + +Arabic Translation: +msgstr: "" +---------------------------------------- +