Exemple #1
0
def user_profile(token, u_id):
    """
    take a valid token and u_id, return a dictionary as below:
    >>> {
        "user" : {
            'u_id': user['id'],
            'email': user['email'],
            'name_first': user['first_name'],
            'name_last': user['last_name'],
            'handle_str': user['handle'],
        }
    }
    """
    # Ensure token is valid
    # This function below has error handling built inside
    auth_get_current_user_id_from_token(token)

    # Ensure u_id is valid and get the user information from database
    try:
        user = database["users"][u_id]
    except KeyError:
        raise InputError(f"{u_id} is not a valid user id")

    # We don't directly return user from database since password is included
    return {"user": get_user_details(user)}
Exemple #2
0
def channels_listall(token):
    channels = []
    # makes sure the user is valid
    auth_get_current_user_id_from_token(token)

    for channel in database["channels"].values():
        channel_info = get_channel_id_and_name(channel)
        channels.append(channel_info)

    return {"channels": channels}
Exemple #3
0
def users_all(token):
    users = []

    # authenticate user
    auth_get_current_user_id_from_token(token)

    for user in database["users"].values():
        user_info = get_user_details(user)
        users.append(user_info)

    return {"users": users}
Exemple #4
0
def upload_photo():
    data = request.get_json()
    token = data["token"]
    img_url = str(data["img_url"])
    x_start = int(data["x_start"])
    y_start = int(data["y_start"])
    x_end = int(data["x_end"])
    y_end = int(data["y_end"])

    # Crop the image and stop it in user_photos folder
    # Name of image is user_id
    user_profile_crop_image(token, img_url, x_start, y_start, x_end, y_end)
    auth_get_current_user_id_from_token(token)

    return {}
Exemple #5
0
def channels_create(token, name, is_public):
    if len(name) > 20:
        raise InputError(f"{name!r} is more than 20 characters long")

    if name == "":
        name = "new_channel"

    creator_data = auth_get_user_data_from_id(
        auth_get_current_user_id_from_token(token))

    id = database["channels_id_head"]
    new_channel = {
        "id": id,
        "name": name,
        "is_public": is_public,
        "owner_members_id": [creator_data["id"]],
        "all_members_id": [creator_data["id"]],
        "messages": {},
        "standup_queue": [],
        "standup_is_active": False,
        "standup_finish_time": None,
    }
    database["channels"][id] = new_channel
    database["channels_id_head"] += 1

    return {"channel_id": new_channel["id"]}
Exemple #6
0
def channel_details(token, channel_id):
    """
    Return channel_details with formats below:
    >>>
    {
        "name": channel["name"],
        "owner_members": owners,
        "all_members": members,
    }
    """
    current_user_id = auth_get_current_user_id_from_token(token)

    channel = get_channel_from_id(channel_id)

    if current_user_id not in channel["all_members_id"]:
        raise AccessError(
            f"user {current_user_id} not authorized to access this channel")

    # build the information dictionnary

    owners = []
    for ownerid in channel["owner_members_id"]:
        user_data = auth_get_user_data_from_id(ownerid)
        owners.append(formated_user_details_from_user_data(user_data))

    members = []
    for memberid in channel["all_members_id"]:
        user_data = auth_get_user_data_from_id(memberid)
        members.append(formated_user_details_from_user_data(user_data))

    return {
        "name": channel["name"],
        "owner_members": owners,
        "all_members": members,
    }
Exemple #7
0
def standup_send(token, channel_id, message):
    # Check token and chanel_id are valid
    user_id = auth_get_current_user_id_from_token(token)
    channel = get_channel_from_id(channel_id)
    handle = auth_get_user_data_from_id(user_id)["handle"]

    # Check is standup is active
    if channel["standup_is_active"] == False:
        raise InputError("There's no active standup running in this channel")

    # Check is the message is too long
    if len(message) > 1000:
        raise InputError(
            f"No one is going to read a {len(message)} character long message!"
        )

    # Make the user is a memeber of the channel
    if user_id not in channel["all_members_id"]:
        raise AccessError(
            f"user {user_id} needs to be a member of this channel to send a message during standup"
        )

    # Append handle and message tuple to the list in order
    channel["standup_queue"].append((handle, message))

    return {}
