# pylint: disable=invalid-name
# pylint: disable=redefined-outer-name
"""_summary_

Returns:
    _type_: _description_
"""
import os
from flask_sqlalchemy import SQLAlchemy
from flask_session import Session
from flask import Flask, session
from flask_cors import CORS
from Event.config import App_Config
from flasgger import Swagger
import yaml

# Create an instance of Swagger
swagger = Swagger()

db = SQLAlchemy()


sess = Session()


def create_app():
    """
    Create a new instance of the app with the given configuration.

    :param config_class: configuration class
    :return: app
    """
    # Initialize Flask-
    app = Flask(__name__)
    app.config["SESSION_SQLALCHEMY"] = db
    app.config.from_object(App_Config)
    if app.config["SQLALCHEMY_DATABASE_URI"] == "sqlite:///test.db":
        print("Using test Database")
    # Initialize CORS
    CORS(app, supports_credentials=True)
    # Initialize SQLAlchemy
    db.init_app(app)
    # initialize Flask-Session
    sess.init_app(app)

    # Load Swagger content from the file
    with open('auth_swagger.yaml', 'r') as file:
        swagger_config = yaml.load(file, Loader=yaml.FullLoader)

    # Initialize Flasgger with the loaded Swagger configuration
    Swagger(app, template=swagger_config)

    # register endpoint(blueprints)
    # pylint: disable=import-outside-toplevel
    from Event.user.routes import users
    from Event.auth.routes import auth
    from Event.events.routes import events
    from Event.errors.handlers import error
    from Event.groups.routes import groups
    from Event.comments.routes import comments
    from Event.likes.routes import likes

    app.register_blueprint(users)
    app.register_blueprint(auth)
    app.register_blueprint(events)
    app.register_blueprint(groups)
    app.register_blueprint(error)
    app.register_blueprint(comments)
    app.register_blueprint(likes)

    # create db tables from models if not exists
    with app.app_context():
        db.create_all()

    return app

Event/__init__.py
hngx-org/Spitfire-events-backend
# pylint: disable=cyclic-import
"""_summary_

    Returns:
        _type_: _description_
    """
# pylint: disable=unused-import
import os
import requests
from google.oauth2 import id_token
from google.auth.transport import requests
from google.auth.exceptions import GoogleAuthError
from flask import jsonify, Blueprint, request, session
from Event.models.users import Users
from Event.errors.handlers import CustomError
from Event.utils import query_one_filtered, is_logged_in


auth = Blueprint(
    "auth", __name__, url_prefix="/api/auth"
)  # url_prefix includes /auth before all endpoints in blueprint

CLIENT_ID_1 = os.environ.get("CLIENT_ID_1")
CLIENT_ID_2 = os.environ.get("CLIENT_ID_2")
CLIENT_ID_3 = os.environ.get("CLIENT_ID_3")


# ...

@auth.route("/", methods=["POST"])
def register_or_login():
    """Register and Login route for google authentication"""
    data = request.get_json()

    # lets collect the credential token from request body
    credential_token = data.get("token")
    if not credential_token:
        raise CustomError("Bad Request", 400, "invalid token")

    try:
        # we'll ask google to verify they issued this token,  give us user data
        id_info = id_token.verify_oauth2_token(
            id_token=credential_token, request=requests.Request()
        )
    except GoogleAuthError as error:
        raise CustomError("Bad Request", 400, "invalid token")
    except ValueError:
        raise CustomError("Bad Request", 400, "invalid token")

    # lets check if the token was issued for us
    if id_info["aud"] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]:
        raise CustomError("Forbidden", 403, "Token is of unknown origin")

    user = query_one_filtered(Users, id=id_info["sub"])
    if not user:
        user = Users(
            id=id_info["sub"],
            name=id_info["name"],
            email=id_info["email"],
            avatar=id_info["picture"],
        )
        user.insert()

    # # we have a valid user,lets create a login session
    session["user"] = {"id": user.id}

    return (
        jsonify(
            {
                "message": "success",
                "data": {"id":user.id,
                            "email": user.email,
                            "name": user.name,
                            "avatar": user.avatar,},
            }
        ),
        200,
    )


# pylint: disable=broad-exception-caught
@auth.route("/@me")
def see_sess():
    """
    get the details of current logged in user
    """
    # lets get the user id of the currently loggedin user using is_logged_in helper
    user_id = is_logged_in(session) 
    try:
        user = query_one_filtered(Users, id=user_id)
        return (
            jsonify(
            {
                "message": "success",
                "data": {"id":user.id,
                            "email": user.email,
                            "name": user.name,
                            "avatar": user.avatar,},
            }
        ),
            200,
        )
    except Exception:
        return (
            jsonify(
                {
                    "error": "Internal server error",
                    "message": "It's not you it's us",
                }
            ),
            500,
        )


@auth.route("/logout", methods=["GET", "POST"])
def logout_user():
    """Logout user by removing login session"""
    session.pop("user", None)
    return jsonify({"message": "success"}), 204

Event/auth/routes.py
hngx-org/Spitfire-events-backend
"""Comments route module"""
from flask import jsonify, Blueprint, request, session
from Event.utils import is_logged_in, query_one_filtered
from Event.models.images import Images
from Event.models.comments import Comments

"""_summary_

    Returns:
        _type_: _description_
    """


comments = Blueprint("comments", __name__, url_prefix="/api/comments")


# POST /api/comments/<comment_id>/images: Add an image or images to a comment
# GET /api/comments/<comment_id>/images: Get all images for a comment
@comments.route("/<comment_id>/images", methods=["GET", "POST"])
def add_images(comment_id):
    """Add an image or images to a comment in an event discussion
    Args:
        comment_id (str): The id of the comment in the discussion
    """
    is_logged_in(session)
    if request.method == "POST":
        response = []
        try:
            comment = query_one_filtered(Comments, id=comment_id)
            image_url_list = request.get_json().get("image_url_list")
            for image_url in image_url_list:
                new_image = Images(url=image_url)
                new_image.insert()
                comment.images.append(new_image)
                comment.update()
                response.append(new_image.format())
            return jsonify(
                {
                    "message": "Image saved successfully",
                    "data": response,
                }
            ), 201
        except Exception as error:
            return jsonify(
                    {
                        "message": "Error: image data could not be saved",
                        "error": "Bad Request"
                    }
                ),  400
    # GET images
    try:
        comment = query_one_filtered(Comments, id=comment_id)
        all_images = comment.images
        return jsonify(
            {
                "message": "all images successfully fetched",
                "data": [image.format() for image in all_images]
                if all_images
                else [],
            }
        ), 200
    except Exception as error:
        return jsonify(
                    {
                        "message": "An error occured while fetching all images",
                        "error": "Bad Request"
                    }
            ), 400

Event/comments/routes.py
hngx-org/Spitfire-events-backend
"""
summary
"""
import os
import datetime
from dotenv import load_dotenv


load_dotenv(".env")


# pylint: disable=invalid-name
class App_Config:
    """_summary_"""

    SECRET_KEY = os.environ.get("SECRET_KEY", "test")

    SQLALCHEMY_DATABASE_URI = os.environ.get(
        "SQLALCHEMY_DATABASE_URI", "sqlite:///test.db"
    )
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    SQLALCHEMY_ECHO = False

    SESSION_TYPE = "filesystem" if not os.getenv('PROD', None) else "sqlalchemy"
    SESSION_USE_SIGNER = True
    SESSION_COOKIE_SECURE = False
    SESSION_COOKIE_SAMESITE = "None"
    SESSION_COOKIE_HTTPONLY = False
    PERMANENT_SESSION_LIFETIME = datetime.timedelta(days=30)


    def nothing(self):
        """_summary_"""

    def another(self):
        """_summary_"""

Event/config.py
hngx-org/Spitfire-events-backend
# pylint: disable=cyclic-import
"""_summary_

    Returns:
        _type_: _description_
    """
from flask import Blueprint, jsonify
from Event import db

# pylint: disable=unused-argument
# pylint: disable=redefined-outer-name
error = Blueprint("error", __name__)


class CustomError(Exception):
    """Exception class for custom errors"""

    def __init__(self, error, code, message):
        """constructor for custom error class

        Args:
            error (_type_): Error Name
            code (_type_): HTTP error code
            message (_type_): error message
        """
        self.error = error
        self.code = code
        self.message = message


# pylint: disable=broad-exception-caught
@error.teardown_app_request
def clean_up(exc):
    """_summary_

    Args:
        exc (_type_): _description_
    """
    try:
        db.session.remove()
    except Exception:
        pass


@error.app_errorhandler(CustomError)
def custom_error(error):
    """app error handler for custom errors"""
    return (
        jsonify({"error": error.error, "message": error.message}),
        error.code,
    )


