def standup_send(token, channel_id, message): '''Sending a message to get buffered in the standup queue, assuming a standup is currently active''' data = getData() user_id = getUserFromToken(token) user = data['users'][user_id] right_channel_index = find_channel(channel_id) if right_channel_index is None: raise Value_Error(f"Channel ID: {channel_id} is not a valid channel") right_channel = data['channels_list'][right_channel_index] if len(message) > 1000: raise Value_Error("Message is more than 1000 characters") if right_channel['standup']['finish_time'] < dt.utcnow(): raise Value_Error( 'An active standup is not currently running in this channel') if not inChannel(token, channel_id): raise AccessError( 'The authorised user is not a member of the channel that the message is within' ) message_id = right_channel['standup']['message_id'] msg_id = find_message(message_id) old_message = data['messages'][msg_id]['message'] old_message += str(user['handle']) + ': ' + message + ' ' edit(token, message_id, old_message) # update data after edit data = getData() save(data) return {}
def test_join(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) join(user_info2['token'], 0) data = getData() assert len(data['channels_list'][0]['members']) == 2 # nothing change if join the same ppl join(user_info2['token'], 0) data = getData() assert len(data['channels_list'][0]['members']) == 2 # Channel ID is not a valid channel leave(user_info2['token'], 0) with pytest.raises(Value_Error): join(user_info2['token'], 1) # a private channel can only be joined by invitation create(user_info1['token'], 'lol', False) with pytest.raises(AccessError): join(user_info2['token'], 1) save(clear_data())
def test_message_edit(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "321654", "Frank", "Li") user_info3 = register("*****@*****.**", "456789", "Frank", "Li") create(user_info1['token'], 'lol', True) invite(user_info1['token'], 0, user_info2['u_id']) send(user_info2['token'], 0, "first message") edit(user_info1['token'], 0, "new first message") data = getData() assert (data['messages'][0]['message'] == "new first message") edit(user_info2['token'], 0, "second new first message") data = getData() assert (data['messages'][0]['message'] == "second new first message") # a normal member who doesn't send message wants to edit it with pytest.raises(AccessError): edit(user_info3['token'], 0, "not possible") # a member in channel but not the owner who doesn't send message wants to edit it invite(user_info1['token'], 0, user_info3['u_id']) with pytest.raises(AccessError): edit(user_info3['token'], 0, "not possible") # now member 3 is a admin data = getData() data['users'][2]['permission'] = 2 edit(user_info1['token'], 0, "final message") data = getData() assert (data['messages'][0]['message'] == "final message") save(clear_data())
def test_message_react(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) send(user_info1['token'], 0, "first message") react(user_info1['token'], 0, 1) data = getData() assert len(data['messages'][0]['reacts'][0]['u_ids']) == 1 # sencond user create a channel and send a msg create(user_info2['token'], 'lol', True) send(user_info2['token'], 1, "second message") data = getData() assert len(data['messages']) == 2 # Not a valid message within a channel that the authorised user has joined with pytest.raises(Value_Error): react(user_info1['token'], 1, 1) # Not a valid react_id with pytest.raises(Value_Error): react(user_info2['token'], 1, 0) # Message already contains active react with same react_id react(user_info2['token'], 1, 1) data = getData() assert len(data['messages'][1]['reacts'][0]['u_ids']) == 1 with pytest.raises(Value_Error): react(user_info2['token'], 1, 1) save(clear_data())
def unpin(token, message_id): ''' Given a message within a channel, remove it's mark as unpinned ''' data = getData() user_id = getUserFromToken(token) user_dict = data['users'][user_id] msg_id = find_message(message_id) if msg_id is None: raise Value_Error("Message_Id is not a valid message") msg_dict = data['messages'][msg_id] if user_dict['permission'] is 3: raise Value_Error( "Message with message_id was not sent by the authorised user making this request and The authorised user is not an admin or owner of this channel or the slackr" ) if not inChannel(token, msg_dict['channel_id']): raise AccessError("you need to be in the channel to pin the message") if msg_dict['is_pinned'] is False: raise Value_Error( f"Message with ID message_id: {message_id} already unpinned") msg_dict['is_pinned'] = False save(data) return {}
def pin(token, message_id): ''' Given a message within a channel, mark it as "pinned" to be given special display treatment by the frontend ''' data = getData() user_id = getUserFromToken(token) user_dict = data['users'][user_id] msg_id = find_message(message_id) if msg_id is None: raise Value_Error("Message_Id is not a valid message") msg_dict = data['messages'][msg_id] if user_dict['permission'] == 3: raise Value_Error("The authorised user is not an admin") if not inChannel(token, msg_dict['channel_id']): raise AccessError( "The authorised user is not a member of the channel that the message is within" ) if msg_dict['is_pinned'] is True: raise Value_Error( f"Message with ID message_id: {message_id} already pinned") msg_dict['is_pinned'] = True save(data) return {}
def unreact(token, message_id, react_id): ''' Given a message within a channel the authorised user is part of, remove a "react" to that particular message ''' data = getData() user_id = getUserFromToken(token) user_dict = data['users'][user_id] msg_dict = data['messages'][find_message(message_id)] reacts_list = msg_dict['reacts'] channel_id = msg_dict['channel_id'] '''Check if the user is in this particular channel_id of the message ''' user_channel_list = user_dict['user_channel'] error_flag = 1 for i in user_channel_list: if i['channel_id'] == channel_id: error_flag = 0 if error_flag == 1: raise Value_Error( "Not a valid message within a channel that the authorised user has joined" ) if react_id != 1: raise Value_Error("Not a valid react_id") reacts_list = msg_dict['reacts'] if user_id not in reacts_list[int(react_id) - 1]['u_ids']: raise Value_Error( "Message with ID message_id does not contain an active React with ID react_id" ) reacts_list[int(react_id) - 1]['u_ids'].remove(user_id) save(data) return {}
def search(token, query_str): '''Given a query string, return a collection of message_list that match the query''' if query_str == '': return {'messages': []} data = getData() user_id = getUserFromToken(token) user = data['users'][user_id] search_list = [] for user_channel in user['user_channel']: for message_info in data['messages']: if int(message_info['channel_id']) == int( user_channel['channel_id']) and int( message_info['u_id']) == user_id: if str(query_str) in str(message_info['message']): message_info['time_created'] = message_info[ 'time_created'].replace( tzinfo=timezone.utc).timestamp() for reaction in message_info['reacts']: reaction['is_this_user_reacted'] = ( user_id in reaction['u_ids']) search_list.append(message_info) return {'messages': search_list}
def uploadphoto(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. ''' if urllib.request.urlopen(img_url).getcode() != 200: raise Value_Error("img_url is returns an HTTP status other than 200.") if not img_url.lower().endswith('.jpg'): raise Value_Error("img_url is returns an HTTP status other than 200.") img_addr = './server/user_image/' + str(token) + '.jpg' urllib.request.urlretrieve(img_url, img_addr) imageObject = Image.open(img_addr) width, height = imageObject.size if x_end not in range(width + 1) or y_end not in range( height + 1) or x_start not in range(x_end) or y_start not in range(y_end): os.remove(img_addr) raise Value_Error( "any of x_start, y_start, x_end, y_end are not within the dimensions of the image at the URL." ) cropped = imageObject.crop((x_start, y_start, x_end, y_end)) cropped.save(img_addr) data = getData() user_id = getUserFromToken(token) data['users'][user_id]['profile_img_url'] = '/user_image?file=' + str( token) + '.jpg' save(data) return {}
def login(email, password): """ Given a registered users' email and password and generates a valid token for the user to remain authenticated """ data = getData() len_user = len(data['users']) right_user = None if len_user > 0: len_user -= 1 if re.search(CHECK_EMAIL, email): for user in data['users']: if user['email'] == email: right_user = user if right_user is None: raise Value_Error("Email entered does not belong to a user") if right_user['password'] != str( sha256(password.encode()).hexdigest()): raise Value_Error("Password is not correct") else: raise Value_Error("Email entered is not a valid email") right_user['loggedIn'] = True save(data) return { 'u_id': right_user['u_id'], 'token': generateToken(right_user['u_id']) }
def sendlater(token, channel_id, message, time_sent): ''' Send a message from authorised_user to the channel specified by channel_id automatically at a specified time in the future ''' data = getData() user_id = getUserFromToken(token) user = data['users'][user_id] right_channel_index = find_channel(channel_id) if type(time_sent) != dt: time_sent = dt.utcfromtimestamp(int(time_sent)) if right_channel_index is None: raise Value_Error(f"Channel ID: {channel_id} is not a valid channel") message_id = get_new_message_id() if len(message) > 1000: raise Value_Error("Message is more than 1000 characters") if not inChannel(token, channel_id): raise AccessError( "the authorised user has not joined the channel they are trying to post to" ) now = dt.utcnow() if now > time_sent: raise Value_Error("Time sent is a time in the past") data = add_message(channel_id, message_id, user['u_id'], message, time_sent) save(data) return {'message_id': message_id}
def test_standup_active(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) finish_time = standup_start(user_info1['token'], 0, 1) standup_send(user_info1['token'], 0, "hi") standup_send(user_info1['token'], 0, "lol") data = getData() assert standup_active(user_info1, 0) == { 'is_active': True, 'time_finish': finish_time['time_finish'] } sleep(1) assert standup_active(user_info1, 0) == { 'is_active': False, 'time_finish': finish_time['time_finish'] } # Channel ID: is not a valid channel with pytest.raises(Value_Error): standup_active(user_info1, 1) save(clear_data())
def create(token, name, is_public): ''' Creates a new channel with that name that is either a public or private channel ''' data = getData() user_id = getUserFromToken(token) if user_id not in range(len(data['users'])): raise Value_Error("the token doesn't exist") if len(name) > 20: raise Value_Error("the name is longer than 20 character") user = data['users'][user_id] user_info = user_detail(user_id, user['name_first'], user['name_last'], user['profile_img_url']) ch_id = len(data['channels_list']) data['channels_list'].append({'channel': {'channel_id': ch_id, 'name': name}, 'owners': [user_info], 'members': [user_info], 'is_public': is_public, 'standup': {'finish_time': dt.utcnow(), 'message_id': None}}) user['user_channel'].append({'channel_id': ch_id, 'name': name}) save(data) return {'channel_id': ch_id}
def listall(token): ''' Provide a list of all channels (and their associated details) ''' tmp = list() data = getData() for channel in data['channels_list']: tmp.append(channel['channel']) return {'channels': tmp}
def invite(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 ''' data = getData() right_channel_index = find_channel(channel_id) if right_channel_index is None: raise Value_Error(f"Channel ID: {channel_id} is not a valid channel") right_channel = data['channels_list'][right_channel_index] if int(u_id) >= len(data['users']): raise Value_Error(f"u_id: {u_id} does not refer to a valid user.") if not inChannel(token, channel_id): raise AccessError("the authorised user is not already a member of the channel.") invitee = data['users'][int(u_id)] invitee_info = user_detail(u_id, invitee['name_first'], invitee['name_last'], invitee['profile_img_url']) if invitee_info not in right_channel['members']: right_channel['members'].append(invitee_info) invitee['user_channel'].append(right_channel['channel']) save(data) return {}
def test_message_unreact(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) send(user_info1['token'], 0, "first message in first channel") react(user_info1['token'], 0, 1) data = getData() assert len(data['messages'][0]['reacts'][0]['u_ids']) == 1 # sencond user create a channel and send a msg and react by itself create(user_info2['token'], 'lol', True) send(user_info2['token'], 1, "first message in second channel") react(user_info2['token'], 1, 1) data = getData() assert len(data['messages']) == 2 # Not a valid message within a channel that the authorised user has joined with pytest.raises(Value_Error): unreact(user_info1['token'], 1, 1) # Not a valid react_id with pytest.raises(Value_Error): unreact(user_info2['token'], 1, 0) # Message with ID message_id does not contain an active React with ID react_id send(user_info2['token'], 1, "third message") data = getData() assert len(data['messages'][2]['reacts'][0]['u_ids']) == 0 with pytest.raises(Value_Error): unreact(user_info2['token'], 2, 1) # pass test unreact(user_info2['token'], 1, 1) data = getData() assert len(data['messages'][1]['reacts'][0]['u_ids']) == 0 save(clear_data())
def test_addowner_and_remove(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) join(user_info2['token'], 0) addowner(user_info1['token'], 0, user_info2['u_id']) data = getData() assert len(data['channels_list'][0]['owners']) == 2 removeowner(user_info1['token'], 0, user_info2['u_id']) data = getData() assert len(data['channels_list'][0]['owners']) == 1 # Channel ID is not a valid channel with pytest.raises(Value_Error): addowner(user_info1['token'], 1, user_info2['u_id']) # you don't have the right to access to add owner with pytest.raises(AccessError): addowner(user_info2['token'], 0, user_info2['u_id']) # you don't have the right to access to remove owner with pytest.raises(AccessError): removeowner(user_info2['token'], 0, user_info2['u_id']) # the user id is already an owner addowner(user_info1['token'], 0, user_info2['u_id']) data = getData() assert len(data['channels_list'][0]['owners']) == 2 with pytest.raises(Value_Error): addowner(user_info1['token'], 0, user_info2['u_id']) # Channel ID: is not a valid channel with pytest.raises(Value_Error): removeowner(user_info1['token'], 1, user_info2['u_id']) save(clear_data())
def edit(token, message_id, message): ''' Given a message, update it's text with new text ''' data = getData() user_dict = data['users'][getUserFromToken(token)] msg_dict = data['messages'][find_message(message_id)] if msg_dict['u_id'] != user_dict['u_id'] and user_dict[ 'permission'] != 1 and user_dict['permission'] != 2: raise AccessError("Unauthorised user making edit request") msg_dict['message'] = message save(data) return {}
def standup_start(token, channel_id, length): '''For a given channel, start the standup period whereby for the next 15 minutes if someone calls "standup_send" with a message, it is buffered during the 15 minute window then at the end of the 15 minute window a message will be added to the message queue in the channel from the user who started the standup.''' data = getData() right_channel_index = find_channel(channel_id) now_time = dt.utcnow() if right_channel_index is None: raise Value_Error(f"Channel ID: {channel_id} is not a valid channel") right_channel = data['channels_list'][right_channel_index] if not inChannel(token, channel_id): raise AccessError( 'The authorised user is not a member of the channel that the message is within' ) if standup_active(token, channel_id)['is_active'] is False: time_finish = now_time + timedelta(seconds=int(length)) message = sendlater(token, channel_id, '', time_finish) # update data data = getData() right_channel_index = find_channel(channel_id) right_channel = data['channels_list'][right_channel_index] right_channel['standup']['finish_time'] = time_finish right_channel['standup']['message_id'] = message['message_id'] else: raise Value_Error( 'An active standup is currently running in this channel') save(data) return { 'time_finish': time_finish.replace(tzinfo=timezone.utc).timestamp() }
def test_message_sendlater(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) sent_time_before = dt.utcnow() - timedelta(minutes=15) sent_time_after = dt.utcnow() + timedelta(minutes=15) sendlater(user_info1['token'], 0, "first message", sent_time_after) data = getData() assert (data['messages'][0]['message'] == "first message" and data['messages'][0]['time_created'] == sent_time_after) # Channel ID: is not a valid channel with pytest.raises(Value_Error): sendlater(user_info1['token'], 1, "second message", sent_time_after) # Message is more than 1000 characters with pytest.raises(Value_Error): sendlater(user_info1['token'], 0, "second message" * 500, sent_time_after) # the authorised user has not joined the channel they are trying to post to with pytest.raises(AccessError): sendlater(user_info2['token'], 0, "second message", sent_time_after) # Time sent is a time in the past with pytest.raises(Value_Error): sendlater(user_info1['token'], 0, "second message", sent_time_before) # if the given time type is not datetime sendlater(user_info1['token'], 0, "first message", sent_time_after.replace(tzinfo=timezone.utc).timestamp()) data = getData() assert (data['messages'][0]['message'] == "first message" and data['messages'][0]['time_created'] == sent_time_after) save(clear_data())
def test_message_send(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) send(user_info1['token'], 0, "first message") data = getData() assert data['messages'][0]['message'] == "first message" send(user_info1['token'], 0, "second message") data = getData() assert len(data['messages']) == 2 # Message is more than 1000 characters with pytest.raises(Value_Error): send(user_info1['token'], 0, "second message" * 500) # the authorised user has not joined the channel they are trying to post to with pytest.raises(AccessError): send(user_info2['token'], 0, "second message") save(clear_data())
def test_channel_leave(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) invite(user_info1['token'], 0, user_info2['u_id']) data = getData() assert len(data['channels_list'][0]['members']) == 2 # Channel ID is not a valid channel with pytest.raises(Value_Error): leave(user_info2['token'], 1) leave(user_info2['token'], 0) data = getData() assert len(data['channels_list'][0]['members']) == 1 # leave again, and it should have no change leave(user_info2['token'], 0) data = getData() assert len(data['channels_list'][0]['members']) == 1 save(clear_data())
def test_standup_start_send(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) finish_time = standup_start(user_info1['token'], 0, 10) standup_send(user_info1['token'], 0, "hi") standup_send(user_info1['token'], 0, "lol") data = getData() assert (len(data['messages']) == 1 and data['messages'][0]['time_created'].replace( tzinfo=timezone.utc).timestamp() == finish_time['time_finish']) # An active standup is currently running in this channel with pytest.raises(Value_Error): finish_time = standup_start(user_info1['token'], 0, 10) # Channel ID: is not a valid channel with pytest.raises(Value_Error): finish_time = standup_start(user_info1['token'], 1, 10) # standup_send: Channel ID: is not a valid channel with pytest.raises(Value_Error): standup_send(user_info1['token'], 1, "hi") # standup_send: Message is more than 1000 characters with pytest.raises(Value_Error): standup_send(user_info1['token'], 0, "hi" * 1000) # The authorised user is not a member of the channel that the message is within with pytest.raises(AccessError): finish_time = standup_start(user_info2['token'], 0, 10) # standup_send: The authorised user is not a member of the channel that the message is within with pytest.raises(AccessError): standup_send(user_info2['token'], 0, "hi") save(clear_data()) # standup_send: An active standup is not currently running in this channel user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") create(user_info1['token'], 'lol', True) # The authorised user is not a member of the channel that the message is within with pytest.raises(Value_Error): standup_send(user_info1['token'], 0, "hi") save(clear_data())
def logout(token): ''' Given an active token, invalidates the taken to log the user out. If a valid token is given, and the user is successfully logged out, it returns true, otherwise false. ''' data = getData() user_id = getUserFromToken(token) if user_id in range(len( data['users'])) and data['users'][user_id]['loggedIn'] == True: data['users'][user_id]['loggedIn'] = False save(data) return {'is_success': True} else: return {'is_success': False}
def test_channel_invite(): user_info1 = register("*****@*****.**", "12345678", "minglang", "xie") user_info2 = register("*****@*****.**", "12345678", "minglang", "xie") user_info3 = register("*****@*****.**", "12345678", "Frank", "Li") # channel_id does not refer to a valid channel that the authorised user is part of create(user_info1['token'], 'lol', True) with pytest.raises(Value_Error): invite(user_info1['token'], 2, user_info2['u_id']) # u_id does not refer to a valid user with pytest.raises(Value_Error): invite(user_info1['token'], 0, 4) # AccessError whenthe authorised user is not already a member of the channel with pytest.raises(AccessError): invite(user_info2['token'], 0, user_info1['u_id']) # AccessError whenthe authorised user is not already a member of the channel with pytest.raises(AccessError): invite(user_info3['token'], 0, user_info2['u_id']) invite(user_info1['token'], 0, user_info2['u_id']) data = getData() assert { 'u_id': user_info2['u_id'], 'profile_img_url': '/user_image?file=default.jpg', 'name_first': data['users'][user_info2['u_id']]['name_first'], 'name_last': data['users'][user_info2['u_id']]['name_last'] } in data['channels_list'][0]['members'] invite(user_info1['token'], 0, user_info2['u_id']) data = getData() assert len(data['channels_list'][0]['members']) == 2 save(clear_data())
def profile(token, u_id): ''' For a valid user, returns information about their email, first name, last name, and handle ''' data = getData() if int(u_id) not in range(len(data['users'])): raise Value_Error(f"u_id: {u_id} does not refer to a valid user.") user = data['users'][int(u_id)] return { 'email': user['email'], 'name_first': user['name_first'], 'name_last': user['name_last'], 'handle_str': user['handle'], 'profile_img_url': user['profile_img_url'] }
def all_user(token): ''' return all user ''' data = getData() user_id = getUserFromToken(token) if user_id not in range(len(data['users'])): return all_list = [] for user in data['users']: user_detail = user_info(user['u_id'], user['email'], user['name_first'], user['name_last'], user['handle'], user['profile_img_url']) all_list.append(user_detail) return {'users': all_list}
def sethandle(token, handle): ''' Update the authorised user's handle (i.e. display name) ''' data = getData() user_id = getUserFromToken(token) for user in data['users']: if user['handle'] == handle: raise Value_Error("handle is already used by another user") if len(handle) < 3 or len(handle) > 20: raise Value_Error("handle_str must be between 3 and 20 characters") data['users'][user_id]['handle'] = handle save(data) return {}
def setname(token, name_first, name_last): '''Update the authorised user's first and last name''' data = getData() user_id = getUserFromToken(token) if len(name_first) not in range(1, 51): raise Value_Error( "name_first is not between 1 and 50 characters in length") if len(name_last) not in range(1, 51): raise Value_Error( "name_last is not between 1 and 50 characters in length") data['users'][user_id]['name_first'] = name_first data['users'][user_id]['name_last'] = name_last save(data) return {}
def setemail(token, email): ''' Update the authorised user's email address ''' data = getData() user_id = getUserFromToken(token) for user in data['users']: if user['email'] == email: raise Value_Error( "Email address is already being used by another user") if (re.search(CHECK_EMAIL, email)): data['users'][user_id]['email'] = email else: raise Value_Error("Email entered is not a valid email") save(data) return {}