Exemple #8
0
def message_send(token, channel_id, message, delay=0):
    """ delay is in seconds """

    user_id = auth_get_current_user_id_from_token(token)
    channel = get_channel_from_id(channel_id)

    if user_id not in channel["all_members_id"]:
        raise AccessError(
            f"user {user_id} needs to be a member of this channel to send a message"
        )

    if len(message) > 1000:
        raise InputError(
            f"No one is going to read a {len(message)} character long message!"
        )

    if delay < 0:
        raise InputError("Time to send the message is in the past")

    if delay == 0:
        message_id = perform_message_send_no_checking(channel, user_id,
                                                      message)
        return {"message_id": message_id}

    timer = Timer(delay, perform_message_send_no_checking,
                  [channel, user_id, message])
    timer.start()
Exemple #9
0
def add_react_information_to_message(token, messages):
    """
    This function is to add a key "is_this_user_reacted" to
    the "reacts" data type in message datatype
    >>> Before adding:
        "messages": [
            1: {
                "message_id": 1,
                "u_id": 1,
                "message": "Hello world",
                "time_created": 1582426789,
                "is_pinned": False,
                "reacts": [
                            { "react_id" : 1,
                                "u_ids": [1, 2, 3, 4],
                            },
                            ]

            }
        },
    ],
    >>> After adding:
            "messages": [
            1: {
                "message_id": 1,
                "u_id": 1,
                "message": "Hello world",
                "time_created": 1582426789,
                "is_pinned": False,
                "reacts": [
                            { "react_id" : 1,
                                "u_ids": [1, 2, 3, 4],
    difference >>>>>>>>>>>>>>> "is_this_user_reacted": True
                            },
                            ]

            }
        },
    ]


    The reason I didn't directly add "is_this_user_reacted"
    to the database is because this value should be change according
    to different user when we output reacts data type. This needs
    us to recognize who is the current user that on the web page.
    I think it is inappropriate to store the current user who
    access to web page in our database since our database serves multiple
    user, but it will be controversial to the front-end interface.
    So I add "is_this_user_reacted" in the process of output messages.
    """
    user_id = auth_get_current_user_id_from_token(token)
    for message in messages:
        for react in message["reacts"]:
            if user_id in react["u_ids"]:
                react["is_this_user_reacted"] = True
            else:
                react["is_this_user_reacted"] = False
Exemple #10
0
def user_profile_setemail(token, email):

    # raises AccessError if token is not active
    u_id = auth_get_current_user_id_from_token(token)

    # raises InputError if email address is illegal
    check_email(email)

    user = database["users"][u_id]
    user["email"] = email
Exemple #11
0
def channels_list(token):
    channels = []
    current_user_id = auth_get_current_user_id_from_token(token)

    for channel in database["channels"].values():
        if current_user_id in channel["all_members_id"]:
            channel_info = get_channel_id_and_name(channel)
            channels.append(channel_info)

    return {"channels": channels}
Exemple #12
0
def channel_join(token, channel_id):
    current_user_id = auth_get_current_user_id_from_token(token)
    channel = get_channel_from_id(channel_id)

    if (not channel["is_public"]
            and database["users"][current_user_id]["is_admin"] is False):
        raise AccessError("Channel is not public")

    if len(channel["all_members_id"]) == 0:
        channel["owner_members_id"].append(current_user_id)

    channel["all_members_id"].append(current_user_id)
Exemple #13
0
def user_profile_setname(token, name_first, name_last):

    # raises AccessError if token is not active
    u_id = auth_get_current_user_id_from_token(token)

    # If user's first name or last name is more than 50 characters
    if not isNameLengthOK(name_first, 1, 50):
        raise InputError(f"{name_first} is illegal")
    if not isNameLengthOK(name_last, 1, 50):
        raise InputError(f"{name_last} is illegal")

    user = database["users"][u_id]
    user["first_name"] = name_first
    user["last_name"] = name_last
Exemple #14
0
def channel_leave(token, channel_id):
    current_user_id = auth_get_current_user_id_from_token(token)
    channel = get_channel_from_id(channel_id)

    if current_user_id not in channel["all_members_id"]:
        raise AccessError("User is not in this channel")

    # If a user is owner of that channel, he should be removed
    # from the owner list when he left the channel
    if current_user_id in channel["owner_members_id"]:
        channel["owner_members_id"].remove(current_user_id)

    # Delete the user's token from that channel
    channel["all_members_id"].remove(current_user_id)
Exemple #15
0
def search(token, query_str):
    user_id = auth_get_current_user_id_from_token(token)

    matching_messages = []
    # loop through every channel in which the user is a member, add add the
    # matching message to the list
    for channel in database["channels"].values():
        if user_id in channel["all_members_id"]:
            add_matching_messages(channel, query_str, matching_messages)

    return {
        "messages": sorted(
            matching_messages, key=lambda message: message["time_created"], reverse=True
        ),
    }