@error.app_errorhandler(400)
def bad_request(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 400


@error.app_errorhandler(404)
def resource_not_found(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 404


@error.app_errorhandler(405)
def method_not_allowed(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return (
        jsonify({"error": error.name, "message": error.description}),
        405,
    )


@error.app_errorhandler(422)
def cant_process(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 422


# pylint: disable=function-redefined
@error.app_errorhandler(429)
def cant_process(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": error.description}), 429


@error.app_errorhandler(500)
def server_error(error):
    """_summary_

    Args:
        error (_type_): _description_

    Returns:
        _type_: _description_
    """
    return jsonify({"error": error.name, "message": "Its not you its us"}), 500

Event/errors/handlers.py
hngx-org/Spitfire-events-backend
# pylint: disable=cyclic-import
"""_summary_
"""
# pylint: disable=unused-import
from flask import Blueprint, request, jsonify, session
from Event.models.images import Images
from Event.models.comments import Comments
from Event.models.events import Events
from Event.models.users import Users
# from Event.models.comment_images import CommentImages
from Event.utils import query_all_filtered, query_all, query_one_filtered, is_logged_in


events = Blueprint("events", __name__, url_prefix="/api/events")

# checked
# POST /api/events: Create a new event
@events.route("/", methods=["POST"])
def create_event():
    """
    Create a new event.

    This function handles a POST request to create a new event. It extracts the necessary data from the request's JSON payload, converts the date and time strings to datetime objects, creates an instance of the `Events` model with the extracted data, inserts the event into the database, and returns a JSON response with the created event.

    :return: A JSON response with the following fields:
             - `msg` (string): A message indicating the success of the event creation.
             - `event` (string): A string representation of the created event.
    """
    # destructure the request dict to kwargs
    user_id = is_logged_in(session)
    try:
        data = request.get_json()
        data['creator_id'] = user_id
        thumbnail=data.get("thumbnail")
        data.pop("thumbnail")
        event = Events(**data)
        event.insert()
        result = event.format()
    
        new_image = Images(url=thumbnail)
        new_image.insert()
        event.thumbnail.append(new_image)
        event.update()
    except Exception as e:
        return jsonify({
                        "error": "Bad Request",
            "message": "An error occurred creating the event.", 
        }), 400
    return jsonify(
        {
        'message': "Event Created",
        'data': result
    }
    ), 201

# to check later
# DELETE /api/events/:eventId: Delete an event
@events.route("/<string:event_id>", methods=["DELETE"])
def delete_event(event_id):
    """
    Delete an event.

    Args:
        id (str): The id of the event to be deleted.

    Returns:
        If the event is successfully deleted, a success response with status code 204 and a JSON body indicating the event was deleted.
        If the event does not exist, a not found error response with status code 404 and a JSON body indicating the event was not found.
    """

    user_id = is_logged_in(session)
    try:
        user = query_one_filtered(Users, id=user_id)
        del_event = query_one_filtered(Events, id=event_id)
        print(del_event)
        if not del_event:
            return (
                jsonify({"Error": "Not Found", "message": "Event not found"}),
                404,
            )
        # check if logged in user is the creator
        if user.id != del_event.creator_id:
            return (
                jsonify(
                    {
                        "Error": "Not Authorized", 
                        "message": "Only the creator can delete group"
                    }
                ),
                403
            )
        del_event.delete()
        return jsonify(
            {
                "Message": "Event deleted",
                "data": []
            }
        ), 204
    except Exception as error:
        return jsonify(
            {
                "Error": "Bad request",
                "message": "something went wrong"                
            }
        ), 400


# checked
# GET /api/events: Get a list of events
@events.route("/", methods=["GET"])
def all_events():
    """
    Retrieves all events from the database and returns them as a JSON response.

    Returns:
        json: A JSON response containing all events created.
    """
    try:
        all_events = query_all(Events)
        #sort all events by updated_at
        all_events.sort(key=lambda element: element.updated_at, reverse=True)
    except Exception:
        return jsonify(
            {
                "error": "Bad Request",
                "message": "something went wrong"
                }
                ), 400
    
    return jsonify(
        {
            "message": "events returned succesfully", 
            "data": [event.format() for event in all_events] if all_events else []
        }
        ), 200

# Checked  
# Get events based on event id
@events.route("/<string:event_id>", methods=["GET"])
def get_event(event_id):
    """
    Get event based on its ID.

    Args:
        event_id (str): The ID of the event to retrieve.

    Returns:
        tuple: A tuple with the response message and status code.

    Example Usage:
        GET /events/123

    This code snippet demonstrates how to make a GET request to retrieve the event with ID 123.
    The expected output is a JSON response containing the event details if it exists, or an error message if the event is not found.
    """
    is_logged_in(session)
    try:
        event = query_one_filtered(table=Events, id=event_id)
        if event:
            return jsonify(
                {
                    "message": "Event successfully Found", 
                    "data": event.format()
                }
            ), 200
        return jsonify(
        {
        "error": "Not Found",
        "message": "Event Not Found"
        }
        ), 404


    except Exception as error:
        return jsonify(
            {
                "error": "Bad Request",
                "message": "something went wrong"
                }
                ), 400

# checked
# PUT /api/events/:eventId: Update event details
@events.route("/<string:event_id>", methods=["PUT"])
def update_event(event_id: str) -> tuple:
    """
    Updates an event in the database based on the provided event ID and request data.

    Args:
        event_id (str): The ID of the event to be updated.

    Returns:
        tuple: A JSON response with a message and a status code.

    Raises:
        Exception: If an error occurs during the update process.
    """
    is_logged_in(session)
    try:
        req = request.get_json()
        db_data = query_one_filtered(Events, id=event_id)
        if not db_data:
            return jsonify(
                {
                    "error": "Not Found",
                    "message": "Event not Found",
                    }
                    ), 404
        
            
        for k, v in req.items():
            if k == 'creator_id' or k == 'created_at':
                continue
            setattr(db_data, k, v)
        db_data.update()
        return jsonify(
            {
            "message": "item updated",
            # "Event_id": event_id,
            "data": db_data.format()
            }
            ), 201
    except Exception as exc:
        return jsonify(
            {"error": "Bad Request",
             "message":"Something Went Wrong"
            }
    ), 400

# checked
@events.route("/<string:event_id>/comments", methods=["GET", "POST"])
def add_comments(event_id: str):
    """
    Add a comment to an event discussion or fetch all comments for an event.

    Args:
        event_id (str): The ID of the event causing the discussion.

    Returns:
        For POST requests:
            dict: A JSON response with the status, message, and data containing the newly created comment ID and body.
        For GET requests:
            dict: A JSON response with the status, message, and data containing a list of all comments associated with the event.
    """
    user_id = is_logged_in(session)
    if request.method == "POST":
        try:
            data = request.get_json()
            body = data.get("body")
            image_url_list = data.get("image_url_list", None)
            new_comment = Comments(event_id=event_id, user_id=user_id,
                                   body=body)
            new_comment.insert()
            # save images if they exist
            if image_url_list is not None:
                for image_url in image_url_list:
                    new_image = Images(url=image_url)
                    try:
                        new_image.insert()
                        # comment_image = CommentImages(comment_id=new_comment.id,
                        #                               image_id=new_image.id)
                        # comment_image.insert()
                        new_comment.images.append(new_image)
                        new_comment.update()
                    except Exception as error:
                        return jsonify(
                            {
                                "message": "Failed to save to database",
                                "error": "Bad Request"
                            }
                        ), 400

            return jsonify(
                {
                    "message": "Comment saved successfully",
                    "data": {"id": new_comment.id, "body": new_comment.body},
                }
            ), 201
        except Exception as error:
            return jsonify(
                    {
                        "message": "Comment data could not be saved",
                        "error": "Bad Request"
                    }
                ), 400
            

    # GET comments
    try:
        all_comments = query_all_filtered(Comments, event_id=event_id)
        #sort all_comments by creation date
        all_comments.sort(key=lambda element: element.created_at, reverse=False)
        if not all_comments:
            return jsonify(
                {"status": "failed", 
                 "message": "Comments not found"
                 }
                 ), 404
        # if found
        return jsonify(
            {
                "message": "comments fetched successfully",
                "data": [comment.format() for comment in all_comments]
                if all_comments
                else [],
            }
        ), 200
    except Exception as error:
        return (
            jsonify(
                {
                    "error": "Bad Request",
                    "message": "An error occured while fetching all comments",
                }
            ),
            400,
        )

Event/events/routes.py
hngx-org/Spitfire-events-backend
"""
Module for removing user from a group.
"""

from flask import Blueprint, jsonify, request, session
from Event.models.users import Users
from Event.models.groups import Groups
from Event import db
from Event.utils import query_one_filtered, is_logged_in


groups = Blueprint("groups", __name__, url_prefix="/api/groups")

@groups.route("/<string:group_id>/members/<string:new_member_id>",methods=["POST"])
def add_user_to_group(group_id, new_member_id):
    is_logged_in(session)
    try:
        group = query_one_filtered(Groups,id=group_id)
        # user = query_one_filtered(Users,id=user_id)
        new_member = query_one_filtered(Users,id=new_member_id)

        # Check if the group and user exist
        if group is None or new_member is None:
            return jsonify(
                {
                    "message": f"Group or user not found",
                    "error": "Not Found"
                }
            ), 404

        newgroup=new_member.user_groups
        if group.id in [group.id for group in newgroup]:
            return jsonify(
                {
                    "error":"Forbidden",
                    "message":"User already in group"
                    }
                    ),403
        newgroup.append(group)

        new_member.user_groups=newgroup
        new_member.update()
        
        return jsonify(
            {
                 "data": group.id,
                   "message": "User added to Group"
                   }
                   ), 201
    except Exception as e:
        return jsonify(
            {"error": "Bad Request",
             "message":"Something went wrong with the request",
             }
             ), 400


@groups.route("/<string:group_id>", methods=["GET"])
def get_group_by_id(group_id):
    """
    Get details of a group by its group ID.

    Args:
        groupId (str): The ID of the group to fetch.

    Returns:
        dict: A JSON response with group details.
    """
    is_logged_in(session)
    try:
        group = query_one_filtered(Groups,id=group_id)

        if group:
            # Create a dictionary with group details
            group_details = {"id": group.id, "title": group.title}
            return jsonify(
                {
                    "message": "Group details successfully fetched",
                    "data": group_details,
                }
            ), 200
        else:
            return (
                jsonify(
                    {
                        "error": "Not Found",
                        "message": f"Group not found",
                    }
                ),
                404,
            )
    except Exception as e:
        return jsonify(
            {
                "error": "Bad Request",
                "message": "An error occurred while fetching group details",
            }
            ), 400


@groups.route("/<string:group_id>", methods=["PUT"])
def update_group(group_id):
    """
    Update an existing group.

    This route allows updating an existing group's title by providing
    the group_id in the URL.
    The 'title' parameter must be included in the JSON request body to
    specify the updated title.

    Args:
        group_id (int): The unique identifier for the group to be updated.

    Returns:
        json: A JSON object containing the updated group information
        and a success message.

    Raises:
        400 Bad Request: If the 'title' parameter is missing in the request.
        404 Not Found: If the group with the provided group_id is not found.
        400 Internal Server Error: If any server error occurs
        during the update process.
    """
    is_logged_in(session)
    try:
        data = request.get_json()
        if "title" not in data:
            return jsonify(
                {
                    "error": "Bad Request",
                    "message": "Something is wrong with the request data"
                    }
                    ), 400

        group = query_one_filtered(Groups, id=group_id)

        if not group:
            return jsonify(
                {
                    "error": "Not Found",
                    "message": f"Group with ID {group_id} not found"
                    }
                    ), 404

        group.title = data["title"]

        group.update()

        return jsonify(
                {
                    "message": "Group updated successfully",
                    "data": group.format(),
                }
            ), 201

    except Exception as error:  # pylint: disable=broad-except
        return jsonify(
            {
                "error": "Bad Request",
                "message": " Something went wrong"
                }
                ), 400


# Define the route to remove a user from a group
@groups.route("/<string:group_id>/members/<string:member_id>", methods=["DELETE"])
def remove_user_from_group(group_id, member_id):
    """
    Remove a user from a group.

    Parameters:
    group_id (str): The ID of the group.
    user_id (str): The ID of the user to be removed from the group.

    Returns:
    tuple: A tuple containing response message and status code.
    """
    is_logged_in(session)
    try:
        # Check if the group and user exist in the database
        group = query_one_filtered(Groups,id=group_id)
        member = query_one_filtered(Users,id=member_id)

        if group is None or member is None:
            return jsonify(
                {
                    "message": "Group or user not found",
                    "error":"Not Found"
                    }
                    ), 404

        user_groups=member.user_groups
        if group_id not in [group.id for group in user_groups ]:
            return jsonify(
                {
                    "error":"forbidden",
                    "message":"User is not a member of this group"
                    }
                    ),403

        for key,group in enumerate(member.user_groups):
            if group_id==group.id:
                user_groups.pop(key)
        member.user_groups=user_groups
        member.update()

        return jsonify(
            {
                "message": "User removed from group successfully",
                "data":group_id}
                ), 204

    except Exception as e:
        # Handle any potential errors
        return jsonify(
            {
                "error": "Bad Request",
                "message": "Something went wrong"
                }
                ), 400


@groups.route("/create", methods=["POST"])
def create_group():
    """
    Create a new group.

    This route expects a JSON request containing a 'title' key
    to create a new group.
    The 'title' is used to create a new group instance and
    insert it into the database.

    Returns:
        JSON response with information about the created
        group or an error message.
    """
    is_logged_in(session)
    try:
        # Attempt to extract JSON data from the incoming request.
        data = request.get_json()

        # Check if the 'title' key is present in the JSON data.
        if "title" not in data:
            return jsonify(
                {"error": "Bad Request",
                 "message": "Missing 'title' in request",
                 }
                 ), 400

        # Extract the 'title' from the JSON data.
        title = data["title"]

        # Create a new group instance with the provided 'title'.
        new_group = Groups(title=title)

        # Insert the new group into the database.
        new_group.insert()

        # Return a JSON response indicating successful group creation.
        return jsonify(
                {
                    "message": "Group created successfully",
                    "data": new_group.format(),
                }
            ), 201

    # Handle exceptions and return an error response if any occur.
    except Exception as error:
        return jsonify(
            {
                "message": "group creation failed",
                  "error": "Bad Request"
                }
                ), 400

      
@groups.route("/<string:group_id>", methods=["DELETE"])
def delete_group(group_id):
    """
    Delete a group by its ID.

    Parameters:
    group_id (int): The ID of the group to be deleted.

    Returns:
    tuple: A tuple containing response message and status code.
    """
    is_logged_in(session)
    try:
        # Retrieve the group from the database
        group = query_one_filtered(Groups,id=group_id)

        # Check if the group exists
        if group is None:
            return jsonify(
                {
                    "error": "Not found",
                    "message":"Group not found"
                 }
                 ), 404

        # Delete the group from the database
        group.delete()

        return jsonify(
            {
                "message": "Group deleted successfully",
                "data": "No Content",
             }
             ),204

    except Exception as e:

        # Handle any exceptions that may occur during deletion
        return jsonify(
            {
                "error": "Bad Request",
                "Message": "Something went wrong with this request"
                }
                ), 400

# Get all groups available
@groups.route("/", methods=["GET"])
def get_all_groups():
    """
    Get all groups.

    Returns:
        JSON response with a list of group details.
    """
    # is_logged_in(session)
    try:
        # Query the database to retrieve all groups
        all_groups = Groups.query.all()
        #sort all groups by updated_at
        all_groups.sort(key=lambda x: x.updated_at, reverse=True)

        # Format the groups as a list of dictionaries
        group_list = [
            {   
                "id": group.id,
                "created_at": group.created_at,
                "id" : group.id,
                "title": group.title,
                "updated_at": group.updated_at,
            }
            for group in all_groups
        ]

        return jsonify(
            {
                "message": "All groups successfully fetched",
                "data": group_list,
            }
        ), 200

    except Exception as e:
        return jsonify(
            {
                "error": "Bad Request",
                "message": "An error occurred while fetching all groups",
            }
        ), 400

Event/groups/routes.py
hngx-org/Spitfire-events-backend
"""
Module containing user-related routes for the Events-App, Team Spitfire.
"""

from flask import Blueprint, session, jsonify
from Event.models.users import Users, likes
from Event.models.comments import Comments
from Event.utils import is_logged_in, query_one_filtered

likes = Blueprint("likes", __name__, url_prefix="/api/likes")

@likes.route("/<string:comment_id>",
             methods=["GET"],
             )
def number_of_likes(comment_id):
    """
    Retrieves the number of likes for a given comment.

    Args:
        comment_id (string): The ID of the comment for which to retrieve the number of likes.

    Returns:
        JSON response: A JSON response containing the number of likes for the given comment.

    Example Usage:
        GET /likes/12345
        Input: comment_id = "12345"
        Output: 
        {
          "message": "Number of likes",
          "data": 5
        }
    """
    is_logged_in(session)
    try:
        comment = query_one_filtered(Comments, id=comment_id)
        if not comment:
            return jsonify(
                {
                    "error": "Not Found",
                    "message": "Event Not Found",
                }
            ), 404
        number_of_likes = len(comment.user_likes)
        return jsonify(
            {
                "message": "Number of likes", 
                "data": f"{number_of_likes}"
            }
        ), 200
    
    except Exception as exc:
        return jsonify(
            {
                "error": "Forbidden",
                "message": "you are not allowed to perform such actions",
            }
        ), 403





@likes.route("/<string:comment_id>",
             methods=["POST"],
             )
def like_and_unlike_comment(comment_id):
    """
    Like or unlike a comment.

    Args:
        comment_id (str): The ID of the comment to be liked or unliked.

    Returns:
        dict: A success response with the message "liked/unliked" and the updated number of likes.
              An error response if the user is not logged in or if the comment or user object is not found.
    """

    user_id = is_logged_in(session)  


    #THIS GETS THE COMMENT OBJECT    
    try:
        user = query_one_filtered(Users, id=user_id)
        comment = query_one_filtered(Comments, id=comment_id)
        if not comment or not user:
            return jsonify(
                {
                    "error": "Not Found",
                    "message": "Event Not Found",
                }
            ), 404
        if comment in user.likes:
            user.likes.remove(comment)
            user.update()
            return jsonify(
                {
                    "message": "unliked",
                    "data":  len(comment.user_likes)
                }
            ), 200

        # FOR LIKES SCENARIO
        user.likes.append(comment)
        user.update()
        return jsonify(
                {
                    "message": "liked", 
                    "data":  len(comment.user_likes),
                    }
                    ), 200
    
    except Exception as exc:
        return jsonify(
            {
                "error": "Forbidden",
                "message": "you are not allowed to perform such actions",
            }
        ), 403

Event/likes/routes.py
hngx-org/Spitfire-events-backend
#!/usr/bin/env python3
"""
Base template for the Event driven application
"""
from Event import db
from uuid import uuid4
from datetime import datetime


def get_uuid():
    """Generate a unique id using uuid4()"""
    return uuid4().hex


# Create a base model class that will contain common functionality
class BaseModel(db.Model):
    """BaseClass for all models"""

    # Make this class abstract so it won't be mapped to a database table
    __abstract__ = True

    # Define a primary key column with a default value of a generated UUID
    id = db.Column(db.String(60), primary_key=True, default=get_uuid)
    created_at = db.Column(db.DateTime(), default=datetime.utcnow, nullable=False)
    updated_at = db.Column(db.DateTime(), default=datetime.utcnow, nullable=False)

    def insert(self):
        """Insert the current object into the database"""
        db.session.add(self)
        db.session.commit()

    def update(self):
        """Update the current object in the database"""
        self.updated_at = datetime.utcnow()
        db.session.commit()

    def delete(self):
        """Delete the current object from the database"""
        db.session.delete(self)
        db.session.commit()

    def format(self):
        """Format the object's attributes as a dictionary"""
        # This method should be overridden in subclasses
        raise NotImplementedError(
            "Subclasses must implement the 'format' method"
        )

Event/models/base_model.py
hngx-org/Spitfire-events-backend
# #!/usr/bin/env python3
# """Template for the CommentImages Class"""

# from Event import db
# from Event.models.base_model import BaseModel


# class CommentImages(db.Model):
#     """CommentImages class"""
#     __tablename__ = "comment_images"

#     comment_id = db.Column(db.String(60), db.ForeignKey("comments.id"),
#                            primary_key=True, nullable=False)
#     image_id = db.Column(db.String(60), db.ForeignKey("images.id"),
#                          primary_key=True, nullable=False)

#     def __init__(self, comment_id, image_id):
#         """_summary_

#         Args:
#             comment_id (_type_): _description_
#             image_id (_type_): _description_
#         """
#         self.comment_id = comment_id
#         self.image_id = image_id

#     def insert(self):
#         """Insert the current object into the database"""
#         db.session.add(self)
#         db.session.commit()

#     def update(self):
#         """Update the current object in the database"""
#         db.session.commit()

#     def delete(self):
#         """Delete the current object from the database"""
#         db.session.delete(self)
#         db.session.commit()


#     def __repr__(self):
#         """
#         Return a string representation of the CommentImages object.
#         This method is automatically called when the object is printed or
#         when the `repr()` function is used. It returns a formatted string
#         containing the CommentImages object's identifier.

#         Returns:
#             str: A string representation of the CommentImages object.
#         """
#         return "id: {}, comment_id: {}, image_id: {}".format(self.id, self.comment_id, self.image_id)

#     def format(self):
#         """
#         Return a dictionary representation of the CommentImages object.
#         This method is automatically called when the object is printed or
#         when the `format()` function is used. It returns a dictionary
#         containing the CommentImages object's attributes.
#         """
#         return {
#             "comment_id": self.comment_id,
#             "image_id": self.image_id
#         }
    
# comment_images = db.Table('comment_images',
#     comment_id = db.Column(db.String(60), db.ForeignKey("comments.id"), primary_key=True, nullable=False),
#     image_id = db.Column(db.String(60), db.ForeignKey("images.id"), primary_key=True, nullable=False)
# )
Event/models/comment_images.py
hngx-org/Spitfire-events-backend
#!/usr/bin/env python3
"""Template for the Comment Class"""

from Event import db
from Event.models.base_model import BaseModel


# Association table between Comments and Images
comment_images = db.Table('comment_images',
    db.Column('comment_id', db.String(60), db.ForeignKey("comments.id"), primary_key=True,nullable=False),
    db.Column('image_id', db.String(60), db.ForeignKey("images.id"), primary_key=True, nullable=False)
)

class Comments(BaseModel):
    """
    Model schema for the comments in the events section
    Attributes:
            id (str):
                Primary key for the table
            event_id (str):
                Foreign key for the event table
            user_id (str):
                Foreign key for the user table
            body (str):
                The comment body
            image (str):
                The image associated with the comment
            event (Event):
                The relationship to the event table
            user (User):
                The relationship to the user table
    Methods:
            __init__(self,event_id,user_id, body, image ):
              Constructor for the EventComment class.
            __repr__(self):
              Representation of the EventComment class.
            insert(self): Inserts a new EventComment object into the db.
            update(self): Updates an existing EventComment object in the db.
            delete(self): Deletes an existing EventComment object from the db.
            format(self): Returns a dict repr of the EventComment obj.


        Examples:
            comment = Comments(event_id=1, user_id=1,
                               body="This is a comment",
                               image="https://www.google.com")
    """

    __tablename__ = "comments"

    # Define columns for the Users table
    event_id = db.Column(db.String(60), db.ForeignKey("events.id"), nullable=False)
    user_id = db.Column(db.String(60), db.ForeignKey("users.id"), nullable=False)
    body = db.Column(db.String(1000), nullable=False)

    # Add relationships to Events and Users models

    # event = db.relationship("Events", backref=db.backref("comments", lazy=True))
    # user = db.relationship("Users", backref=db.backref("comments", lazy=True))
    images = db.relationship("Images", backref="comments",
                             secondary=comment_images, lazy="dynamic",
                             cascade="all, delete")

    def __init__(self, event_id, user_id, body):
        """Initialize the Comment object"""
        self.body = body
        self.event_id = event_id
        self.user_id = user_id

    def __repr__(self):
        """Return a string representation of the Comment object"""
        return "event_id: {}, user_id: {}, body: {}, created_at: {}, updated_at: {}".format(
            self.event_id, self.user_id, self.body, self.created_at, self.updated_at
        )

    def format(self):
        """Return a dictionary representation of the Comment object"""
        return {
            "id": self.id,
            "event_id": self.event_id,
            "user_id": self.user_id,
            "body": self.body,
            "created_at": format(self.created_at),
            "updated_at": format(self.updated_at),
            # "images": [image.format() for image in self.images]
        }

Event/models/comments.py
hngx-org/Spitfire-events-backend
# #!/usr/bin/env python3
# """Template for the EventThumbnail Class"""

# from Event import db
# # from Event.models.base_model import BaseModel




# class EventThumbnail(db.Model):
#     """
#         Model Schema for EventThumbnail.

#     """
#     __tablename__ = "event_thumbnails"

#     image_id = db.Column(db.String(60), db.ForeignKey("images.id"), primary_key=True, nullable=False)
#     event_id = db.Column(db.String(60), db.ForeignKey("events.id"), primary_key=True, nullable=False)

#     def __init__(self, image_id, event_id):
#         """_summary_

#         Args:
#             comment_id (_type_): _description_
#             event_id (_type_): _description_
#         """
#         self.image_id = image_id
#         self.event_id = event_id

#     def insert(self):
#         """Insert the current object into the database"""
#         db.session.add(self)
#         db.session.commit()

#     def update(self):
#         """Update the current object in the database"""
#         db.session.commit()

#     def delete(self):
#         """Delete the current object from the database"""
#         db.session.delete(self)
#         db.session.commit()

#     def __repr__(self):
#         """
#         Return a string representation of the EventThumbnail object.
#         This method is automatically called when the object is printed or
#         when the `repr()` function is used. It returns a formatted string
#         containing the EventThumbnail object's identifier.
#         """
#         return "id: {}, image_id: {}, event_id: {}".format(self.id, self.image_id, self.event_id)
Event/models/event_thumbnail.py
hngx-org/Spitfire-events-backend
#!/usr/bin/env python3
"""Base Template for the events class"""
from Event import db
from Event.models.base_model import BaseModel
from datetime import datetime


# Association table between Events and Images
event_thumbnail = db.Table('event_thumbnail',
    db.Column('image_id', db.String(60), db.ForeignKey("images.id"), primary_key=True, nullable=False),
    db.Column('event_id', db.String(60), db.ForeignKey("events.id"), primary_key=True, nullable=False)
)


class Events(BaseModel):
    """_summary_

    Args:
        db (_type_): _description_

    Returns:
        _type_: _description_
    """

    __tablename__ = "events"

    # Define columns for the Events table
    title = db.Column(db.String(60), unique=True, nullable=False)
    description = db.Column(db.String(225), nullable=False)
    creator_id = db.Column(
        db.String(60), db.ForeignKey("users.id"), nullable=False
    )
    location = db.Column(db.String(1024), nullable=False)
    start_date = db.Column(db.Date(), nullable=False)
    start_time = db.Column(db.Time(), nullable=False)
    end_date = db.Column(db.Date(), nullable=False)
    end_time = db.Column(db.Time(), nullable=False)
    comments = db.relationship("Comments", backref=db.backref("event", lazy=True), 
                                cascade="all, delete-orphan")
    thumbnail = db.relationship("Images", secondary=event_thumbnail,
                                backref=db.backref("event", lazy=True), lazy="subquery")


    def __init__(
        self,
        title,
        description,
        creator_id,
        location,
        start_date,
        start_time,
        end_date,
        end_time,
    ):
        """_summary_

        Args:
            title (_type_): _description_
            description (_type_): _description_
            location (_type_): _description_
            start_date (_type_): _description_
            start_time (_type_): _description_
            end_date (_type_): _description_
            end_time (_type_): _description_
        """
        self.title = title
        self.description = description
        self.creator_id = creator_id
        self.location = location
        self.start_date = start_date
        self.start_time = start_time
        self.end_date = end_date
        self.end_time = end_time

    def __repr__(self):
        """Return a string representation of the Event object"""
        return (
            "Title: {}, Description: {}, Creator Id: {}, Location: {}, "
            "Start Date: {}, Start Time: {}, "
            "End Date: {}, End Time: {}"
        ).format(
            self.title,
            self.description,
            self.creator_id,
            self.location,
            self.start_date,
            self.start_time,
            self.end_date,
            self.end_time,
        )

    # Override the format method to return event attributes as a dictionary
    def format(self):
        """Return a dictionary representation of the Event object"""
        return {
            "id": self.id,
            "title": self.title,
            "description": self.description,
            "creator_id": self.creator_id if self.creator_id else "",
            "location": self.location,
            "start_date": format(self.start_date),
            "start_time": format(self.start_time),
            "end_date": format(self.end_date),
            "end_time": format(self.end_time),
            "created_at": format(self.created_at),
            "updated_at": format(self.updated_at)
        }

Event/models/events.py
hngx-org/Spitfire-events-backend
# #!/usr/bin/env python3
# """
# This the base template for GrouopEvents object instances.
# """
# from Event import db
# from Event.models.base_model import BaseModel


# class GroupEvents(BaseModel):
#     """
#     Model Schema for GrproupEvents.

#     Attributes:
#         group_id (str):
#             Foreign key for the group table.
#         event_id (str):
#             Foreign key for the event table.
#     """

#     __tablename__ = "group_events"
#     group_id = db.Column(
#         db.String(120), db.ForeignKey("groups.id"), nullable=False
#     )
#     event_id = db.Column(
#         db.String(120), db.ForeignKey("events.id"), nullable=False
#     )

#     def __init__(self, group_id, event_id):
#         self.group_id = group_id
#         self.event_id = event_id

#     def format(self):
#         """Return a dictionary representation of the Group object"""
#         return {"id":self.id if self.id else "","group_id": self.group_id, "event_id": self.event_id}

Event/models/group_events.py
hngx-org/Spitfire-events-backend
# #!/usr/bin/env python3
# """Template for the GroupImage Class"""

# from Event import db
# from Event.models.base_model import BaseModel

# class GroupImage(BaseModel):
#     """Group Image Class"""

#     __tablename__ = "group_images"

#     group_id = db.Column(db.String(128), db.ForeignKey("groups.id"), nullable=False)
#     image_id = db.Column(db.String(128), db.ForeignKey("images.id"), nullable=False)

#     def __init__(self, group_id, image_id):
#         self.group_id = group_id
#         self.image_id = image_id

#     def __repr__(self):
#         """
#         Return a string representation of the GroupImage object.
#         This method is automatically called when the object is printed or
#         when the `repr()` function is used. It returns a formatted string
#         containing the GroupImage object's identifier.

#         Returns:
#             str: A string representation of the GroupImage object.
#         """
#         return "id: {}, group_id: {}, image_id: {}".format(self.id, self.group_id, self.image_id)

#     def format(self):
#         """
#         Return a dictionary representation of the GroupImage object.
#         This method is automatically called when the object is printed or
#         when the `format()` function is used. It returns a dictionary
#         containing the GroupImage object's attributes.
#         """
#         return {
#             "id":self.id if self.id else "",
#             "group_id": self.group_id,
#             "image_id": self.image_id
#         }


Event/models/group_image.py
hngx-org/Spitfire-events-backend
#!/usr/bin/env python3
"""Template for the Groups Class"""

from Event import db
from Event.models.base_model import BaseModel

# Association table between groups and events
group_events = db.Table('group_events',
    db.Column('group_id', db.String(60), db.ForeignKey("groups.id"), primary_key=True, nullable=False),
    db.Column('event_id', db.String(60), db.ForeignKey("events.id"), primary_key=True, nullable=False)
)
# Association table between groups and images
group_image = db.Table('group_image',
    db.Column('group_id', db.String(60), db.ForeignKey("groups.id"), primary_key=True, nullable=False),
    db.Column('image_id', db.String(60), db.ForeignKey("images.id"), primary_key=True, nullable=False)
)


class Groups(BaseModel):
    """
    Model Schema for groups.

    Attributes:
        title (str):
            The title of the group.

    Methods:
        __init__(self, title):
            Constructor for the Groups class.
        __repr__(self):
            Representation of the Groups class.
        format(self):
            Returns a dictionary representation of the Groups object.
    """

    __tablename__ = "groups"

    title = db.Column(db.String(60), unique=True, nullable=False)
    # creator_id = db.column(db.Strings(60), db.ForeignKey("users.id") nullable=False)

    # many to many relationships with groups
    events = db.relationship("Events", secondary=group_events, backref=db.backref("involved_groups", lazy=True), lazy="subquery")

    thumbnails = db.relationship("Images", secondary=group_image, backref=db.backref("group", lazy=True), lazy="subquery")

    def __init__(self, title):
        """Constructor for the Groups class."""
        self.title = title

    def __repr__(self):
        """Representation of the Groups class."""
        return "title: {}".format(self.title)

    def format(self):
        """Returns a dictionary representation of the Groups object."""
        return {"id": self.id,
                 "title": self.title,
                 "created_at": format(self.created_at),
                 "updated": format(self.updated_at)}

Event/models/groups.py
hngx-org/Spitfire-events-backend
#!/usr/bin/env python3
"""Template for the Images Class"""

from Event import db
from Event.models.base_model import BaseModel


class Images(BaseModel):
    """
    Model Schema for images.

    Attributes:
        id (str):
            Primary key for the table.
        url (str):
            The URL or path to the image.

    Methods:
        __init__(self, comment_id, image_url):
            Constructor for the Image class.
        __repr__(self):
            Representation of the Image class.
        format(self):
            Returns a dictionary representation of the Image object.
    Examples:
        image = Images(comment_id=1, url="https://example.com/image.jpg")
    """

    __tablename__ = "images"

    url = db.Column(db.String(255), nullable=False)
    
    def __init__(self, url):
        """_summary_

        Args:
            comment_id (str): foreign key
            url (str): source url of the saved image
        """
        self.url = url

    def __repr__(self):
        """object representation

        Returns:
            _type_: _description_
        """
        return "url: {}, created_at: {}, updated_at: {}".format(
            self.url, self.created_at, self.updated_at
        )

    def format(self):
        """_summary_

        Returns:
            _type_: _description_
        """
        return {
            "id": self.id,
            "url": self.url,
            "created_at": self.created_at,
            "updated_at": self.updated_at
        }

Event/models/images.py
hngx-org/Spitfire-events-backend
# #!/usr/bin/env python3
# """
# This the base template for all IntresedeEvents object instances.
# """
# from Event import db
# from Event.models.base_model import BaseModel


# class InterestedEvents(BaseModel):
#     """
#     Model Schema for InterestedEvents.

#     Attributes:
#         user_id (str):
#             Foreign key for the user table.
#         event_id (str):
#             Foreign key for the event table.
#     """

#     __tablename__ = "interested_events"
#     user_id = db.Column(
#         db.String(120), db.ForeignKey("users.id"), nullable=False
#     )
#     event_id = db.Column(
#         db.String(120), db.ForeignKey("events.id"), nullable=False
#     )

#     def __init__(self, user_id, event_id):
#         """Initialize the InterestedEvents object"""
#         self.user_id = user_id
#         self.event_id = event_id

#     def format(self):
#         """Return a dictionary representation of the InterestedEvents object"""
#         return {"user_id": self.user_id, "event_id": self.event_id}

Event/models/interested_events.py
hngx-org/Spitfire-events-backend
# #!/usr/bin/env python3
# """Template for the Like Class"""

# from Event import db
# from Event.models.base_model import BaseModel


# class Likes(BaseModel):
#     """
#     Model Schema for Likes

#     """

#     __tablename__ = "likes"
    
#     comment_id = db.Column(db.String(60), db.ForeignKey("comments.id"), primary_key=True, nullable=False)
#     user_id = db.Column(db.String(60), db.ForeignKey("users.id"), primary_key=True, nullable=False)

#     def __init__(self, comment_id, user_id):
#         self.comment_id = comment_id
#         self.user_id = user_id

#     def __repr__(self):
#         """Return a string representation of the Like object"""
#         return "Comment: {}, User: {},".format(
#             self.comment_id, self.user_id,
#         )

#     def format(self):
#         """Return a dictionary representation of the Like object"""
#         return {
#             "comment_id": self.comment_id,
#             "user_id": self.user_id
#         }

Event/models/likes.py
hngx-org/Spitfire-events-backend
# #!/usr/bin/env python3
# """
# This the base template for UserGroup object instances.
# """
# from Event import db
# from Event.models.base_model import BaseModel


# class UserGroups(BaseModel):
#     """
#     Model Schema for usergroups.

#     Attributes:
#         user_id (str):
#             Foreign key for the user table.
#         group_id (str):
#             Foreign key for the group table.
#     Methods:
#         __init__(self, user_id, group_id):
#             Constructor for the UserGroups class.
#         __repr__(self):
#             Representation of the UserGroups class.
#         insert(self):
#             Inserts a new UserGroups object into the database.
#         update(self):
#             Updates an existing UserGroups object in the database.
#         delete(self):
#             Deletes an existing UserGroups object from the database.
#         format(self):
#             Returns a dictionary representation of the UserGroups object.

#     Examples:
#         usergroup = UserGroups(user_id=1, group_id=1f)
#     """

#     __tablename__ = "user_groups"
#     user_id = db.Column(
#         db.String(120), db.ForeignKey("users.id"), nullable=False
#     )
#     group_id = db.Column(
#         db.String(120), db.ForeignKey("groups.id"), nullable=False
#     )

#     def __init__(self, user_id, group_id):
#         """_summary_

#         Args:
#             user_id (_type_): _description_
#             group_id (_type_): _description_
#         """
#         self.user_id = user_id
#         self.group_id = group_id

#     def format(self):
#         """_summary_

#         Returns:
#             _type_: _description_
#         """
#         return "id: {}, user_id: {}, group_id: {} ".format(
#             self.id, self.user_id, self.group_id
#         )

Event/models/user_groups.py
hngx-org/Spitfire-events-backend
#!/usr/bin/env python3
"""Template for the User Class"""

from Event import db
from Event.models.base_model import BaseModel
from datetime import datetime


# Association table between Events and Users
interested_events = db.Table('interested_events',
    db.Column('user_id', db.String(60), db.ForeignKey("users.id"), primary_key=True, nullable=False),
    db.Column('event_id', db.String(60), db.ForeignKey("events.id"), primary_key=True, nullable=False)
)
# Association table between Users and Groups
user_groups = db.Table('user_groups',
    db.Column('user_id', db.String(60), db.ForeignKey("users.id"), primary_key=True, nullable=False),
    db.Column('group_id', db.String(60), db.ForeignKey("groups.id"), primary_key=True, nullable=False)
)
# Association table between Users and Comments
likes = db.Table('likes',
    db.Column('user_id', db.String(60), db.ForeignKey("users.id"), primary_key=True, nullable=False),
    db.Column('comment_id', db.String(60), db.ForeignKey("comments.id"), primary_key=True, nullable=False)
)



class Users(BaseModel):
    """User model"""

    __tablename__ = "users"

    # Override the id attribute to have a different type
    id = db.Column(
        db.String(60), primary_key=True, unique=True, nullable=False
    )

    # Define columns for the Users table
    name = db.Column(db.String(120), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    avatar = db.Column(db.String(255), nullable=False)
    created_at = db.Column(db.DateTime(), default=datetime.utcnow, nullable=False)
    updated_at = db.Column(db.DateTime(), default=datetime.utcnow, nullable=False)
    # one to many relationships
    comments = db.relationship("Comments", backref=db.backref("commenter", lazy=True), 
                           cascade="all, delete-orphan")
    # created_groups = db.relationship("Groups", backref=db.backref("creator", lazy=True), 
    #                        cascade="all, delete-orphan")
    # many to many relationships
    user_groups = db.relationship("Groups", backref=db.backref("members", lazy=True),
                                  secondary=user_groups, cascade="all, delete")
    interested_events = db.relationship("Events", backref=db.backref("interested_users", lazy=True),
                                        secondary=interested_events, cascade="all, delete")
    likes = db.relationship("Comments", backref=db.backref("user_likes", lazy=True),
                            secondary=likes, cascade="all, delete")

    def __init__(self, id, name, email, avatar):
        """_summary_

        Args:
            id (_type_): _description
            name (_type_): _description_
            email (_type_): _description_
            avatar (_type_): _description_
        """
        self.id = id
        self.name = name
        self.email = email
        self.avatar = avatar

    def __repr__(self):
        """Return a string representation of the User object"""
        return "Id: {}, Name: {}, Email: {}".format(
            self.id, self.name, self.email
        )

    # Override the format method to return event attributes as a dictionary
    def format(self):
        """Return a dictionary representation of the User object"""
        return {
            "id":self.id if self.id else "",
            "name": self.name,
            "email": self.email,
            "avatar": self.avatar,
            "created_at": format(self.created_at),
            "updated_at": format(self.updated_at)
        }

Event/models/users.py
hngx-org/Spitfire-events-backend
"""
Module containing user-related routes for the Events-App, Team Spitfire.
"""


from flask import Blueprint, request, jsonify, session
from Event import db
from Event.utils import query_one_filtered, is_logged_in
from Event.models.users import Users
from Event.models.events import Events


users = Blueprint("users", __name__, url_prefix="/api/users")



# Checked
# GET /api/users/<string:user_id>: Get user profile
@users.route("/")
def get_user_info():
    """gets the user info for the profile page

    Args:
        user_id (str): the id of the user

    Returns:
        str: details of the user info.

    """
    user_id = is_logged_in(session)
    try:
        user = query_one_filtered(Users, id=user_id)
        # Check if the user is a member of the group
        if user is None:
            return (
                jsonify(
                    {
                        "error": "Not Found",
                        "message": "User not found",
                    }
                ),
                404,
            )
        user_details = user.format()
        return (
            jsonify(
                {"message": f"user  details fetched successfully", "data": user_details}
            ),
            200,
        )
    except Exception as error:
        return (
            jsonify(
                {
                    "message": "your request could not be completed",
                    "error": "Bad Request",
                }
            ),
            400,
        )


# Checked
# PUT /api/users/<string:user_id>: Update user profile
@users.route("/", methods=["PUT"], strict_slashes=False)
def update_user():
    """updates the user details

    Args:
        user_id (str): the id of the user

    Returns:
        str: the new user info.

    """
    user_id = is_logged_in(session)
    try:
        data = request.get_json()
        user = query_one_filtered(Users, id=user_id)
        if user is None:
            return jsonify({"error": "Not Found", "message": "User not found"}), 404
        for key, value in data.items():
            if key == "id" or key == "created_at":
                continue
            setattr(user, key, value)
        user.update()
        return (
            jsonify(
                {
                    "message": f"user {user_id} details updated successfully",
                    "data": user.format(),
                }
            ),
            201,
        )
    except Exception as error:
        return (
            jsonify(
                {
                    "message": "your request could not be completed",
                    "error": "Bad Request",
                }
            ),
            400,
        )


# Checked
# POST /api/users/<string:user_id>/interests/<string:event_id>: Show interests
@users.route(
    "/interests/<string:event_id>",
    methods=["POST"],
    strict_slashes=False,
)
def create_interest(event_id):
    """Create interest in an event"""
    user_id = is_logged_in(session)
    try:
        user = query_one_filtered(Users, id=user_id)
        event = query_one_filtered(Events, id=event_id)

        if not user or not event:
            return (
                jsonify({"Error": "Not Found", "message": "User or Event not found"}),
                404,
            )
        if event not in user.interested_events:
            user.interested_events.append(event)
            user.update()

            return (
                jsonify(
                    {
                        "message": "Interest registered",
                        "data": f"{user_id} has shown interest in {event_id}",
                    }
                ),
                201,
            )
        # else return interest shown already
        return (
            jsonify(
                {
                    "message": "Interest cannot be registered twice",
                    "error": f"{user_id} has shown interest in {event_id} previously",
                }
            ),
            201,
        )

    except Exception as e:
        db.session.rollback()
        return jsonify({"error": "Bad Request", "message": "Something went wrong"}), 400


# DELETE /api/users/userId/interests/eventId
@users.route("/interests/<string:event_id>", methods=["DELETE"])
def delete_user_interest(event_id):
    """Delete interest in event
    Args:
        user_id: The id of the user
        event_id: the id of the event to be deleted
    Returns:
        str: success msessage
    """
    user_id = is_logged_in(session)
    try:
        user = query_one_filtered(Users, id=user_id)
        event = query_one_filtered(Events, id=event_id)
        if not user or not event:
            return jsonify({"error": "Not Found", "message": "Interest not found"}), 404
        new_interested_events = user.interested_events

        if event in user.interested_events:
            for key, interest in enumerate(user.interested_events):
                if event.id == interest.id:
                    new_interested_events.pop(key)
            user.interested_events = new_interested_events
            user.update()

            return jsonify({"message": "Interest deleted", "data": "No content"}), 204
        # else return error
        return jsonify(
            {
                "message": "user has not previously shown interest", 
                "error": "Nothing to delete"
            }
        ), 403
    except Exception as error:
        return jsonify({"error": "Bad Request", "message": "Something went wrong"}), 400

Event/user/routes.py
hngx-org/Spitfire-events-backend
"""
summary
"""
from Event import db
from Event.errors.handlers import CustomError
from datetime import datetime
import os



# db helpers


# get unique item from table based on filter
# args:table=model_class **kwargs=filters
def query_one_filtered(table, **kwargs):
    """_summary_

    Args:
        table (_type_): _description_

    Returns:
        _type_: _description_
    """
    return db.session.execute(
        db.select(table).filter_by(**kwargs)
    ).scalar_one_or_none()


# get all items from table based on filter
# args:table=model_class **kwargs=filters
def query_all_filtered(table, **kwargs):
    """_summary_

    Args:
        table (_type_): _description_

    Returns:
        _type_: _description_
    """
    return (
        db.session.execute(db.select(table).filter_by(**kwargs))
        .scalars()
        .all()
    )


# get first one item from table no filter
def query_one(table):
    """_summary_

    Args:
        table (_type_): _description_

    Returns:
        _type_: _description_
    """
    return db.session.execute(db.select(table)).scalar_one_or_none()


# get all items on table no filter
def query_all(table):
    """_summary_

    Args:
        table (_type_): _description_

    Returns:
        _type_: _description_
    """
    return db.session.execute(db.select(table)).scalars().all()


# get all items from table no filter paginated
def query_paginated(table, page):
    """_summary_

    Args:
        table (_type_): _description_
        page (_type_): _description_

    Returns:
        _type_: _description_
    """
    return db.paginate(
        db.select(table).order_by(table.date_created.desc()),
        per_page=15,
        page=page,
        error_out=False,
    )


# get all items from table based on filtered paginated
def query_paginate_filtered(table, page, **kwargs):
    """_summary_

    Args:
        table (_type_): _description_
        page (_type_): _description_

    Returns:
        _type_: _description_
    """
    return db.paginate(
        db.select(table)
        .filter_by(**kwargs)
        .order_by(table.date_created.desc()),
        per_page=15,
        page=page,
        error_out=False,
    )


# session helpers


def is_logged_in(session):
    """
    Ensures a user is logged in or returns error

    Parameterss:
        session(dict):
            - flask session object

    returns
        id(str):
            - logged in users id
    """
    # user = session.get("user")
    user = session.get("user")  or { "id": "user11_id"}

    if not user:
        raise CustomError("Unauthorized", 401, "You are not logged in")

    return user.get("id")


def format_date(date):
    """Formats date to readable string"""
    return datetime.strptime(date, '%Y-%m-%d')


def format_time(time):
    """Formats date to readable string"""

    return datetime.strptime(time,'%H:%M')



Event/utils.py
hngx-org/Spitfire-events-backend
"""_summary_
"""
from Event import create_app

app = create_app()

# @app.before_request
# def is_logged_in(session):
#     """
#     Ensures a user is logged in returns error

#     Parameterss:
#         session(dict):
#             - flask session object

#     returns
#         id(str):
#             - logged in users id
#     """
#     user = session.get("user")

#     if not user:
#         raise CustomError("Unauthorized", 401, "You are not logged in")

#     return user.get("id")

if __name__ == "__main__":
    app.run(debug=True)
    
run.py
hngx-org/Spitfire-events-backend
import unittest
import requests

BASE_URI = "http://spitfire.onrender.com/api/events/"

class TestAddComments(unittest.TestCase):
    def setUp(self):
        # Create an event to add comments to
        self.event_data = {
            "title": "Event 1",
            "description": "Event 1 Description",
            "location": "Event 1 Location",
            "start_date": "2023-09-21",
            "start_time": "10:00:00",
            "end_date": "2023-09-22",
            "end_time": "12:00:00",
            "thumbnail": "thumbnail-url-1"
        }
        response = requests.post(BASE_URI, json=self.event_data)
        self.event_id = response.json()["data"]["id"]

    def test_add_comment_success(self):
        # Make a POST request to add a comment to an event
        comment_data = {
            "user_id": "user-1",
            "body": "This is a comment"
        }
        response = requests.post(BASE_URI + self.event_id + "/comments", json=comment_data)

        # Check if the response status code is 200 (OK)
        self.assertEqual(response.status_code, 200)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)
        self.assertIn("data", response_data)
        self.assertIn("id", response_data["data"])
        self.assertIn("body", response_data["data"])

    def test_add_comment_missing_fields(self):
        # Make a POST request to add a comment to an event with missing fields
        comment_data = {    
            "user_id": "user-1"
        }
        response = requests.post(BASE_URI + self.event_id + "/comments", json=comment_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)
        self.assertIn("error", response_data)

    def test_add_comment_invalid_event_id(self):
        # Make a POST request to add a comment to an invalid event ID
        comment_data = {
            "user_id": "user-1",
            "body": "This is a comment"
        }
        response = requests.post(BASE_URI + "invalid-event-id/comments", json=comment_data)

        # Check if the response status code is 404 (Not Found)
        self.assertEqual(response.status_code, 404)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)

    def test_add_comment_database_error(self):
        # Mock the insert function to raise an exception
        def mock_insert(*args, **kwargs):
            raise Exception("Database error")

        # Replace the original function with the mock function
        original_function = __import__("routes").Comments.insert
        __import__("routes").Comments.insert = mock_insert

        # Make a POST request to add a comment to an event
        comment_data = {
            "user_id": "user-1",
            "body": "This is a comment"
        }
        response = requests.post(BASE_URI + self.event_id + "/comments", json=comment_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)
        self.assertIn("error", response_data)

    def test_get_comments_success(self):
        # Make a GET request to get all comments for an event
        response = requests.get(BASE_URI + self.event_id + "/comments")

        # Check if the response status code is 200 (OK)
        self.assertEqual(response.status_code, 200)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)
        self.assertIn("data", response_data)

    def test_get_comments_invalid_event_id(self):
        # Make a GET request to get comments for an invalid event ID
        response = requests.get(BASE_URI + "invalid-event-id/comments")

        # Check if the response status code is 404 (Not Found)
        self.assertEqual(response.status_code, 404)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)

    def test_get_comments_no_comments(self):
        # Make a GET request to get comments for an event with no comments
        event_data = {
            "title": "Event 2",
            "description": "Event 2 Description",
            "location": "Event 2 Location",
            "start_date": "2023-09-21",
            "start_time": "10:00:00",
            "end_date": "2023-09-22",
            "end_time": "12:00:00",
            "thumbnail": "thumbnail-url-2"
        }
        response = requests.post(BASE_URI, json=event_data)
        event_id = response.json()["data"]["id"]
        response = requests.get(BASE_URI + event_id + "/comments")

        # Check if the response status code is 404 (Not Found)
        self.assertEqual(response.status_code, 404)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)

        # Delete the event from the database
        delete_uri = BASE_URI + event_id
        requests.delete(delete_uri)

    def test_get_comments_database_error(self):
        # Mock the query_all_filtered function to raise an exception
        def mock_query_all_filtered(*args, **kwargs):
            raise Exception("Database error")

        # Replace the original function with the mock function
        original_function = __import__("routes").query_all_filtered
        __import__("routes").query_all_filtered = mock_query_all_filtered

        # Make a GET request to get all comments for an event
        response = requests.get(BASE_URI + self.event_id + "/comments")

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)
        self.assertIn("error", response_data)
