Source code for notifications.apis

import pytz
from fcm_django.models import FCMDevice

import api.apis as api_app
from cobalt.settings import GLOBAL_ORG, TIME_ZONE
from notifications.models import RealtimeNotification
from notifications.views.core import send_cobalt_bulk_notifications

TZ = pytz.timezone(TIME_ZONE)


[docs] def fcm_token_get_user_v1(fcm_token): """Helper function to validate an FCM token Args: fcm_token: str. FCM token usually passed from mobile client. Not trusted. Returns: user(User): either a valid user associated with this token or False return_error_code (int or None): if user is invalid this will be the return code return_error_msg_structure (struct or None): if user is invalid this will be the return structure (StatusResponseV1) """ # Test for empty string if fcm_token == "": return ( False, 403, { "status": api_app.APIStatus.ACCESS_DENIED, "message": "Token is invalid", }, ) fcm_token_object = FCMDevice.objects.filter(registration_id=fcm_token).first() if fcm_token_object: return fcm_token_object.user, None, None return ( False, 403, { "status": api_app.APIStatus.ACCESS_DENIED, "message": "Token is invalid", }, )
[docs] def notifications_api_file_upload_v1(request, file, sender_identification=None): """API call to upload a file and send SMS messages""" from api.apis import APIStatus data = [] invalid_lines = [] lines_in_file = 0 for line in file.readlines(): lines_in_file += 1 line = line.decode("utf-8").strip() try: number, msg = line.split("\t") number = int(number) if isinstance(number, int): if len(msg.strip()) > 0: data.append((number, msg.replace("<NL>", "\n"))) else: invalid_lines.append( f"Line {lines_in_file}. No message found. : {line}" ) else: invalid_lines.append( f"Line {lines_in_file}. No {GLOBAL_ORG} number found. : {line}" ) except Exception as exc: if "too many values to unpack" in exc.__str__(): invalid_lines.append( f"Line {lines_in_file}. Too many tab characters in row. : {line}" ) elif "not enough values to unpack" in exc.__str__(): invalid_lines.append( f"Line {lines_in_file}. No tab character found in row. : {line}" ) elif "invalid literal for int" in exc.__str__(): invalid_lines.append( f"Line {lines_in_file}. Invalid {GLOBAL_ORG} number. : {line}" ) else: invalid_lines.append(f"Line {lines_in_file}. Invalid row {exc}: {line}") ( sent_users, unregistered_users, uncontactable_users, ) = send_cobalt_bulk_notifications( msg_list=data, admin=request.auth, description=file.name, invalid_lines=invalid_lines, total_file_rows=lines_in_file, sender_identification=sender_identification, ) # If we sent anything, we were successful status = APIStatus.SUCCESS if len(sent_users) > 0 else APIStatus.FAILURE return { "status": status, "sender": request.auth.__str__(), "filename": file.name, "counts": { "total_lines_in_file": lines_in_file, "valid_lines_in_file": len(data), "invalid_lines_in_file": lines_in_file - len(data), "registered_users_in_file": len(data) - len(unregistered_users), "registered_contactable_users_in_file": len(data) - len(unregistered_users) - len(uncontactable_users), "sent": len(sent_users), }, "errors": { "invalid_lines": invalid_lines, "unregistered_users": unregistered_users, "uncontactable_users": uncontactable_users, "sent_users": sent_users, }, }
def _notifications_api_common_messages_for_user_v1(messages): """Common code to handle returning messages""" if not messages: return 404, {"status": api_app.APIStatus.FAILURE, "message": "No data found"} # return messages as list return_messages = [] # mark messages as read now for message in messages: created_datetime = message.created_time.astimezone(TZ).strftime( "%a %d-%b-%Y %-I:%M" ) # Make am/pm lowercase created_datetime += message.created_time.astimezone(TZ).strftime("%p").lower() item = api_app.UnreadMessageV1( id=message.id, message=message.msg, created_datetime=created_datetime ) return_messages.append(item) message.has_been_read = True message.save() return 200, { "status": api_app.APIStatus.SUCCESS, "un_read_messages": return_messages, }
[docs] def notifications_api_unread_messages_for_user_v1(fcm_token): """Send any unread notifications (FCM) for a user""" user, error_code, error_struct = fcm_token_get_user_v1(fcm_token) if not user: return error_code, error_struct messages = ( RealtimeNotification.objects.filter(has_been_read=False) .filter(member=user) .order_by("-pk") ) return _notifications_api_common_messages_for_user_v1(messages)
[docs] def notifications_api_latest_messages_for_user_v1(fcm_token): """Send latest notifications (FCM) for a user regardless if read or not""" user, error_code, error_struct = fcm_token_get_user_v1(fcm_token) if not user: return error_code, error_struct messages = RealtimeNotification.objects.filter(member=user).order_by("-pk")[:50] return _notifications_api_common_messages_for_user_v1(messages)
[docs] def notifications_delete_message_for_user_v1(data): """Delete a single message""" user, error_code, error_struct = fcm_token_get_user_v1(data.fcm_token) if not user: return error_code, error_struct # Get message msg = RealtimeNotification.objects.filter(pk=data.message_id).first() if not msg: return 404, { "status": api_app.APIStatus.FAILURE, "message": "No match found", } if msg.member != user: return 403, { "status": api_app.APIStatus.ACCESS_DENIED, "message": "Message does not belong to this user", } # TODO: Add a status and keep the message but exclude from other queries msg.delete() return 200, { "status": api_app.APIStatus.SUCCESS, "message": "Message deleted", }
[docs] def notifications_delete_all_messages_for_user_v1(data): """Delete all messages for a user""" user, error_code, error_struct = fcm_token_get_user_v1(data.fcm_token) if not user: return error_code, error_struct # Get messages msgs = RealtimeNotification.objects.filter(member=user) if not msgs: return 404, { "status": api_app.APIStatus.FAILURE, "message": "No matches found", } # TODO: Add a status and keep the messages but exclude from other queries msgs.delete() return 200, { "status": api_app.APIStatus.SUCCESS, "message": "Message(s) deleted", }