Exemple #16
0
def admin_userpermission_change(token, u_id, permission_id):
    admin_id = auth_get_current_user_id_from_token(token)

    if database["users"][admin_id]["is_admin"] is False:
        raise AccessError(
            "user isn't a flockr owner, cannot change other user's permission"
        )

    if permission_id != 1 and permission_id != 2:
        raise InputError(f"invalid permission id {permission_id}")

    if u_id not in database["users"]:
        raise InputError(f"invalid user id {u_id}")

    database["users"][u_id]["is_admin"] = permission_id == 1
Exemple #17
0
def user_profile_sethandle(token, handle_str):

    # raises AccessError if token is not active
    u_id = auth_get_current_user_id_from_token(token)

    # raises InputError if there is a duplicated handle
    if is_handle_already_used(handle_str):
        raise InputError(f"{handle_str} has been used")

    # raises InputError if handleis not illegal
    if not isNameLengthOK(handle_str, 3, 20):
        raise InputError(f"length of {handle_str} is illegal")

    user = database["users"][u_id]
    user["handle"] = handle_str
Exemple #18
0
def message_pin(token, message_id):
    """Given a message within a channel, mark it as "pinned" to be
    given special display treatment by the frontend"""

    # Check token is valid - error checking in function
    user_id = auth_get_current_user_id_from_token(token)

    # Check message exists in database
    message_exists = False
    for ch in database["channels"].values():
        if message_id in ch["messages"]:
            channel_id_for_message = ch["id"]
            message_exists = True

    if message_exists == False:
        raise InputError

    # Check message is not already pinned
    channel_msg = database["channels"][channel_id_for_message]["messages"][
        message_id]
    if channel_msg["is_pinned"] == True:
        raise InputError(
            f"message with message id {message_id} is already pinned")

    # Check user is in channel within which the message exists
    if (database["users"][user_id]["is_admin"] is False and user_id not in
            database["channels"][channel_id_for_message]["all_members_id"]):
        raise AccessError(
            "user must be part of the channel to remove his/her message (see assumptions.md)"
        )

    # Pin message if user is authorised - i.e. user is either a flockr owner
    # or the owner of the channel
    if (user_id
            in database["channels"][channel_id_for_message]["owner_members_id"]
            or database["users"][user_id]["is_admin"]):
        user_id_is_owner = True
    else:
        user_id_is_owner = False

    for msg in database["channels"][channel_id_for_message]["messages"].values(
    ):
        if msg["message_id"] == message_id and (user_id_is_owner):
            msg["is_pinned"] = True
            return {}

    # User not authorised to pin message
    raise AccessError
Exemple #19
0
def message_react(token, message_id, react_id):

    # Ensure react id is valid
    if not is_react_id_valid(react_id):
        raise InputError("react id is invalid")

    # If token is invalid, the function below will raise AccessError
    user_id = auth_get_current_user_id_from_token(token)

    channels = [
        channel for channel in database["channels"].values()
        if user_id in channel["all_members_id"]
    ]

    # User is not in any channel
    if channels == []:
        raise InputError("user is not in any channel")

    # Ensure there is a message match the message_id
    message = next(
        (message for channel in channels
         for message in channel["messages"].values()
         if message_id == message["message_id"]),
        None,
    )

    if message == None:
        raise InputError("Message_id is invalid")

    # Get the react from message
    react = next(
        (react
         for react in message["reacts"] if react["react_id"] == react_id),
        None)
    # React haven't being created
    if react == None:
        new_react = {
            "react_id": react_id,
            "u_ids": [user_id],
        }
        message["reacts"].append(new_react)
    else:
        # If user has reacted
        if user_id in react["u_ids"]:
            raise InputError("This user has reacted")
        else:
            react["u_ids"].append(user_id)
