""" Django settings for AgdarCentre project. Generated by 'django-admin startproject' using Django 5.2.7. For more information on this file, see 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 decouple import config from django.utils.translation import gettext_lazy as _ import environ # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Initialize environment variables env = environ.Env( DEBUG=(bool, True), ALLOWED_HOSTS=(list, []), LANGUAGE_CODE=(str, 'en'), TIME_ZONE=(str, 'Asia/Riyadh'), ) # Read .env file if it exists environ.Env.read_env(os.path.join(BASE_DIR, '.env')) # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-r@=dh)^w%tf2zff(stlze)4=f*&8iw%j=iri-#e@k=fz#_h!va' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ['localhost', '127.0.0.1', '192.168.1.137', '10.10.1.129'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # Third-party apps 'rest_framework', 'rest_framework.authtoken', 'drf_spectacular', 'django_filters', 'corsheaders', 'crispy_forms', 'crispy_bootstrap5', 'django_htmx', 'simple_history', 'phonenumber_field', 'django_celery_beat', 'django_celery_results', 'django_extensions', 'webpack_loader', # Project apps 'core.apps.CoreConfig', 'appointments.apps.AppointmentsConfig', 'finance.apps.FinanceConfig', 'notifications.apps.NotificationsConfig', 'nursing.apps.NursingConfig', 'medical.apps.MedicalConfig', 'aba.apps.AbaConfig', 'ot.apps.OtConfig', 'slp.apps.SlpConfig', 'referrals.apps.ReferralsConfig', 'integrations.apps.IntegrationsConfig', 'hr.apps.HrConfig', 'documents.apps.DocumentsConfig', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', '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', 'django_htmx.middleware.HtmxMiddleware', 'simple_history.middleware.HistoryRequestMiddleware', ] ROOT_URLCONF = 'AgdarCentre.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', ], 'debug': DEBUG, # Disable template caching in debug mode }, }, ] WSGI_APPLICATION = 'AgdarCentre.wsgi.application' # Database # https://docs.djangoproject.com/en/5.2/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Custom User Model AUTH_USER_MODEL = 'core.User' # Authentication URLs LOGIN_URL = 'login' LOGIN_REDIRECT_URL = '/dashboard/' LOGOUT_REDIRECT_URL = '/' # Password validation # https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators 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', }, ] # Internationalization # https://docs.djangoproject.com/en/5.2/topics/i18n/ LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), ) LANGUAGE_COOKIE_NAME = 'django_language' LANGUAGE_CODE = "en" USE_I18N = True USE_L10N = True USE_TZ = True LANGUAGES = [ ('en', _('English')), ('ar', _('Arabic')), ] TIME_ZONE = "Asia/Riyadh" # Static files (CSS, JavaScript, Images) STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' STATICFILES_DIRS = [ BASE_DIR / 'static', ] # Media files MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media' # WhiteNoise configuration STORAGES = { "default": { "BACKEND": "django.core.files.storage.FileSystemStorage", }, "staticfiles": { "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", }, } # Default primary key field type # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # Django REST Framework # https://www.django-rest-framework.org/api-guide/settings/ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.SearchFilter', 'rest_framework.filters.OrderingFilter', ], 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 50, 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle', ], 'DEFAULT_THROTTLE_RATES': { 'anon': '100/hour', 'user': '1000/hour', }, } # DRF Spectacular (API Documentation) # https://drf-spectacular.readthedocs.io/ SPECTACULAR_SETTINGS = { 'TITLE': 'Tenhal Multidisciplinary Healthcare Platform API', 'DESCRIPTION': 'API for managing multidisciplinary clinic operations including appointments, clinical documentation, and integrations', 'VERSION': '1.0.0', 'SERVE_INCLUDE_SCHEMA': False, 'COMPONENT_SPLIT_REQUEST': True, 'SCHEMA_PATH_PREFIX': '/api/', } # CORS Settings # https://github.com/adamchainz/django-cors-headers CORS_ALLOW_ALL_ORIGINS = True CORS_ALLOW_CREDENTIALS = True # Crispy Forms # https://django-crispy-forms.readthedocs.io/ CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_TEMPLATE_PACK = "bootstrap5" # Phone Number Field # https://github.com/stefanfoulis/django-phonenumber-field PHONENUMBER_DEFAULT_REGION = 'SA' PHONENUMBER_DEFAULT_FORMAT = 'INTERNATIONAL' # Django Money # https://github.com/django-money/django-money CURRENCIES = ('SAR', 'USD', 'EUR') CURRENCY_CHOICES = [ ('SAR', 'Saudi Riyal'), ('USD', 'US Dollar'), ('EUR', 'Euro'), ] # Celery Configuration # https://docs.celeryq.dev/en/stable/django/ CELERY_BROKER_URL = 'redis://localhost:6379/0' CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' CELERY_ACCEPT_CONTENT = ['json'] CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' CELERY_TIMEZONE = TIME_ZONE CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' CELERY_RESULT_EXTENDED = True # Email Configuration # https://docs.djangoproject.com/en/5.2/topics/email/ EMAIL_BACKEND = env('EMAIL_BACKEND', default='django.core.mail.backends.console.EmailBackend') EMAIL_HOST = env('EMAIL_HOST', default='localhost') EMAIL_PORT = env.int('EMAIL_PORT', default=587) EMAIL_USE_TLS = env.bool('EMAIL_USE_TLS', default=True) EMAIL_HOST_USER = env('EMAIL_HOST_USER', default='') EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD', default='') DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL', default='noreply@agdarcentre.sa') # Site URL for email links (consent signing, etc.) SITE_URL = env('SITE_URL', default='http://localhost:8000') # SMS/WhatsApp Configuration (Twilio) TWILIO_ACCOUNT_SID = '' TWILIO_AUTH_TOKEN = '' TWILIO_PHONE_NUMBER = '' TWILIO_WHATSAPP_NUMBER = '' # NPHIES Integration Configuration NPHIES_BASE_URL = 'https://nphies-test.sa/fhir' NPHIES_CLIENT_ID = '' NPHIES_CLIENT_SECRET = '' NPHIES_ENVIRONMENT = 'SIMULATION' # ZATCA E-Invoicing Configuration ZATCA_BASE_URL = 'https://gw-fatoora.zatca.gov.sa/e-invoicing/simulation' ZATCA_ENVIRONMENT = 'SIMULATION' ZATCA_OTP = '123345' ZATCA_CSID = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ0ZUQ0NBYndDQVFBd2RURUxNQWtHQTFVRUJoTUNVMEV4RmpBVUJnTlZCQXNNRFZKcGVXRmthQ0JDY21GdQpZMmd4SmpBa0JnTlZCQW9NSFUxaGVHbHRkVzBnVTNCbFpXUWdWR1ZqYUNCVGRYQndiSGtnVEZSRU1TWXdKQVlEClZRUUREQjFVVTFRdE9EZzJORE14TVRRMUxUTTVPVGs1T1RrNU9Ua3dNREF3TXpCV01CQUdCeXFHU000OUFnRUcKQlN1QkJBQUtBMElBQktGZ2ltdEVtdlJTQkswenI5TGdKQXRWU0NsOFZQWno2Y2RyNVgrTW9USG84dkhOTmx5Vwo1UTZ1N1Q4bmFQSnF0R29UakpqYVBJTUo0dTE3ZFNrL1ZIaWdnZWN3Z2VRR0NTcUdTSWIzRFFFSkRqR0IxakNCCjB6QWhCZ2tyQmdFRUFZSTNGQUlFRkF3U1drRlVRMEV0UTI5a1pTMVRhV2R1YVc1bk1JR3RCZ05WSFJFRWdhVXcKZ2FLa2daOHdnWnd4T3pBNUJnTlZCQVFNTWpFdFZGTlVmREl0VkZOVWZETXRaV1F5TW1ZeFpEZ3RaVFpoTWkweApNVEU0TFRsaU5UZ3RaRGxoT0dZeE1XVTBORFZtTVI4d0hRWUtDWkltaVpQeUxHUUJBUXdQTXprNU9UazVPVGs1Ck9UQXdNREF6TVEwd0N3WURWUVFNREFReE1UQXdNUkV3RHdZRFZRUWFEQWhTVWxKRU1qa3lPVEVhTUJnR0ExVUUKRHd3UlUzVndjR3g1SUdGamRHbDJhWFJwWlhNd0NnWUlLb1pJemowRUF3SURSd0F3UkFJZ1NHVDBxQkJ6TFJHOApJS09melI1L085S0VicHA4bWc3V2VqUlllZkNZN3VRQ0lGWjB0U216MzAybmYvdGo0V2FxbVYwN01qZVVkVnVvClJJckpLYkxtUWZTNwotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K' ZATCA_CERTIFICATE = '' ZATCA_PRIVATE_KEY = '' # Lab Integration Configuration LAB_API_URL = '' LAB_API_KEY = '' # Radiology Integration Configuration RADIOLOGY_API_URL = '' RADIOLOGY_API_KEY = '' # Security Settings SECURE_SSL_REDIRECT = False SESSION_COOKIE_SECURE = False CSRF_COOKIE_SECURE = False SECURE_HSTS_SECONDS = 0 SECURE_HSTS_INCLUDE_SUBDOMAINS = False SECURE_HSTS_PRELOAD = False # Logging Configuration LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}', 'style': '{', }, 'simple': { 'format': '{levelname} {message}', 'style': '{', }, }, 'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { 'console': { 'level': 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'file': { 'level': 'INFO', 'class': 'logging.FileHandler', 'filename': BASE_DIR / 'logs' / 'django.log', 'formatter': 'verbose', }, }, 'loggers': { 'django': { 'handlers': ['console', 'file'], 'level': 'INFO', 'propagate': False, }, 'core': { 'handlers': ['console', 'file'], 'level': 'DEBUG', 'propagate': False, }, 'appointments': { 'handlers': ['console', 'file'], 'level': 'DEBUG', 'propagate': False, }, 'integrations': { 'handlers': ['console', 'file'], 'level': 'DEBUG', 'propagate': False, }, }, } # Session Configuration SESSION_COOKIE_AGE = 86400 # 24 hours SESSION_SAVE_EVERY_REQUEST = True SESSION_COOKIE_HTTPONLY = True # File Upload Settings FILE_UPLOAD_MAX_MEMORY_SIZE = 10485760 # 10MB DATA_UPLOAD_MAX_MEMORY_SIZE = 10485760 # 10MB