tests/events/test_add_comment.py
hngx-org/Spitfire-events-backend
import unittest
import requests

BASE_URI = "http://spitfire.onrender.com/api/events/"

class TestCreateEvent(unittest.TestCase):
    def test_create_event_success(self):
        # Define event details in JSON format
        event_data = {
            "title":"New Event",
            "description": "Event Description",
            "thumbnail": "Event Thumbnail",
            "location": "Event Location",
            "creator_id": "user1_id",
            "start_time": "06:52:10",
            "end_time": "06:57:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request to create a new event
        response = requests.post(BASE_URI, json=event_data)

        # Check if the response status code is 201 (Created)
        self.assertEqual(response.status_code, 201)

        # Check if the response data contains the expected event details
        response_data = response.json()
        self.assertIn("data", response_data)
        created_event = response_data["data"]
        self.assertEqual(created_event["title"], "New Event")
        self.assertEqual(created_event["description"], "Event Description")
        #self.assertEqual(created_event["thumbnail"], "Event Thumbnail")
        self.assertEqual(created_event["location"], "Event Location")
        self.assertEqual(created_event["creator_id"], "user1_id")
        self.assertEqual(created_event["start_time"], "06:52:10")
        self.assertEqual(created_event["end_time"], "06:57:10")
        self.assertEqual(created_event["start_date"], "2000-07-11")
        self.assertEqual(created_event["end_date"], "1999-06-11")
        self.assertIn("created_at", created_event)
        self.assertIn("updated_at", created_event)

    def test_create_event_missing_data(self):
        # Define event details with missing data (e.g., missing title, creator_id, etc.)
        event_data = {
            "description": "Event Description",
            "thumbnail": "Event Thumbnail",
            "location": "Event Location",
            "start_time": "06:52:10",
            "end_time": "06:57:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request with missing data
        response = requests.post(BASE_URI, json=event_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Missing required fields")

    def test_empty_fields(self):
        # Define event details with empty fields (e.g., empty title, creator_id, etc.)
        event_data = {
            "title": "",
            "description": "",
            "thumbnail": "",
            "location": "",
            "creator_id": "",
            "start_time": "",
            "end_time": "",
            "start_date": "",
            "end_date": ""
        }

        # Make a POST request with empty fields
        response = requests.post(BASE_URI, json=event_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Missing required fields")

    def test_fields_as_none(self):
        # Define event details with null fields (e.g., null title, creator_id, etc.)
        event_data = {
            "title": None,
            "description": None,
            "thumbnail": None,
            "location": None,
            "creator_id": None,
            "start_time": None,
            "end_time": None,
            "start_date": None,
            "end_date": None
        }

        # Make a POST request with null fields
        response = requests.post(BASE_URI, json=event_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Missing required fields")


    def test_long_fields(self):
        # Define event details with long fields (e.g., long title, creator_id, etc.)
        event_data = {
            "title": "a" * 65,
            "description": "T" * 230,
            "thumbnail": "Event Thumbnail",
            "location": "T" * 1030,
            "creator_id": "T" * 65,
            "start_time": "06:52:10",
            "end_time": "06:57:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request with long fields
        response = requests.post(BASE_URI, json=event_data)
        
        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Invalid field length")


    def test_negative_time_duration(self):
        # Define event details with negative time duration
        event_data = {
            "title": "New Event",
            "description": "Event Description",
            "thumbnail": "Event Thumbnail",
            "location": "Event Location",
            "creator_id": "user1_id",
            "start_time": "06:52:10",
            "end_time": "06:51:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request with negative time duration
        response = requests.post(BASE_URI, json=event_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Invalid time duration")


if __name__ == '__main__':
    unittest.main()
tests/events/test_create_event.py
hngx-org/Spitfire-events-backend
import unittest
import requests

BASE_URI = "http://spitfire.onrender.com/api/events/"

class TestUpdateEventById(unittest.TestCase):
    def setUp(self):
        # Create some events to be retrieved
        self.event_data = {
            "title":"New Event",
            "description": "Event Description",
            "thumbnail": "Event Thumbnail",
            "location": "Event Location",
            "creator_id": "user1_id",
            "start_time": "06:52:10",
            "end_time": "06:57:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request to create a new event and store the event id
        response = requests.post(BASE_URI, json=self.event_data)
        self.event_data = response.json()["data"]
        self.event_id = response.json()["data"]["id"]

    def test_update_event_by_id_success(self):
        # Make a DELETE request to delete the event
        response = requests.delete(BASE_URI + self.event_id)

        # Check if the response status code is 204 (No Content)
        self.assertEqual(response.status_code, 204)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertEqual(response_data["message"], "Event deleted successfully")

        # Check if the event was deleted from the database
        response = requests.delete(BASE_URI + self.event_id)
        self.assertEqual(response.status_code, 404)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertEqual(response_data["error"], "Not found")
        self.assertEqual(response_data["message"], "Event not found")


    def test_update_event_by_id_not_found(self):
        # Make a PUT request to update an event by an invalid id
        updated_data = {
            "title": "Updated Event",
            "description": "Updated Event Description",
            "location": "Updated Event Location"
        }   
        event_id = "invalid-id"
        response = requests.put(BASE_URI + event_id)

        # Check if the response status code is 404 (Not Found)
        self.assertEqual(response.status_code, 404)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertEqual(response_data["error"], "Not found")
        self.assertEqual(response_data["message"], "Event not found")

    def test_update_event_by_id_error(self):
        # skip this test for now
        return

        # Simulate a database error

        # Check if the response status code is 500 (Internal Server Error)
        self.assertEqual(response.status_code, 500)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertEqual(response_data["error"], "Internal Server Error")
        self.assertEqual(response_data["message"], "Something went wrong")
tests/events/test_delete_event.py
hngx-org/Spitfire-events-backend
import unittest
import requests

BASE_URI = "http://spitfire.onrender.com/api/events/"

class TestAllEvents(unittest.TestCase):
    def test_all_events_success(self):
        # Make a GET request to retrieve all events
        response = requests.get(BASE_URI)

        # Check if the response status code is 200 (OK)
        self.assertEqual(response.status_code, 200)

        # Check if the response data contains multiple events and veriy their fields
        response_data = response.json()
        self.assertIn("message", response_data)
        self.assertIn("data", response_data)
        all_events = response_data["data"]
        for event in all_events:
            self.assertIn("id", event)
            self.assertIn("title", event)
            self.assertIn("description", event)
            self.assertIn("location", event)
            self.assertIn("start_date", event)
            self.assertIn("start_time", event)
            self.assertIn("end_date", event)
            self.assertIn("end_time", event)
            self.assertIn("created_at", event)
            self.assertIn("updated_at", event)

    def test_all_events_empty(self):
        # Time will come to write this test
        # Let the database breathe.
        pass


if __name__ == '__main__':
    unittest.main()
tests/events/test_get_all_event.py
hngx-org/Spitfire-events-backend
import unittest
import requests

BASE_URI = "http://spitfire.onrender.com/api/events/"

class TestGetComments(unittest.TestCase):
    def setUp(self):
        # Create an event to get comments for
        self.event_data = {
            "title": "Event 1",
            "description": "Event 1 Description",
            "location": "Event 1 Location",
            "start_date": "2023-09-21",
            "start_time": "10:00:00",
            "end_date": "2023-09-22",
            "end_time": "12:00:00",
            "thumbnail": "thumbnail-url-1"
        }
        response = requests.post(BASE_URI, json=self.event_data)
        self.event_id = response.json()["data"]["id"]

        # Create some comments for the event
        self.comment_data = [
            {
                "user_id": "user-1",
                "body": "This is comment 1"
            },
            {
                "user_id": "user-2",
                "body": "This is comment 2"
            },
            {
                "user_id": "user-3",
                "body": "This is comment 3"
            }
        ]

    def test_empty_comments_success(self):
        # Make a GET request to get comments for an event
        response = requests.get(BASE_URI + self.event_id + "/comments")

        # Check if the response status code is 200 (OK)
        self.assertEqual(response.status_code, 200)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("message", response_data)
        self.assertIn("data", response_data)

        # Check if the response data is empty
        self.assertEqual(len(response_data["data"]), 0)


    def test_get_comments_success(self):
        for comment in self.comment_data:
            response = requests.post(BASE_URI + self.event_id + "/comments", json=comment)
    
        # Make a GET request to get comments for an event
        response = requests.get(BASE_URI + self.event_id + "/comments")

        # Check if the response status code is 200 (OK)
        self.assertEqual(response.status_code, 200)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("message", response_data)
        self.assertIn("data", response_data)

        # Check if the response data contains the expected number of comments
        self.assertEqual(len(response_data["data"]), len(self.comment_data))

        # Check the data inthe comment are same as expected
        for i in range(len(self.comment_data)):
            self.assertEqual(response_data["data"][i]["user_id"], self.comment_data[i]["user_id"])
            self.assertEqual(response_data["data"][i]["body"], self.comment_data[i]["body"])


    def test_get_comments_not_found(self):
        # Make a GET request to get comments for a non-existent event
        response = requests.get(BASE_URI + "non-existent-event-id/comments")

        # Check if the response status code is 404 (Not Found)
        self.assertEqual(response.status_code, 404)

        # Check if the response data contains the expected fields and values
        response_data = response.json()
        self.assertEqual(response_data["error"], "Not found")
        self.assertEqual(response_data["message"], "Event not found")
tests/events/test_get_comment.py
hngx-org/Spitfire-events-backend
import unittest
import requests

BASE_URI = "http://spitfire.onrender.com/api/events/"

class TestGetEventById(unittest.TestCase):
    def setUp(self):
        # Create some events to be retrieved
        self.event_data = {
            "title":"New Event",
            "description": "Event Description",
            "thumbnail": "Event Thumbnail",
            "location": "Event Location",
            "creator_id": "user1_id",
            "start_time": "06:52:10",
            "end_time": "06:57:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request to create a new event and store the event id
        response = requests.post(BASE_URI, json=self.event_data)
        self.event_data = response.json()["data"]
        self.event_id = response.json()["data"]["id"]


    def test_get_event_by_id_success(self):
        # Make a GET request to retrieve an event by id
        response = requests.get(BASE_URI + self.event_id)

        # Check if the response status code is 200 (OK)
        self.assertEqual(response.status_code, 200)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("status", response_data)
        self.assertIn("message", response_data)
        self.assertIn("data", response_data)

        # Check if the response data matches the expected event
        expected_event = self.event_data
        returned_event = response_data["data"]
        self.assertEqual(expected_event["title"], returned_event["title"])
        self.assertEqual(expected_event["description"], returned_event["description"])
        self.assertEqual(expected_event["location"], returned_event["location"])
        self.assertEqual(expected_event["creator_id"], returned_event["creator_id"])
        self.assertEqual(expected_event["start_date"], returned_event["start_date"])
        self.assertEqual(expected_event["start_time"], returned_event["start_time"])
        self.assertEqual(expected_event["end_date"], returned_event["end_date"])
        self.assertEqual(expected_event["end_time"], returned_event["end_time"])
        self.assertEqual(expected_event["created_at"], returned_event["created_at"])
        self.assertEqual(expected_event["created_at"], returned_event["created_at"])

    def test_get_event_by_id_invalid_id(self):
        # Make a GET request to retrieve an event by an invalid id
        event_id = "invalid-id"
        response = requests.get(BASE_URI + event_id)

        # Check if the response status code is 404 (Not Found)
        self.assertEqual(response.status_code, 404)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertEqual(response_data["error"], "Not found")
        self.assertEqual(response_data["message"], "Event not found")

    def test_get_event_by_id_error(self):
        # skip this test for now
        return

        # Simulate a database error

        # Check if the response status code is 500 (Internal Server Error)
        self.assertEqual(response.status_code, 500)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertEqual(response_data["error"], "Internal Server Error")
        self.assertEqual(response_data["message"], "Something went wrong")
tests/events/test_get_event_by_id.py
hngx-org/Spitfire-events-backend
import unittest
import requests

BASE_URI = "http://spitfire.onrender.com/api/events/"

class TestUpdateEventById(unittest.TestCase):
    def setUp(self):
        # Create some events to be retrieved
        self.event_data = {
            "title":"New Event",
            "description": "Event Description",
            "location": "Event Location",
            "creator_id": "user1_id",
            "start_time": "06:52:10",
            "end_time": "06:57:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request to create a new event and store the event id
        response = requests.post(BASE_URI, json=self.event_data)
        self.event_data = response.json()["data"]
        self.event_id = response.json()["data"]["id"]

    def test_update_event_by_id_success(self):
        # Make a PUT request to update an event by id
        updated_data = {
            "title":"Updated Event",
            "description": "Updated Event Description",
            "location": "Updated Event Location",
            "creator_id": "user1_id",
            "start_time": "14:00:00",
            "end_time": "16:00:00",
            "start_date": "2023-09-23",
            "end_date": "2023-09-24"
        }
        response = requests.put(BASE_URI + self.event_id, json=updated_data)

        # Check if the response status code is 201 (Created)
        self.assertEqual(response.status_code, 201)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertIn("message", response_data)
        self.assertIn("Event_id", response_data)
        self.assertIn("data", response_data)

        # Check if the response data matches the updated event
        expected_event = {**self.event_data, **updated_data}
        returned_event = response_data["data"]
        self.assertEqual(expected_event["title"], returned_event["title"])
        self.assertEqual(expected_event["description"], returned_event["description"])
        self.assertEqual(expected_event["location"], returned_event["location"])
        self.assertEqual(expected_event["start_date"], returned_event["start_date"])
        self.assertEqual(expected_event["start_time"], returned_event["start_time"])
        self.assertEqual(expected_event["end_date"], returned_event["end_date"])
        self.assertEqual(expected_event["end_time"], returned_event["end_time"])


    def test_update_event_by_id_not_found(self):
        # Make a PUT request to update an event by an invalid id
        updated_data = {
            "title": "Updated Event",
            "description": "Updated Event Description",
            "location": "Updated Event Location"
        }   
        event_id = "invalid-id"
        response = requests.put(BASE_URI + event_id, json=updated_data)

        # Check if the response status code is 404 (Not Found)
        self.assertEqual(response.status_code, 404)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertEqual(response_data["error"], "Not found")
        self.assertEqual(response_data["message"], "Event not found")


    def test_empty_fields(self):
        # Define event details with empty fields (e.g., empty title, creator_id, etc.)
        event_data = {
            "title": "",
            "description": "",
            "location": "",
            "creator_id": "",
            "start_time": "",
            "end_time": "",
            "start_date": "",
            "end_date": ""
        }

        # Make a POST request with empty fields
        response = requests.put(BASE_URI, json=event_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Missing required fields")

    def test_fields_as_none(self):
        # Define event details with null fields (e.g., null title, creator_id, etc.)
        event_data = {
            "title": None,
            "description": None,
            "location": None,
            "creator_id": None,
            "start_time": None,
            "end_time": None,
            "start_date": None,
            "end_date": None
        }

        # Make a POST request with null fields
        response = requests.put(BASE_URI, json=event_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Missing required fields")


    def test_long_fields(self):
        # Define event details with long fields (e.g., long title, creator_id, etc.)
        event_data = {
            "title": "a" * 65,
            "description": "T" * 230,
            "location": "T" * 1030,
            "creator_id": "T" * 65,
            "start_time": "06:52:10",
            "end_time": "06:57:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request with long fields
        response = requests.put(BASE_URI, json=event_data)
        
        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Invalid field length")


    def test_negative_time_duration(self):
        # Define event details with negative time duration
        event_data = {
            "title": "New Event",
            "description": "Event Description",
            "location": "Event Location",
            "creator_id": "user1_id",
            "start_time": "06:52:10",
            "end_time": "06:51:10",
            "start_date": "2000-07-11",
            "end_date": "1999-06-11"
        }

        # Make a POST request with negative time duration
        response = requests.put(BASE_URI, json=event_data)

        # Check if the response status code is 400 (Bad Request)
        self.assertEqual(response.status_code, 400)

        # Check if the error message is correct
        response_data = response.json()
        self.assertEqual(response_data["error"], "Bad Request")
        self.assertEqual(response_data["message"], "Invalid time duration")


    def test_update_event_by_id_error(self):
        # skip this test for now
        return

        # Simulate a database error

        # Check if the response status code is 500 (Internal Server Error)
        self.assertEqual(response.status_code, 500)

        # Check if the response data contains the expected fields
        response_data = response.json()
        self.assertEqual(response_data["error"], "Internal Server Error")
        self.assertEqual(response_data["message"], "Something went wrong")


if __name__ == '__main__':
    unittest.main()

tests/events/test_update_event_by_id.py
hngx-org/Spitfire-events-backend