Exemple #20
0
def message_edit(token, message_id, message):
    """Given a message, update its text with new text. If the new message
    is an empty string, the message is deleted
    """

    # Check token is valid - error checking in function
    user_id = auth_get_current_user_id_from_token(token)

    # Check message exists in database
    message_exists = False
    for ch in database["channels"].values():
        if message_id in ch["messages"]:
            channel_id_for_message = ch["id"]
            message_exists = True

    if message_exists == False:
        raise AccessError

    if (database["users"][user_id]["is_admin"] is False and user_id not in
            database["channels"][channel_id_for_message]["all_members_id"]):
        raise AccessError(
            "user must be part of the channel to edit his/her message (see assumptions.md)"
        )

    # This is done after error checking as input error not allowed in this function
    if message == "":
        message_remove(token, message_id)
        return {}

    if len(message) > 1000:
        raise InputError(
            "message cannot exceed 1000 characters (see assumptions.md)")

    # Edit message if user is authorised, delete if message = ''
    for msg in database["channels"][channel_id_for_message]["messages"].values(
    ):
        if msg["message_id"] == message_id and (
                msg["u_id"] == user_id or
            (user_id in database["channels"][channel_id_for_message]
             ["owner_members_id"]) or
            (database["users"][user_id]["is_admin"])):
            msg["message"] = message
            return {}

    # Unauthorised to edit message
    raise AccessError
Exemple #21
0
def channel_invite(token, channel_id, u_id):
    inviter_user_id = auth_get_current_user_id_from_token(token)

    try:
        database["users"][u_id]
    except KeyError:
        raise InputError(f"{u_id} is an invalid user id")

    channel = get_channel_from_id(channel_id)

    if inviter_user_id not in channel["all_members_id"]:
        raise AccessError(
            f"user {inviter_user_id} not authorized to invite you to this channel"
        )

    if u_id not in channel["all_members_id"]:
        channel["all_members_id"].append(u_id)

    return {}
Exemple #22
0
def channel_removeowner(token, channel_id, u_id):
    """
    When there is only one owner in the channel and this owner is removed,
    there should be another user in this room randomly became the owner.
    """
    # Generate the channel which match the channel_id
    user_who_remove_others_uid = auth_get_current_user_id_from_token(token)

    channel = get_channel_from_id(channel_id)

    if u_id not in channel["owner_members_id"]:
        raise InputError("User is not a owner, can not be removed")

    if (user_who_remove_others_uid not in channel["owner_members_id"]
            and database["users"][user_who_remove_others_uid]["is_admin"] is
            False):
        raise AccessError("User is not authorized")

    if len(channel["all_members_id"]) == 1:
        # If a owner are the only member of a channel, when this owner
        # remove owner himself, he will automatically leave the channel

        # we can't use channel_leave, because we want to remove u_id, not u_id
        # of token (token might be an admin's token)
        channel["all_members_id"].remove(u_id)
        channel["owner_members_id"].remove(u_id)
    else:
        # If the owner is the only owner in the channel
        if len(channel["owner_members_id"]) == 1:

            # Generate a member of channel to become the owner
            owner_iterator = iter(
                [user for user in channel["all_members_id"] if user != u_id])
            next_owner_uid = next(owner_iterator, None)

            # We assume we will always find a member in the channel
            # to become owner since length of channel["all_members_id"]
            # is bigger than 2
            assert next_owner_uid != None
            channel["owner_members_id"].append(next_owner_uid)
            channel["owner_members_id"].remove(u_id)
        else:
            channel["owner_members_id"].remove(u_id)
Exemple #23
0
def standup_start(token, channel_id, length):
    assert length >= 0

    # Check token and chanel_id are valid
    user_id = auth_get_current_user_id_from_token(token)
    channel = get_channel_from_id(channel_id)

    # Check if a standup is already running
    if channel["standup_is_active"] == True:
        raise InputError("There's an active standup running in this channel")

    # Turn the standup on, set the time when it ends
    channel["standup_is_active"] = True
    channel["standup_finish_time"] = round(time() + length)

    # Send all message from the queue after length second
    t = Timer(length, send_message_from_queue, [user_id, channel])
    t.start()

    return {"time_finish": channel["standup_finish_time"]}
Exemple #24
0
def channel_addowner(token, channel_id, u_id):
    channel = get_channel_from_id(channel_id)

    if u_id in channel["owner_members_id"]:
        raise InputError("User is already an owner of the channel")

    # make sure the user adding an owner is allowed to
    adder_id = auth_get_current_user_id_from_token(token)
    if (adder_id not in channel["owner_members_id"]
            and database["users"][adder_id]["is_admin"] is False):
        raise AccessError(
            "User is neither an owner of the channel, nor an admin")

    # make sure target user is valid
    if u_id not in database["users"]:
        raise InputError(f"{u_id} is an invalid user id")

    # the user wasn't nescerally a member of the channel before becoming an owner
    if u_id not in channel["all_members_id"]:
        channel["all_members_id"].append(u_id)

    channel["owner_members_id"].append(u_id)
