mirror of
				https://github.com/i701/sarlink-portal-api.git
				synced 2025-10-31 16:06:58 +00:00 
			
		
		
		
	Enhance environment variable handling, add Celery configuration, and implement device expiration notification tasks
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build and Push Docker Images / Build and Push Docker Images (push) Failing after 13m45s
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build and Push Docker Images / Build and Push Docker Images (push) Failing after 13m45s
				
			This commit is contained in:
		
							
								
								
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -9,5 +9,6 @@ | |||||||
|   ], |   ], | ||||||
|   "python.testing.pytestEnabled": false, |   "python.testing.pytestEnabled": false, | ||||||
|   "python.testing.unittestEnabled": true, |   "python.testing.unittestEnabled": true, | ||||||
|   "postman.settings.dotenv-detection-notification-visibility": false |   "postman.settings.dotenv-detection-notification-visibility": false, | ||||||
|  |   "python.analysis.autoImportCompletions": true | ||||||
| } | } | ||||||
| @@ -4,8 +4,13 @@ import json | |||||||
| import logging | import logging | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| api_url = config("SMS_API_URL", default="") | api_url = str(config("SMS_API_URL", cast=str, default="")) | ||||||
| api_key = config("SMS_API_KEY", default="") | api_key = str(config("SMS_API_KEY", cast=str, default="")) | ||||||
|  |  | ||||||
|  | if not api_url or not api_key: | ||||||
|  |     raise ValueError( | ||||||
|  |         "SMS_API_URL and SMS_API_KEY must be set in the environment variables." | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def send_otp(mobile: str, otp: int, message: str): | def send_otp(mobile: str, otp: int, message: str): | ||||||
|   | |||||||
							
								
								
									
										59
									
								
								api/tasks.py
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								api/tasks.py
									
									
									
									
									
								
							| @@ -1,16 +1,57 @@ | |||||||
| from django.shortcuts import get_object_or_404 | from django.shortcuts import get_object_or_404 | ||||||
| from api.models import User | from api.models import User | ||||||
|  | from devices.models import Device | ||||||
| from api.sms import send_sms | from api.sms import send_sms | ||||||
| import requests | import requests | ||||||
| from apibase.env import env, BASE_DIR | from apibase.env import env, BASE_DIR | ||||||
| import os | import os | ||||||
| import logging | import logging | ||||||
|  | from celery import shared_task | ||||||
|  | from django.utils import timezone | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
| env.read_env(os.path.join(BASE_DIR, ".env")) | env.read_env(os.path.join(BASE_DIR, ".env")) | ||||||
| PEOPLE_API_URL = env.str("PEOPLE_API_URL", "") | PERSON_VERIFY_BASE_URL = env.str("PERSON_VERIFY_BASE_URL") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @shared_task | ||||||
|  | def add(x, y): | ||||||
|  |     print(f"Adding {x} and {y}") | ||||||
|  |     return x + y | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @shared_task | ||||||
|  | def deactivate_expired_devices(): | ||||||
|  |     expired_devices = Device.objects.filter( | ||||||
|  |         expiry_date__lte=timezone.localtime(timezone.now()), is_active=True | ||||||
|  |     ).select_related("user") | ||||||
|  |     print("Expired Devices: ", expired_devices) | ||||||
|  |     count = expired_devices.count() | ||||||
|  |     user_devices_map = {} | ||||||
|  |     for device in expired_devices: | ||||||
|  |         if device.user and device.user.mobile: | ||||||
|  |             if device.user.mobile not in user_devices_map: | ||||||
|  |                 user_devices_map[device.user.mobile] = [] | ||||||
|  |             user_devices_map[device.user.mobile].append(device.name) | ||||||
|  |  | ||||||
|  |     for mobile, device_names in user_devices_map.items(): | ||||||
|  |         if not mobile: | ||||||
|  |             continue | ||||||
|  |         device_list = "\n".join( | ||||||
|  |             [f"{i + 1}. {name}" for i, name in enumerate(device_names)] | ||||||
|  |         ) | ||||||
|  |         print("device list: ", device_list) | ||||||
|  |         send_sms( | ||||||
|  |             mobile, | ||||||
|  |             f"Dear {mobile}, \n\nThe following devices have expired: \n{device_list}. \n\nPlease make a payment to keep your devices active. \n\n- SAR Link", | ||||||
|  |         ) | ||||||
|  |     # expired_devices.update(is_active=False) | ||||||
|  |     print(f"Total {count} expired devices.") | ||||||
|  |     return { | ||||||
|  |         "total_expired_devices": count, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| def verify_user_with_person_api_task(user_id: int): | def verify_user_with_person_api_task(user_id: int): | ||||||
| @@ -21,9 +62,11 @@ def verify_user_with_person_api_task(user_id: int): | |||||||
|  |  | ||||||
|     user = get_object_or_404(User, id=user_id) |     user = get_object_or_404(User, id=user_id) | ||||||
|     # Call the Person API to verify the user |     # Call the Person API to verify the user | ||||||
|     if not PEOPLE_API_URL: |     if not PERSON_VERIFY_BASE_URL: | ||||||
|         raise ValueError("PEOPLE_API_URL is not set in the environment variables.") |         raise ValueError( | ||||||
|     response = requests.get(f"{PEOPLE_API_URL}/api/person/{user.id_card}") |             "PERSON_VERIFY_BASE_URL is not set in the environment variables." | ||||||
|  |         ) | ||||||
|  |     response = requests.get(f"{PERSON_VERIFY_BASE_URL}/api/person/{user.id_card}") | ||||||
|     if response.status_code == 200: |     if response.status_code == 200: | ||||||
|         data = response.json() |         data = response.json() | ||||||
|         api_nic = data.get("nic") |         api_nic = data.get("nic") | ||||||
| @@ -33,6 +76,13 @@ def verify_user_with_person_api_task(user_id: int): | |||||||
|         api_atoll = data.get("atoll_en") |         api_atoll = data.get("atoll_en") | ||||||
|         api_island_name = data.get("island_name_en") |         api_island_name = data.get("island_name_en") | ||||||
|  |  | ||||||
|  |         if not user.mobile or user.dob is None: | ||||||
|  |             logger.error("User mobile or date of birth is not set.") | ||||||
|  |             return None | ||||||
|  |         if not user.island or user.atoll is None: | ||||||
|  |             logger.error("User island or atoll is not set.") | ||||||
|  |             return None | ||||||
|  |  | ||||||
|         logger.info(f"API nic: {api_nic}") |         logger.info(f"API nic: {api_nic}") | ||||||
|         logger.info(f"API name: {api_name}") |         logger.info(f"API name: {api_name}") | ||||||
|         logger.info(f"API house name: {api_house_name}") |         logger.info(f"API house name: {api_house_name}") | ||||||
| @@ -77,6 +127,7 @@ def verify_user_with_person_api_task(user_id: int): | |||||||
|         else: |         else: | ||||||
|             user.verified = False |             user.verified = False | ||||||
|             user.save() |             user.save() | ||||||
|  |  | ||||||
|             send_sms( |             send_sms( | ||||||
|                 user.mobile, |                 user.mobile, | ||||||
|                 f"Dear {user.first_name} {user.last_name}, \n\nYour account registration is being processed. \n\nWe will notify you once verification is complete. \n\n - SAR Link", |                 f"Dear {user.first_name} {user.last_name}, \n\nYour account registration is being processed. \n\nWe will notify you once verification is complete. \n\n - SAR Link", | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								api/views.py
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								api/views.py
									
									
									
									
									
								
							| @@ -31,7 +31,7 @@ from typing import cast, Dict, Any | |||||||
| from django.core.mail import send_mail | from django.core.mail import send_mail | ||||||
| from django.db.models import Q | from django.db.models import Q | ||||||
| from api.sms import send_otp | from api.sms import send_otp | ||||||
|  | from .tasks import add, deactivate_expired_devices | ||||||
|  |  | ||||||
| # local apps import | # local apps import | ||||||
| from .serializers import ( | from .serializers import ( | ||||||
| @@ -57,6 +57,13 @@ class ErrorMessages: | |||||||
|     UNDERAGE_ERROR = "You must be 18 and above to signup." |     UNDERAGE_ERROR = "You must be 18 and above to signup." | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @api_view(["GET"]) | ||||||
|  | def healthcheck(request): | ||||||
|  |     add.delay(1, 2) | ||||||
|  |     deactivate_expired_devices.delay() | ||||||
|  |     return Response({"status": "Good"}, status=status.HTTP_200_OK) | ||||||
|  |  | ||||||
|  |  | ||||||
| class UpdateUserWalletView(generics.UpdateAPIView): | class UpdateUserWalletView(generics.UpdateAPIView): | ||||||
|     # Create user API view |     # Create user API view | ||||||
|     serializer_class = CustomUserByWalletBalanceSerializer |     serializer_class = CustomUserByWalletBalanceSerializer | ||||||
| @@ -416,11 +423,6 @@ class UserDetailAPIView(StaffEditorPermissionMixin, generics.RetrieveAPIView): | |||||||
|         return Response(data) |         return Response(data) | ||||||
|  |  | ||||||
|  |  | ||||||
| @api_view(["GET"]) |  | ||||||
| def healthcheck(request): |  | ||||||
|     return Response({"status": "Good"}, status=status.HTTP_200_OK) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @api_view(["POST"]) | @api_view(["POST"]) | ||||||
| @permission_classes((permissions.AllowAny,)) | @permission_classes((permissions.AllowAny,)) | ||||||
| def test_email(request): | def test_email(request): | ||||||
|   | |||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | # myproject/__init__.py | ||||||
|  | from .celery import app as celery_app | ||||||
|  |  | ||||||
|  | __all__ = ("celery_app",) | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								apibase/celery.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								apibase/celery.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | import os | ||||||
|  |  | ||||||
|  | from celery import Celery | ||||||
|  |  | ||||||
|  | # Set the default Django settings module for the 'celery' program. | ||||||
|  | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "apibase.settings") | ||||||
|  |  | ||||||
|  | app = Celery("apibase") | ||||||
|  |  | ||||||
|  | # Using a string here means the worker doesn't have to serialize | ||||||
|  | # the configuration object to child processes. | ||||||
|  | # - namespace='CELERY' means all celery-related configuration keys | ||||||
|  | #   should have a `CELERY_` prefix. | ||||||
|  | app.config_from_object("django.conf:settings", namespace="CELERY") | ||||||
|  |  | ||||||
|  | # Load task modules from all registered Django apps. | ||||||
|  | app.autodiscover_tasks(["api", "devices"]) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.task(bind=True, ignore_result=True) | ||||||
|  | def debug_task(self): | ||||||
|  |     print(f"Request: {self.request!r}") | ||||||
| @@ -363,3 +363,10 @@ PASSWORDLESS_AUTH = { | |||||||
|     "PASSWORDLESS_REGISTER_NEW_USERS": True, |     "PASSWORDLESS_REGISTER_NEW_USERS": True, | ||||||
|     "PASSWORDLESS_EMAIL_NOREPLY_ADDRESS": "noreply@sarlink.net", |     "PASSWORDLESS_EMAIL_NOREPLY_ADDRESS": "noreply@sarlink.net", | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # CELERY CONFIGURATION | ||||||
|  | CELERY_BROKER_URL = "redis://localhost:6379/0"  # or your Redis URL | ||||||
|  | CELERY_ACCEPT_CONTENT = ["json"] | ||||||
|  | CELERY_TASK_SERIALIZER = "json" | ||||||
|  | CELERY_RESULT_BACKEND = "redis://localhost:6379/0" | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
| 	"venv": ".venv", | 	"venv": ".venv", | ||||||
| 	"reportMissingImports": "error", | 	"reportMissingImports": "error", | ||||||
| 	"include": ["src"], | 	"include": ["src"], | ||||||
| 	"typeCheckingMode": "off", | 	"typeCheckingMode": "standard", | ||||||
| 	"exclude": [ | 	"exclude": [ | ||||||
| 		"council-api/**/migrations", | 		"council-api/**/migrations", | ||||||
| 		"**/__pycache__", | 		"**/__pycache__", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user