def channels_listall_v2(token): """ Given a user's id, returns a list of all channels and their associated details Arguments: token (string) - token of a registered user Return value: { channels } """ # get data data = getData() findUser(token) # Creates the return list channel_list = [] # Iterates through all the channels in the data dictionary for channels in data['channels']: # Adds the channel to the return list channel_dic = {'channel_id': channels['channel_id'], 'name': channels['channel_name']} channel_list.append(channel_dic) writeData(data) return {'channels' : channel_list}
def channel_leave_v1(token, channel_id): """Given channel id, removes an authorised user from that channel. Arguments: token (string) - token of an authenticated user channel_id (integer) - id of channel to leave Exceptions: InputError - Occurs when channel_id is not a valid channel - Occurs when the last owner tries to leave AccessError - Occurs when token refers to a user who is not a member of the channel - Occurs when token is not a valid Return value: {} """ # Load .json data store data_store = getData() # Check validity of channel_id ch_index = check_validity(data_store["channels"], channel_id, "channel_id", INVALID_CHANNEL_ID) ch_src = data_store["channels"][ch_index] # Check validity of token u_index = findUser(token) - 1 u_src = data_store["users"][u_index] # Check user is actually a member if not is_member(ch_src, u_src): raise AccessError(description="user is not a member of this channel.") # Check not last owner owner = is_owner(ch_src, u_src) if owner and len(ch_src["owner_members"]) == 1: raise InputError(description="last owner cannot leave the channel.") # Remove user from *_members user_remove = { "u_id": u_src["u_id"], "name_first": u_src["name_first"], "name_last": u_src["name_last"], "email": u_src["email"], "handle_str": u_src["handle_str"], "profile_img_url": u_src["profile_img_url"], } if owner: ch_src["owner_members"].remove(user_remove) ch_src["all_members"].remove(user_remove) append_user_stats(findUser(token), data_store) append_dream_stats(findUser(token), data_store) writeData(data_store) return {}
def message_edit_v2(token, message_id, message): ''' Arguments: token (string) - the token of the user who wants to create a channel message_id (int) - the id of the message being edited message (string) - the new message Exceptions: InputError - Message is more than 1000 characters or the message id refers to a deleted message AccessError - The authorised user has not joined the channel they are trying to post to Return value: Returns {} ''' # get data data = getData() # get id auth_user_id = findUser(token) # Checks that the length is not above 1000 characters if len(message) > 1000: raise InputError( description="Message must be less than 1000 characters") # Finds the message msg = get_message(message_id, data) if msg == -1: raise AccessError(description="Message does not exist") # Checks the message was sent by the user requesting to edit it or # the user is a channel owner if msg['u_id'] != auth_user_id and get_user_details( auth_user_id, data)['permission_id'] != 1: raise AccessError( description= "User requesting edit did not post message or not channel owner") # Delete the message if message is empty, updates the message if it isn't if message == '': remove_msg_from_channel_or_dm(data, msg) else: msg_key = data['messages'].index(msg) data['messages'][msg_key]['message'] = message # Sends notification send_msg_notification(data, token, message, msg['channel_id'], msg['dm_id'], False, -1) append_user_stats(findUser(token), data) append_dream_stats(findUser(token), data) writeData(data) return {}
def search_v2(token, query_str): """ Given a query string, return a collection of messages in all the channels/DMs that the authorised user has joined that match the query Arguments: token (string) - token of authenticated user query_str (string) - string to be searched for Exceptions: InputError - Occurs when query_str is above 1000 characters - Occurs when query_str is empty AccessError - Occurs when token is invalid Return value: { messages - List of dictionaries, where each dictionary contains types: {message_id, u_id, message, time_created} } """ # Check validity of token findUser(token) # Check query_str is less than 1000 characters or not empty if len(query_str) > 1000: raise InputError(description="query string can't be over 1000 characters") if len(query_str) == 0: raise InputError(description="query string can't be empty") # Define messages return type message_dict = { "messages" : [] } # Go through channels u_channels_list = channels_list_v2(token)["channels"] for channel in u_channels_list: msgs = channel_messages_v2(token, channel["channel_id"], 0) look_for_message(query_str, msgs, message_dict) while msgs["end"] != -1: msgs = channel_messages_v2(token, channel["channel_id"], msgs["end"]) look_for_message(query_str, msgs, message_dict) # Go through dms u_dms_list = dm_list_v1(token)["dms"] for dm in u_dms_list: msgs = dm_messages_v1(token, dm["dm_id"], 0) look_for_message(query_str, msgs, message_dict) while msgs["end"] != -1: msgs = dm_messages_v1(token, dm["dm_id"], msgs["end"]) look_for_message(query_str, msgs, message_dict) return message_dict
def message_senddm_v1(token, dm_id, message): ''' Arguments: token (string) - the token of the user who wants to create a channel dm_id (int) - the id of the dm the message is being sent to message (string) - the message being sent Exceptions: InputError - Message is more than 1000 characters AccessError - The authorised user has not joined the dm they are trying to post to Return value: Returns {'message_id': message_id} ''' # get data data = getData() # get id auth_user_id = findUser(token) # Checks the message is not more than 1000 characters if len(message) > 1000: raise InputError( description="Message must be less than 1000 characters") # Checks the auth_user_id is part of the dm dm_members = get_dm_members(dm_id, data) if auth_user_id not in dm_members: raise AccessError(description="User not in DM being posted to") # Get new message id new_message_id = get_new_message_id(data) # Adds message to data dictionary add_data(data, new_message_id, auth_user_id, -1, dm_id, message, int(time.time()), [], False) # Adds message to dm for dm in data['dms']: if dm_id == dm['dm_id']: dm['message_ids'].append(new_message_id) # Sends notification send_msg_notification(data, token, message, -1, dm_id, False, -1) append_user_stats(findUser(token), data) append_dream_stats(findUser(token), data) # Adds message to dm and makes notification writeData(data) return { 'message_id': new_message_id, }
def channel_join_v2(token, channel_id): """Given channel id, adds an authorised user to that channel. Arguments: token (string) - token of an authenticated user channel_id (integer) - id of channel to be joined Exceptions: InputError - Occurs when channel_id is not a valid channel AccessError - Occurs when channel_id refers to private channel and user is not a global owner - Occurs when token is not a valid id Return value: {} """ # Load .json data store data_store = getData() # Check validity of channel_id ch_index = check_validity(data_store["channels"], channel_id, "channel_id", INVALID_CHANNEL_ID) ch_src = data_store["channels"][ch_index] # Check validity of token u_id = findUser(token) for user in data_store["users"]: if user["u_id"] == u_id: u_src = user # Check permissions of channel and user u_permission = u_src["permission_id"] if not ch_src["is_public"] and u_permission != 1: raise AccessError(description="Can't join a private channel") # Add user to the channel user_add = { "u_id": u_src["u_id"], "name_first": u_src["name_first"], "name_last": u_src["name_last"], "email": u_src["email"], "handle_str": u_src["handle_str"], "profile_img_url": u_src["profile_img_url"], } ch_src["all_members"].append(user_add) append_user_stats(findUser(token), data_store) append_dream_stats(findUser(token), data_store) writeData(data_store) return {}
def user_profile_v2(token, u_id): """ Given valid token and u_id, returns information regarding the user with u_id 'u_id' Arguments: token (string) - valid token associated with a registered user's session u_id (int) - user id associated with a registered user Exceptions: InputError - Occurs when u_id is not associated with a valid user AccessError - Occurs if token is invalid Return value: {'user' : {'u_id' : u_id, 'email' :email, 'name_first' : name_first, 'name_last' : name_last, 'handle_str' : handle_str,} } """ #Retrieving data from export.json data = getData() #Testing for a valid token findUser(token) #Cycling through all users to find the user with user id u_id for user in data['users']: if u_id == user['u_id']: #Returning the users information return { 'user': { 'u_id': user['u_id'], 'email': user['email'], 'name_first': user['name_first'], 'name_last': user['name_last'], 'handle_str': user['handle_str'], 'profile_img_url': user['profile_img_url'] }, } #Repeating the process with 'removed_users' to assist in other functions for user in data['removed_users']: if u_id == user['u_id']: return { 'user': { 'u_id': user['u_id'], 'email': user['email'], 'name_first': user['name_first'], 'name_last': user['name_last'], 'handle_str': user['handle_str'], 'profile_img_url': user['profile_img_url'] }, } #Raising an input error if the user could not be found raise InputError(description="User with u_id is not a valid user")
def message_remove_v1(token, message_id): ''' Arguments: token (string) - the token of the user who wants to create a channel message_id (int) - the id of the message being removed Exceptions: InputError - Message id no longer exists AccessError - User did not send message and is not an owner of the channel/dm or dreams Return value: Returns {} ''' # get data data = getData() # get id auth_user_id = findUser(token) # Checks the message was sent by the user requesting to edit it or # the user is a channel owner msg = get_message(message_id, data) if msg == -1: raise AccessError(description="Message does not exist") chan_owners = [] if msg['channel_id'] != -1: chan = get_channel_details(msg, data) chan_owners = get_channel_owners(chan['channel_id'], data) if msg['u_id'] != auth_user_id and get_user_details( auth_user_id, data)['permission_id'] != 1 and auth_user_id not in chan_owners: raise AccessError( description= "User requesting edit did not post message or not channel/DREAMS owner" ) # Remove from channels/dms list remove_msg_from_channel_or_dm(data, msg) append_user_stats(findUser(token), data) append_dream_stats(findUser(token), data) writeData(data) return {}
def new_added_notification(token, channel_id, dm_id, u_id): ''' Returns a notification dictionary in the expected format after a user is added to a channel/dm Arguments: token (str) - token of the auth user who added the new user channel_id (int) - id of the channel in which it occured dm_id (int) - id of the dm in which it occured u_id (int) - id of the user being added Return value: { 'u_id': u_id 'channel_id': channel_id, 'dm_id': dm_id, 'notification_message': message } ''' database = getData() auth_user_id = findUser(token) user_handle = user_profile_v2(token, auth_user_id)['user']['handle_str'] # Find the channel/dm name given channel/dm id src_name = get_channel_dm_name(database, channel_id, dm_id) return { 'u_id': u_id, 'channel_id': channel_id, 'dm_id': dm_id, 'notification_message': create_add_notif(user_handle, src_name) }
def user_profile_setname_v2(token, name_first, name_last): """ Given valid token and first/last names, changes a registered users name Arguments: token (string) - valid token associated with a registered user's session name_first (string) - first name the user wants to update to name_last (string) - last name the user wants to update to Exceptions: InputError - When either name_first or name_last is not between 1 and 50 characters, inclusive AccessError - Occurs if token is invalid Return value: {} """ #Retrieving data from export.json data = getData() #Testing for a valid token and finding the associated u_id u_id = findUser(token) #Checking if the names are of valid length if len(name_first) > 50 or len(name_first) < 1: raise InputError(description="Length of first name is not valid") elif len(name_last) > 50 or len(name_last) < 1: raise InputError(description="Length of last name is not valid") #Finding the user and changing their names for user in data['users']: if u_id == user['u_id']: user['name_first'] = name_first user['name_last'] = name_last #Writing data to export.json writeData(data) return {}
def user_profile_uploadphoto_v1(token, img_url, x_start, y_start, x_end, y_end): """ Given a URL of an image on the internet, crops the image within bounds (x_start, y_start) and (x_end, y_end). Position (0,0) is the top left. Arguments: token (string) - valid token associated with a registered user's session img_url (string) - URl to a jpg online x_start (int) - indicates leftmost point on image y_start (int) - indicates topmost point on image x_end (int) - indicates righttmost point on image y_end (int) - indicates bottommost point on image Exceptions: InputError - img_url returns an HTTP status other than 200. InputError - any of x_start, y_start, x_end, y_end are not within the dimensions of the image at the URL. InputError - Image uploaded is not a JPG Return value: { } """ #Retrieving data from dictionary data = getData() #Find user id from their token u_id = findUser(token) #Define URL URL = f'{img_url}' #Define file path fileName = f'src/static/{u_id}.jpg' #Trying to open URL, if it fails raise an InputError try: status = requests.get(URL).status_code except: status = -1 if status != 200: raise InputError(description='Invalid URL') #Raising an error if the URL does not point to a jpg if not URL.endswith('.jpg'): raise InputError(description='Image is not a jpg') #Retrieve the URL abd save the image as fileName urllib.request.urlretrieve(URL, fileName) #Open the image img = Image.open(fileName) #Define the size of the image width, height = img.size #Raising an error if the crop points aren't valid if x_start < 0 or y_start < 0 or x_end > width or y_end > height or x_end < x_start or y_end < y_start: raise InputError(description='Crop start or end points not valid') #Cropping and saving the image crop_img = img.crop((x_start, y_start, x_end, y_end)) crop_img.save(fileName) #Changin the user's associated URL to the new image for user in data['users']: if u_id == user['u_id']: user['profile_img_url'] = f'{config.url}/images/{u_id}.jpg' #Writing data to dictionary writeData(data) return {}
def message_unreact_v1(token, message_id, react_id): ''' Arguments: token (string) - the token of the user who wants to create a channel message_id - the id of the message being reacted to react_id (int) - the id of the react Exceptions: InputError - message_id is not a valid message within a channel or DM that the authorised user has joined InputError - react_id is not a valid React ID. The only valid react ID the frontend has is 1 InputError - Message with ID message_id does not contain an active React with ID react_id from the authorised user AccessError - The authorised user is not a member of the channel or DM that the message is within Return value: Returns {} ''' # get data data = getData() # get id auth_user_id = findUser(token) # Gets message msg = get_message(message_id, data) # Message does not exist (has been removed or invalid message_id passed in) if msg == -1: raise InputError("Message is not a valid.") # Gets message key msg_key = data['messages'].index(msg) # Gets list of dictionary containing all the reacts for that message react_list = data['messages'][msg_key]['reacts'] # Checks react id is valid and in use already if react_id != 1: raise InputError("React ID is not valid.") elif not has_user_reacted(auth_user_id, react_id, react_list, data): raise InputError("User has not already reacted with this react.") # Checks the user is part of the channel/dm check_user_permissions(data, msg_key, auth_user_id) # Remove/update react react_key = get_react(react_id, react_list, msg_key, data) data['messages'][msg_key]['reacts'][react_key]['u_ids'].remove( auth_user_id) if data['messages'][msg_key]['reacts'][react_key]['u_ids'] == []: data['messages'][msg_key]['reacts'].remove({ 'react_id': react_id, 'u_ids': [] }) writeData(data) return {}
def admin_userpermission_change_v1(token, u_id, permission_id): """ Given a User by their user ID, set their permissions to new permissions described by permission_id. Arguments: token (str) - token passed in to the function u_id (int) - id of user to change permission of permission_id (int) - new permission id Exceptions: AccessError - when token passed in is not a valid id InputError - when u_id does not refer to a valid user InputError - when permission_id does not refer to a valid permission AccessError - when authorised user is not an owner Return value: {} """ data_store = getData() # Check for exceptions # validate token auth_user_id = findUser(token) # check if u_id is a valid user, and also get index of position if valid valid = False for user_index, user in enumerate(data_store["users"]): if user["u_id"] == u_id: valid = True break if not valid: raise InputError(description="u_id does not refer to a valid user") # Check permission_id is valid if permission_id != 1 and permission_id != 2: raise InputError(description="Invalid permission_id") # Check if authorised user is an owner (with permission 1) for user in data_store["users"]: if user["u_id"] == auth_user_id: if user["permission_id"] != 1: raise AccessError(description="Authorised user is not an owner.") # Cannot change permission to permission_id 2 if there is only one # owner remaining in Dreams if permission_id == 2: if get_dream_owners_count(data_store) > 1: data_store["users"][user_index]["permission_id"] = permission_id else: data_store["users"][user_index]["permission_id"] = permission_id writeData(data_store) return {}
def users_all_v1(token): """ Given valid token, returns a list of all users and their associated details Arguments: token (string) - valid token associated with a registered user's session Exceptions: AccessError - Occurs if token is invalid Return value: { 'users' : [ user' : {'u_id' : u_id, 'email' :email, 'name_first' : name_first, 'name_last' : name_last, 'handle_str' : handle_str,} ] } """ #Retrieving data from export.json data = getData() #Checking for invalid token findUser(token) #Creating a user list user_list = [] #Appending every user's info to the user_list for user in data['users']: info = { 'u_id': user['u_id'], 'email': user['email'], 'name_first': user['name_first'], 'name_last': user['name_last'], 'handle_str': user['handle_str'], 'profile_img_url': user['profile_img_url'] } user_list.append(info) #Returning a dictionary with user_list return { 'users': user_list, }
def channel_details_v2(token, channel_id): """ Given a Channel with ID channel_id that the authorised user is part of, provide basic details about the channel. Arguments: token (str) - token passed in to the function channel_id (int) - channel_id of the channel to show details of Exceptions: InputError - Occurs when channel ID is not a valid channel. AccessError - Occurs when token does not refer to a valid user. AccessError - Occurs when authorised user is not a member of channel with channel_id. Return Value: Returns a dictionary { name, owner_members, all_members } on successful call. """ database = getData() auth_user_id = findUser(token) # Check for exceptions channel_index = check_validity(database["channels"], channel_id, "channel_id", INVALID_CHANNEL_ID) channel_src = database["channels"][channel_index] check_validity(channel_src["all_members"], auth_user_id, "u_id", UNAUTHORISED_AUTH_U_ID) # Copy details from "database" to empty dictionaries owner_members_list = [] all_members_list = [] owner_members_list = copy_channel_member_details( channel_src["owner_members"], owner_members_list, database) all_members_list = copy_channel_member_details(channel_src["all_members"], all_members_list, database) # Create and return a dictionary with copied data channel_details = { "name": channel_src.get("channel_name"), "owner_members": owner_members_list, "all_members": all_members_list, } return channel_details
def new_react_notification(token, channel_id, dm_id, u_id): database = getData() auth_user_id = findUser(token) user_handle = user_profile_v2(token, auth_user_id)['user']['handle_str'] # Find the channel/dm name given channel/dm id src_name = get_channel_dm_name(database, channel_id, dm_id) return { 'u_id': u_id, 'channel_id': channel_id, 'dm_id': dm_id, 'notification_message': create_react_notif(user_handle, src_name) }
def notifications_get_v1(token): """ Return the user's most recent 20 notifications Arguments: token (str) - token passed in to the function Exceptions: AccessError - when the token passed in is not a valid id Return value: {notifications} - a list of dictionaries, where each dictionary contains types { channel_id, dm_id, notification_message } where channel_id is the id of the channel that the event happened in, and is -1 if it is being sent to a DM. dm_id is the DM that the event happened in, and is -1 if it is being sent to a channel. The list should be ordered from most to least recent. Notification_message is a string of the following format for each trigger action: tagged: "{User’s handle} tagged you in {channel/DM name}: {first 20 characters of the message}" added to a channel/DM: "{User’s handle} added you to {channel/DM name}" """ auth_user_id = findUser(token) database = getData() # Extract all notifications relating to the concerned user from database, and # format each to expected "notifications type" as defined in the spec user_notifications_list = [] for user_notification in database['user_notifications']: if user_notification['u_id'] == auth_user_id: formatted_notification = { 'channel_id': user_notification['channel_id'], 'dm_id': user_notification['dm_id'], 'notification_message': user_notification['notification_message'] } user_notifications_list.append(formatted_notification) # Reverse the user_notifications_list to get the reversed chronological order, # and return the 20 most recent notifications user_notifications_list.reverse() return {'notifications': user_notifications_list[0:20]}
def message_unpin_v1(token, message_id): ''' Arguments: token (string) - the token of the user who wants to create a channel message_id - the id of the message being pinned Exceptions: InputError - message_id is not a valid message InputError - Message with ID message_id is already unpinned AccessError - The authorised user is not a member of the channel or DM that the message is within or the authorised user is not an owner of the channel or DM Return value: Returns {} ''' # get data data = getData() # get id auth_user_id = findUser(token) # Gets message msg = get_message(message_id, data) # Message does not exist (has been removed or invalid message_id passed in) if msg == -1: raise InputError("Message is not a valid.") # Gets message key msg_key = data['messages'].index(msg) # Checks if message is already unpinned if not data['messages'][msg_key]['is_pinned']: raise InputError("Message is already unpinned.") # Checks the user is part of/an owner of the channel/dm check_user_owners_permissions(data, msg_key, auth_user_id) # Add pin data['messages'][msg_key]['is_pinned'] = False writeData(data) return {}
def user_profile_sethandle_v1(token, handle_str): """ Given valid token and handle, changes a registered users handle Arguments: token (string) - valid token associated with a registered user's session handle (string) - handle the user wants to update to Exceptions: InputError - Occurs if the handle is not between 3 and 20 characters, inclusive InputError - Occurs if the handle is already in use AccessError - Occurs if token is invalid Return value: {} """ #Retrieving data from export.json data = getData() #Checking for an invalid token u_id = findUser(token) #Checking for @ and whitespace - as per the assumption in assumptions.md if handle_str.find('@') != -1: raise InputError(description="Handle cannot contain '@' symbol") elif handle_str.find(' ') != -1: raise InputError(description="Handle cannot contain whitespace") #Checking if the handle is of valid length elif len(handle_str) > 20 or len(handle_str) < 3: raise InputError(description="Length of handle is not valid") #Checking if the handle is in use for user in data['users']: if handle_str == user['handle_str']: raise InputError(description='Handle is already in use') #Changing the given users handle for user in data['users']: if u_id == user['u_id']: user['handle_str'] = handle_str #Writing data to export.json writeData(data) return {}
def user_stats(token): """ Fetches the required statistics about this user's use of UNSW Dreams Arguments: token (string) - valid token associated with a registered user's session Return value: { users_stats } """ #Retrieving data data = getData() #Finding a users id from their token u_id = findUser(token) #Retrieving total channel, dm and message counts for involvement rate calculation total_channel_count = data['dream_stats']['channels_exist'][-1][ 'num_channels_exist'] total_dm_count = data['dream_stats']['dms_exist'][-1]['num_dms_exist'] total_message_count = data['dream_stats']['messages_exist'][-1][ 'num_messages_exist'] total_count = total_message_count + total_dm_count + total_channel_count #For the provided user, retrieve their personal message, channel and dm counts whilst also calculating their involvement rate for user in data['users']: if u_id == user['u_id']: message_count = user['user_stats']['messages_sent'][-1][ 'num_messages_sent'] channel_count = user['user_stats']['channels_joined'][-1][ 'num_channels_joined'] dm_count = user['user_stats']['dms_joined'][-1]['num_dms_joined'] if total_count != 0: involve_rate = (message_count + dm_count + channel_count) / total_count else: involve_rate = 0 user['user_stats']['involvement_rate'] = involve_rate stat_data = user['user_stats'] return {'user_stats': stat_data}
def channels_list_v2(token): """ Given a user's token, returns a list of all channels and their associated details that the user is in Arguments: token (string) - token of a registered user Return value: { channels } """ # get data data = getData() # get user id auth_user_id = findUser(token) # Creates the return list channel_list = [] # Iterates through all the channels in the data dictionary for channels in data['channels']: # Checks if the user is a member in the channel for users in channels['all_members']: if auth_user_id == users['u_id']: # If member - adds the channel details to the return list channel_dic = {'channel_id': channels['channel_id'], 'name': channels['channel_name']} channel_list.append(channel_dic) writeData(data) return {'channels' : channel_list}
def user_profile_setemail_v2(token, email): """ Given valid token and email, changes a registered users email Arguments: token (string) - valid token associated with a registered user's session email (string) - email the user wants to update to Exceptions: InputError - Occurs if the email is not of valid formation InputError - Occurs if the email is already in use AccessError - Occurs if token is invalid Return value: {} """ #Retrieving data from export.json data = getData() #Checking for an invalid token u_id = findUser(token) #Checking if the inputted email is valid if check_email(email) == False: raise InputError(description='Invalid email') #Checking if the email is already in use for user in data['users']: if email == user['email']: raise InputError(description='Email address is already in use') #Changing the given users email for user in data['users']: if u_id == user['u_id']: user['email'] = email #Writing data to export.json writeData(data) return {}
def channel_invite_v2(token, channel_id, u_id): """ Invites a user (with user id u_id) to join a channel with ID channel_id. Once invited the user is added to the channel immediately. Arguments: token (str) - token passed in to the function channel_id (int) - the id of the channel to invite to u_id (int) - the id of the user that is being invited Exceptions: InputError - Occurs when channel_id does not refer to a valid channel. InputError - Occurs when u_id does not refer to a valid user. AccessError - Occurs when token pass in is not a valid id. AccessError - Occurs when the authorised user is not already a member of the channel. Return Value: Returns {} on successful invite """ auth_user_id = findUser(token) database = getData() # Check for exceptions channel_index = check_validity(database["channels"], channel_id, "channel_id", INVALID_CHANNEL_ID) channel_src = database["channels"][channel_index] user_index = check_validity(database["users"], u_id, "u_id", INVALID_U_ID) user_src = database["users"][user_index] check_validity(channel_src["all_members"], auth_user_id, "u_id", UNAUTHORISED_AUTH_U_ID) # Only add the user if they are not already in the channel for member in channel_src["all_members"]: if u_id in member.values(): return {} # Create a new dictionary for the added user with their details # and add them to the channel new_member = { "u_id": user_src["u_id"], "name_first": user_src["name_first"], "name_last": user_src["name_last"], "email": user_src["email"], "handle_str": user_src["handle_str"], "profile_img_url": user_src["profile_img_url"], } channel_src["all_members"].append(new_member) # Create a notification for the added user user_notification = new_added_notification(token, channel_id, -1, u_id) database["user_notifications"].append(user_notification) append_user_stats(u_id, database) append_dream_stats(u_id, database) writeData(database) return {}
def channels_create_v2(token, name, is_public): ''' Arguments: token (string) - the token of the one who wants to create a channel name (string) - the name of the channel which is going to be created is_public (boolean) - whether the channel is public or private Exceptions: Inputerror - occur when the name of channel is larger than 20 characters Accesserror - occur when the token is not valid or hasn't been registered. Return value: Returns {'channel_id': } when there is no exception being raised ''' data = getData() auth_user_id = findUser(token) if len(name) > 20: raise InputError(description="Name too long.") else: new_channel_id = len(data['channels']) + 1 name_last = data['users'][auth_user_id - 1]['name_last'] name_first = data['users'][auth_user_id - 1]['name_first'] email = data['users'][auth_user_id - 1]['email'] handle_str = data['users'][auth_user_id - 1]['handle_str'] is_active = False stand_up_finished_time = 0 stand_up_length = 0 data['channels'].append({ 'channel_id': new_channel_id, 'channel_name' : name, 'all_members': [{ 'u_id':auth_user_id, 'name_last': name_last, 'name_first': name_first, 'email': email, 'handle_str': handle_str, 'profile_img_url' : data['users'][auth_user_id - 1]['profile_img_url'], },], 'owner_members':[{ 'u_id':auth_user_id, 'name_last': name_last, 'name_first': name_first, 'email': email, 'handle_str': handle_str, 'profile_img_url' : data['users'][auth_user_id - 1]['profile_img_url'], },], 'is_public' : is_public, 'standup': { 'is_active': is_active, 'messages':"", 'time_finish': stand_up_finished_time, 'stand_up_length': stand_up_length, }, 'messages' : [], }) append_user_stats(auth_user_id, data) append_dream_stats(auth_user_id, data) writeData(data) return { 'channel_id': new_channel_id, }
def message_share_v1(token, og_message_id, message, channel_id, dm_id): ''' Arguments: token (string) - the token of the user who wants to create a channel og_message_id - the id of the inital message message (string) - the message being appended at the end of the share channel_id (int) - the id of the channel the message is being sent to dm_id (int) - the id of the dm the message is being sent to Exceptions: InputError - Message is more than 1000 characters AccessError - The authorised user has not joined the channel/dm they are trying to share to Return value: Returns {'shared_message_id': shared_message_id} ''' # get data data = getData() # get id auth_user_id = findUser(token) # Checks that the user is part of the channel or dm they are sharing to channel_members = get_channel_members(channel_id, data) dm_members = get_dm_members(dm_id, data) if auth_user_id not in channel_members and auth_user_id not in dm_members: raise AccessError(description="User not member of channel") # Get og message msg = get_message(og_message_id, data) # Check user has access to og message if auth_user_id != msg['u_id']: raise AccessError( "User not member of channel from which the message comes from") # Checks the new message is not more than 1000 characters if len(msg['message'] + '\n' + message) > 1000 and message != '': raise InputError( description="Message must be less than 1000 characters") # Get new message id new_message_id = get_new_message_id(data) # Copies og message new_msg = msg['message'] # Add optional message at end if message != '': new_msg = msg['message'] + '\n' + message # Adds message to data dictionary field 'messages' add_data(data, new_message_id, auth_user_id, channel_id, -1, new_msg, int(time.time()), [], False) # Share to DM/Channel if channel_id != -1 and dm_id != -1: raise InputError("Usage: One of channel_id or dm_id must be -1.") elif channel_id == -1: # Adds message to dm for dm in data['dms']: if dm_id == dm['dm_id']: dm['message_ids'].append(new_message_id) else: # Adds message to channel for chan in data['channels']: if channel_id == chan['channel_id']: chan['messages'].append(new_message_id) # Sends notification send_msg_notification(data, token, new_msg, channel_id, dm_id, False, -1) append_user_stats(findUser(token), data) append_dream_stats(findUser(token), data) writeData(data) return {'shared_message_id': new_message_id}
def channel_messages_v2(token, channel_id, start): """Given channel id, return up to 50 messages Arguments: token (string) - token of an authenticated user who is part of the channel channel_id (integer) - id of channel where messages come from start (integer) - starting index (counting backwards, most recent messages returned first) Exceptions: InputError - Occurs when channel_id is not a valid channel - Occurs when start is greater than total number of messages AccessError - Occurs when authorised user is not a member of the channel - Occurs when token is not valid Return value: { messages, - List of dictionaries, where each dictionary contains types: {message_id, u_id, message, time_created, reacts, is_pinned} start, - integer end - integer (value of start+50 or -1) } """ current_time = int(time.time()) # Load .json data store data_store = getData() # Check validity of channel_id ch_index = check_validity(data_store["channels"], channel_id, "channel_id", INVALID_CHANNEL_ID) ch_src = data_store["channels"][ch_index] # Check validity of token u_id = findUser(token) for user in data_store["users"]: if user["u_id"] == u_id: u_src = user # Check user is a member of channel check_validity(ch_src["all_members"], u_src["u_id"], "u_id", UNAUTHORISED_AUTH_U_ID) # Check start is less than total number of messages total_messages = len(ch_src["messages"]) if total_messages > 0 and start > total_messages - 1: raise InputError( description="There aren't that many messages in this channel.") # Return type message_dict = { "messages": [], "start": start, "end": -1, } # Go through channel messages backwards msg_reverse = list(reversed(ch_src["messages"])) if start + 50 < total_messages - 1: message_dict["messages"] = msg_reverse[start:(start + 50)] message_dict["end"] = start + 50 else: message_dict["messages"] = msg_reverse[start:] message_list = [] for message_id in message_dict["messages"]: message_to_add = get_message_from_id(message_id, u_id, data_store) if message_to_add["time_created"] <= current_time: message_list.append(message_to_add) message_dict["messages"] = message_list return message_dict
def admin_user_remove_v1(token, u_id): """ Given a User by their user ID, remove the user from the Dreams. Dreams owners can remove other **Dreams** owners (including the original first owner). Once users are removed from **Dreams**, the contents of the messages they sent will be replaced by 'Removed user'. Their profile must still be retrievable with user/profile/v2, with their name replaced by 'Removed user' Arguments: token (str) - token passed in to the function u_id (int) - id of user to remove Exceptions: AccessError - when token passed in is not a valid id InputError - when u_id does not refer to a valid user InputError - when the user is currently the only owner AccessError - when the authorised user is not an owner Return value: {} """ data_store = getData() # Check for exceptions # validate token auth_user_id = findUser(token) # check if u_id is a valid user, and also get index of position if valid valid = False for user_index, user in enumerate(data_store["users"]): if user["u_id"] == u_id: valid = True break if not valid: raise InputError(description="u_id does not refer to a valid user") # Check if authorised user is an owner (with permission 1) for user in data_store["users"]: if user["u_id"] == auth_user_id: if user["permission_id"] != 1: raise AccessError(description="Authorised user is not an owner.") break # Check if the user is currently the only owner if get_dream_owners_count(data_store) == 1 and auth_user_id == u_id: raise InputError(description="user is currently the only owner.") # Move user dictionary from users list to removed_users list data_store["removed_users"].append(data_store["users"][user_index]) del data_store["users"][user_index] # Remove user from all channels for channel in data_store["channels"]: for member in channel["all_members"]: if member["u_id"] == u_id: channel["all_members"].remove(member) break for owner in channel["owner_members"]: if owner["u_id"] == u_id: channel["owner_members"].remove(owner) break # Remove user from all dms for dm in data_store["dms"]: for member in dm["dm_members"]: if member == u_id: dm["dm_members"].remove(member) break # Replace name of user with "Removed user", and also clear token and # session lists removed_user_index = len(data_store["removed_users"]) - 1 removed_user_profile = data_store["removed_users"][removed_user_index] removed_user_profile["name_first"] = "Removed user" removed_user_profile["name_last"] = "" removed_user_profile["token_list"] = [] removed_user_profile["sessions_lists"] = [] # Replace the contents that the user sent with "Removed user" for user_message in data_store["messages"]: if user_message["u_id"] == u_id: user_message["message"] = "Removed user" writeData(data_store) return {}
def message_sendlater_v1(token, channel_id, message, time_sent): ''' Send a message from authorised user to the channel, automatically at a specified time in the future Arguments: token (string) - token of an authenticated user who is part of the channel channel_id (integer) - id of channel to send message in message (string) - actual message content time_sent (integer) - unix timestamp of when message will be sent Exceptions: InputError - Occurs when channel_id is not a valid channel - Occurs when message is more than 1000 characters - Occurs when time_sent before current time AccessError - Occurs when authorised user is not a member of channel - Occurs when token is not valid Return value: { message_id } ''' current_time = int(time.time()) # Load .json data store data_store = getData() # Check validity of channel_id check_validity(data_store["channels"], channel_id, "channel_id", 0) # Check message is less than 1000 characters if len(message) > 1000: raise InputError( description="Message must be less than 1000 characters") # Check time_sent is in the future if time_sent < current_time: raise InputError(description="Cannot send a message in the past") # Get user from token and check they are member of channel u_id = findUser(token) channel_members = get_channel_members(channel_id, data_store) if u_id not in channel_members: raise AccessError(description="User not in channel!") # Get new message_id new_message_id = get_new_message_id(data_store) # Add message to data_store add_data(data_store, new_message_id, u_id, channel_id, -1, message, time_sent, [], False) # Set up thread to send message in channel later if (time_sent == current_time): # Append message to channel messages for channel in data_store["channels"]: if channel_id == channel["channel_id"]: channel["messages"].append(new_message_id) send_msg_notification(data_store, token, message, channel_id, -1, False, new_message_id) else: sendlater_thread = Timer(time_sent - current_time, send_msg_notification, args=[ data_store, token, message, channel_id, -1, True, new_message_id ]) sendlater_thread.start() append_user_stats(findUser(token), data_store) append_dream_stats(findUser(token), data_store) writeData(data_store) return {"message_id": new_message_id}
def channel_removeowner_v1(token, channel_id, u_id): """Given channel id, removes u_id from owners of the channel. Arguments: token (string) - token of an authenticated user who is either an owner of the channel or a global owner channel_id (integer) - id of channel with ownership u_id (integer) - id of user to be removed of channel ownership Exceptions: InputError - Occurs when channel_id is not a valid channel - Occurs when u_id refers to a user who is not an owner - Occurs when u_id is currently the only owner AccessError - Occurs when token refers to a user who is not a global owner or an owner of the target channel - Occurs when token is not valid Return value: {} """ # Load .json data store data_store = getData() # Check validity of channel_id ch_index = check_validity(data_store["channels"], channel_id, "channel_id", INVALID_CHANNEL_ID) ch_src = data_store["channels"][ch_index] # Check validity of token token_index = findUser(token) - 1 token_src = data_store["users"][token_index] # Check validity of u_id u_index = check_validity(data_store["users"], u_id, "u_id", INVALID_U_ID) u_src = data_store["users"][u_index] # Check user to be removed of ownership is actually an owner if not is_owner(ch_src, u_src): raise InputError(description="user is not an owner of this channel.") # Check token user is either global owner or an owner of the channel token_permission = token_src["permission_id"] if token_permission != 1 and not is_owner(ch_src, token_src): raise AccessError( description="token does not have permission to remove owner.") # Check user is not the only owner if len(ch_src["owner_members"]) == 1: raise InputError(description="user is the only owner remaining.") # Remove user from owner_members owner_remove = { "u_id": u_src["u_id"], "name_first": u_src["name_first"], "name_last": u_src["name_last"], "email": u_src["email"], "handle_str": u_src["handle_str"], "profile_img_url": u_src["profile_img_url"], } ch_src["owner_members"].remove(owner_remove) append_user_stats(u_id, data_store) append_dream_stats(u_id, data_store) writeData(data_store) return {}
def channel_addowner_v1(token, channel_id, u_id): """Given channel id, makes a u_id owner of that channel. Arguments: token (string) - token of an authenticated user who is either an owner of the channel or a global owner channel_id (integer) - id of channel for ownership u_id (integer) - id of user to be granted ownership of channel Exceptions: InputError - Occurs when channel_id is not a valid channel - Occurs when u_id refers to a user who is already an owner AccessError - Occurs when token refers to a user who is not a global owner or an owner of the target channel - Occurs when token is not valid Return value: {} """ # Load .json data store data_store = getData() # Check validity of channel_id ch_index = check_validity(data_store["channels"], channel_id, "channel_id", INVALID_CHANNEL_ID) ch_src = data_store["channels"][ch_index] # Check validity of token token_index = findUser(token) - 1 token_src = data_store["users"][token_index] # Check validity of u_id u_index = check_validity(data_store["users"], u_id, "u_id", INVALID_U_ID) u_src = data_store["users"][u_index] # Check user to be added is not already an owner if is_owner(ch_src, u_src): raise InputError( description="user is already an owner of this channel.") # Check token user is either global owner # or an owner of the channel token_permission = token_src["permission_id"] if token_permission != 1 and not is_owner(ch_src, token_src): raise AccessError( description="token does not have permission to add owner") # Add user to owner_members owner_add = { "u_id": u_src["u_id"], "name_first": u_src["name_first"], "name_last": u_src["name_last"], "email": u_src["email"], "handle_str": u_src["handle_str"], "profile_img_url": u_src["profile_img_url"], } ch_src["owner_members"].append(owner_add) # If user was not originally member of channel, # add them to all_members too if not is_member(ch_src, u_src): ch_src["all_members"].append(owner_add) # Create a notification for the added user user_notification = new_added_notification(token, channel_id, -1, u_id) data_store["user_notifications"].append(user_notification) append_user_stats(u_id, data_store) append_dream_stats(u_id, data_store) writeData(data_store) return {}