Exemple #25
0
def user_profile_crop_image(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."""

    # Ensure token is valid - error checking in function
    user_id = auth_get_current_user_id_from_token(token)

    # Returns (filename, headers) if successful
    try:
        urllib_request.urlretrieve(img_url, "static/" + str(user_id) + ".jpg")
    except:
        raise InputError("img_url returns an HTTP status other than 200")

    original_img = Image.open("static/" + str(user_id) + ".jpg")
    original_width, original_height = original_img.size

    # Ensure image in correct format
    if original_img.format != "JPG" and original_img.format != "JPEG":
        raise InputError("Image uploaded is not a JPG")

    # Basic error checking for given coordinates
    if x_start < 0 or y_start < 0 or x_end < x_start or y_end < y_start:
        raise InputError("given dimensions incorrectly formatted")

    # Error checking that coordinates are within original image
    if x_end > original_width or y_end > original_height:
        raise InputError("Dimensions out of bounds from original image")

    cropped = original_img.crop((x_start, y_start, x_end, y_end))
    cropped.save("static/" + str(user_id) + ".jpg")

    database["users"][user_id]["profile_img_url"] = (
        get_host_url() + "static/" + str(user_id) + ".jpg"
    )
    return {}
Exemple #26
0
def message_remove(token, message_id):
    """
    Given a message_id for a message, this message is removed from the channel
    """
    # Check token is valid - error checking in function
    user_id = auth_get_current_user_id_from_token(token)

    # Check message exists in database
    message_exists = False
    for ch in database["channels"].values():
        if message_id in ch["messages"]:
            channel_id_for_message = ch["id"]
            message_exists = True

    if message_exists == False:
        raise InputError

    if (database["users"][user_id]["is_admin"] is False and user_id not in
            database["channels"][channel_id_for_message]["all_members_id"]):
        raise AccessError(
            "user must be part of the channel to remove his/her message (see assumptions.md)"
        )

    # Remove message if user is authorised
    for msg in database["channels"][channel_id_for_message]["messages"].values(
    ):
        if msg["message_id"] == message_id and (
                msg["u_id"] == user_id or
            (user_id in database["channels"][channel_id_for_message]
             ["owner_members_id"]) or database["users"][user_id]["is_admin"]):
            del database["channels"][channel_id_for_message]["messages"][
                message_id]
            return {}

    # Unauthorised to remove message
    raise AccessError
Exemple #27
0
def channel_messages(token, channel_id, start):
    """
    >>> Return:
    {
        "messages": [
                   1: {
                       "message_id": 1,
                       "u_id": 1,
                       "message": "Hello world",
                       "time_created": 1582426789,
                       "is_pinned": False,
                       "reacts": [
                                   { "react_id" : 1,
                                        "u_ids": [1, 2, 3, 4],
                                        "is_this_user_reacted": True
                                   },
                                 ]

                   }
        ],
        "start": start,
        "end": -1 if len(channel_messages) < 50 else start + 50,
    }

    Note that "is_this_user_reacted" is not the data from database
    """
    current_user_id = auth_get_current_user_id_from_token(token)
    channel = get_channel_from_id(channel_id)

    # Authorised user not part of channel
    if current_user_id not in channel["all_members_id"]:
        raise AccessError(
            f"Authorised user ({current_user_id}) not part of channel ({channel_id})"
        )

    # Invalid start:
    #   Negative start index
    #   Start greater than total number of messages in channel
    messages_total = len(channel["messages"])
    if start < 0 or start > messages_total:
        raise InputError("Invalid start value")

    # some unreadable-smart-looking python code to make please my ego. sorted
    # sorts each messages according to the "time_created" key we reverse
    # because we want the newest message first (newer means bigger timestamp)
    # then, [start:start+50] takes a slice of a list. start is included,
    # start+50 is excluded, so it gives exactly 50 elements at most. If
    # start+50 >= len(messages), then python handles everything nicely and just
    # gives back a smaller slice.
    channel_messages = sorted(channel["messages"].values(),
                              key=lambda msg: msg["time_created"],
                              reverse=True)[start:start + 50]

    # Add "is_this_user_reacted" to every reacts in every messages
    add_react_information_to_message(token, channel_messages)

    return {
        "messages": channel_messages,
        "start": start,
        # showing off again...
        "end": -1 if len(channel_messages) < 50 else start + 50,
    }