diff --git a/.github/workflows/ms5yml b/.github/workflows/ms5yml index d352c7f..e9dbb55 100644 --- a/.github/workflows/ms5yml +++ b/.github/workflows/ms5yml @@ -1,59 +1,54 @@ -name: ms5API Testing - -on: - push: #we want only the milstone5 - branches: [Milestone-5_feature_branch] - pull_request: - branches: [Milestone-5_feature_branch] - +name: API CI/CD +# This is a trigger for any push to the repo, and tells github when the actions have to be run +# Every time we do a push, the action will be executed +# The actions should be run only when there is a push from main and develop +on: + push: + branches: + - main + - develop + +#Tells github actions what to execute when trigger condition is met jobs: - api-testing: - runs-on: ubuntu-latest - + # Each job runs in parallel + tests: #This is the job name + + # runs-on indicates which GitHub "Runners" will run this CICD pipeline + # For all CSE-2102 repos, just use the following line as is + runs-on: self-hosted + # This next block allows you to run this ENTIRE job on different python versions + strategy: + matrix: + #python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8"] + + # Steps are run in sequence in this job. If one step fails, the entire job fails. steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python 3 - uses: actions/setup-python@v2 - with: - python-version: '3.8' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install flask flask_cors flasgger pylint pytest - - # Run pylint - - name: Run pylint - run: | - pylint ./backend/*.py - continue-on-error: false # Set to true to see warnings - - #docker image build - - name: Build API Docker Image - run: | - docker build -t api-image -f Dockerfile . - - #run docker container - - name: Start API Container - run: | - docker run -d -p 5000:5000 --name api-container api-image - - #running main.py - - name: Start API Service - run: | - docker exec api-container python main.py - - #test_main.py - - name: Run test_main.py - run: | - docker exec api-container pytest test_main.py --maxfail=1 --disable-warnings - - #cut the docker - - name: Stop and Remove API Container - run: | - docker stop api-container - docker rm api-container - - + # Use this next line to pull your repo into the Docker container running the job + - uses: actions/checkout@v3 + # This block sets up the python version + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + # Pylint is a static code analysis tool. Use this block as is to install pylint + # in the Docker container running the job + - name: Install pylint + run: | + python -m pip install --upgrade pip + pip install pylint + pip install requests + + - name: Install pytest + run: | + python -m pip install --upgrade pip + pip install pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Run Api + run: | + python3 Backend/app.py & + + - name: Run Pytest + run: | + pytest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee8b34f --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# SQLite database +#/Backend/testData.db +#*.db + +#Log files for some reason lol +*.log +logs/ diff --git a/Backend/app.py b/Backend/app.py new file mode 100644 index 0000000..a24b08b --- /dev/null +++ b/Backend/app.py @@ -0,0 +1,178 @@ +""" +This module provides functions for HTTP communication and +route functions +""" +from flask import Flask, request, jsonify +from database_files import databasefunctions +app = Flask(__name__) + +# ----------------------------------------------------------------------------------------- + +@app.route("/add_newuser", methods=["POST"]) +def add_newuser(): + """ + Adds new user to database + """ + data = request.get_json() + newuser_id = databasefunctions.insert_user(data.get("username"),data.get("password"), + data.get("name"), data.get("age"), + data.get("location")) + + return jsonify({'user_id': newuser_id}), 200 + +@app.route("/add_newpet", methods=["POST"]) +def add_newpet(): + """ + Adds new pet to database + """ + data = request.get_json() + newpet_id = databasefunctions.insert_pet(data.get("name"), data.get("age"), + data.get("gender"), data.get("breed"), data.get("type"), + data.get("location"), data.get("photo_path")) + + return jsonify({'pet_id': newpet_id}), 200 + + +@app.route("/add_newfavorite", methods=["POST"]) +def add_newfavorite(): + """ + Adds new user favorite to database + INSERT INTO pets (name, age, gender, breed, type, location, photo_path) + VALUES + ('Bella', 3, 'female', 'Golden Retriever', 'dog', 'New York', 'images/Chuck.jpg'), + ('Max', 5, 'male', 'Maine Coon', 'cat', 'Boston', 'images/gunner.jpg'), + ('Luna', 2, 'female', 'Siamese', 'cat', 'Chicago', 'images/luna.jpg'), + ('Charlie', 4, 'male', 'Beagle', 'dog', 'Los Angeles', 'images/lizzie_izzie.jpg'), + ('Daisy', 1, 'female', 'Labrador Retriever', 'dog', 'Miami', 'images/monster.jpg'); + + """ + data = request.get_json() + newfavorite_id = databasefunctions.insert_favorite(data.get("user_id"), data.get("pet_id")) + + return jsonify({'faviorte_id': newfavorite_id}), 200 + + + +#------------------------------------------------------------------------------------------------ + +@app.route("/remove_user", methods=["DELETE"]) +def removeuser(): + """ + Remove user from database + """ + data = request.get_json() + userid = data.get("user_id") + roweffected = databasefunctions.remove_user(userid) + + return (f"{roweffected}, row was deleted in user table"), 200 + + +@app.route("/remove_pet", methods=["DELETE"]) +def removepet(): + """ + Remove pet from database + """ + data = request.get_json() + petid = data.get("pet_id") + roweffected = databasefunctions.remove_pet(petid) + + return (f"{roweffected}, row was deleted in pet table"), 200 + + +@app.route("/remove_favorite", methods=["DELETE"]) +def removefavorite(): + """ + Remove favorite from database + """ + data = request.get_json() + roweffected = databasefunctions.remove_favorite(data.get("user_id"),data.get("pet_id")) + + return (f"{roweffected}, row was deleted in the favorite table"), 200 + +# --------------------------------------------------------------------- + + + +@app.route("/fetch_userid", methods=["POST"]) +def fetch_userid(): + """ + fetch userid associated with username and password + """ + data = request.get_json() + username = data.get("username") + password = data.get("password") + + + userid = databasefunctions.fetch_userid_from_username_and_password(username, password) + + return jsonify(userid), 200 + + + + + +@app.route("/fetch_pet", methods=["POST"]) +def fetch_pet(): + """ + fetch pet from database + """ + data = request.get_json() + petid = data.get("pet_id") + users_row = databasefunctions.fetch_pet_by_id(petid) + + return jsonify(users_row), 200 + + +@app.route("/fetch_user", methods=["POST"]) +def fetch_user(): + """ + fetch user from database + """ + data = request.get_json() + userid = data.get("user_id") + users_row = databasefunctions.fetch_user_by_id(userid) + + return jsonify(users_row), 200 + + +@app.route("/fetch_favorited_pets", methods=["POST"]) +def fetch_favorite_pet(): + """ + fetches all pets favorited by a user + """ + data = request.get_json() + userid = data.get("user_id") + favorited_pets = databasefunctions.fetch_favorites_by_user(userid) + + return jsonify(favorited_pets), 200 + + +# --------------------------------------------------------------- + +@app.route("/fetch_allusers", methods=["POST"]) +def fetch_allusers(): + """ + fetches all users in database + """ + + allusers = databasefunctions.fetch_all_users() + return jsonify(allusers), 200 + + +@app.route("/fetch_allpets", methods=["POST"]) +def fetch_allpets(): + """ + fetches all pets in database + """ + + allpets = databasefunctions.fetch_all_pets() + return jsonify(allpets), 200 + + + + + + + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) diff --git a/Backend/database_files/__pycache__/databasefunctions.cpython-310.pyc b/Backend/database_files/__pycache__/databasefunctions.cpython-310.pyc new file mode 100644 index 0000000..d1bdb58 Binary files /dev/null and b/Backend/database_files/__pycache__/databasefunctions.cpython-310.pyc differ diff --git a/Backend/database_files/databasefunctions.py b/Backend/database_files/databasefunctions.py new file mode 100644 index 0000000..4aa835e --- /dev/null +++ b/Backend/database_files/databasefunctions.py @@ -0,0 +1,209 @@ +'''DATABASE FUNCTIONS''' +from contextlib import closing +import sqlite3 + +def connect_db(): + """Establish a connection to the SQLite database.""" + return sqlite3.connect('projectdatabase.db') #this might change + +# --------------------- insert functions + +def insert_user(username, password, name, age, location): + """Insert a new user into the accounts table.""" + with closing(connect_db()) as conn, conn: + cursor = conn.cursor() + cursor.execute( + '''INSERT INTO accounts (username, password, name, age, location) + VALUES (?, ?, ?, ?, ?)''', + (username, password, name, age, location) + ) + conn.commit() + return cursor.lastrowid + + +def insert_pet(name, age, gender, breed, type, location, photo_path=None): + """Insert a new pet into the pets table.""" + with closing(connect_db()) as conn, conn: + cursor = conn.cursor() + cursor.execute( + '''INSERT INTO pets (name, age, gender, breed, type, location, photo_path) + VALUES (?, ?, ?, ?, ?, ?, ?)''', + (name, age, gender, breed, type, location, photo_path) + ) + conn.commit() + return cursor.lastrowid + + +def insert_favorite(user_id, pet_id): + """Insert usere favorite pet into favorites table""" + with closing(connect_db()) as conn, conn: + cursor = conn.cursor() + cursor.execute( + '''INSERT INTO favorites (user_id, pet_id) + VALUES (?, ?)''', + (user_id, pet_id) + ) + conn.commit() + return cursor.lastrowid + +# ---------------------------remove functions + +def remove_user(user_id): + """Remove a pet from the accounts table.""" + with closing(connect_db()) as conn, conn: + cursor = conn.cursor() + cursor.execute( + '''DELETE FROM accounts + WHERE user_id = ?''', + (user_id,) + ) + conn.commit() + return cursor.rowcount + + +def remove_pet(pet_id): + """Remove a pet from the pets table.""" + with closing(connect_db()) as conn, conn: + cursor = conn.cursor() + cursor.execute( + '''DELETE FROM pets + WHERE pet_id = ?''', + (pet_id,) + ) + conn.commit() + return cursor.rowcount + + +def remove_favorite(user_id, pet_id): + """Remove a favorite row from the favorites table based on user_id and pet_id.""" + with closing(connect_db()) as conn: + cursor = conn.cursor() + + # Execute DELETE query to remove the row matching user_id and pet_id + cursor.execute(""" + DELETE FROM favorites + WHERE user_id = ? AND pet_id = ? + """, (user_id, pet_id)) + + conn.commit() + return cursor.rowcount # Returns the number of rows affected (should be 1 if successful) + +# ------------------------------------------- change fucntions + + +def change_user_attributes(user_id, attribute, change): + """Update a user's attribute in the accounts table.""" + + # List of valid column names to avoid SQL injection + valid_columns = ['name', 'age', 'location', 'password'] # Add your valid column names here + + if attribute not in valid_columns: + raise ValueError("Invalid attribute name.") + + with closing(connect_db()) as conn: + cursor = conn.cursor() + + # Use the attribute directly, now it's safe since we validated it + cursor.execute(f""" + UPDATE accounts + SET {attribute} = ? + WHERE user_id = ? + """, (change, user_id)) + + conn.commit() + return fetch_user_by_id(user_id) + + +def change_pet_attributes(pet_id, attribute, change): + """Update a user's attribute in the accounts table.""" + + # List of valid column names to avoid SQL injection + valid_columns = ['name', 'age', 'location', 'breed', + 'gender', 'type', 'photo_path'] # Add your valid column names here + + if attribute not in valid_columns: + raise ValueError("Invalid attribute name.") + + with closing(connect_db()) as conn: + cursor = conn.cursor() + + # Use the attribute directly, now it's safe since we validated it + cursor.execute(f""" + UPDATE pets + SET {attribute} = ? + WHERE pet_id = ? + """, (change, pet_id)) + + conn.commit() + return fetch_pet_by_id(pet_id) + +# ------------------------------------- fetch functions + + +def fetch_userid_from_username_and_password(username,password): + """Get a users ID from there username and password""" + + with closing(connect_db()) as conn: + cursor = conn.cursor() + + # Execute DELETE query to remove the row matching user_id and pet_id + cursor.execute(""" + SELECT user_id FROM accounts + WHERE username = ? AND password = ? + """, (username, password)) + + result = cursor.fetchone() + if result: + return result[0] + else: + return None + + +def fetch_favorites_by_user(user_id): + """Fetch all favorite pets for a given user_id.""" + with closing(connect_db()) as conn: + cursor = conn.cursor() + + cursor.execute(""" + SELECT pets.* + FROM pets + INNER JOIN favorites ON pets.pet_id = favorites.pet_id + WHERE favorites.user_id = ? + """, (user_id,)) + + return cursor.fetchall() + + +def fetch_all_users(): + """Retrieve all users from the accounts table.""" + with closing(connect_db()) as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM accounts") + return cursor.fetchall() + + +def fetch_all_pets(): + """Retrieve all pets from the pets table.""" + with closing(connect_db()) as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM pets") + return cursor.fetchall() + + +def fetch_user_by_id(user_id): + """Retrieve a user by their account_id.""" + with closing(connect_db()) as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM accounts WHERE user_id = ?", (user_id,)) + return cursor.fetchone() + + +def fetch_pet_by_id(pet_id): + """Retrieve a pet by their pet_id.""" + with closing(connect_db()) as conn: + cursor = conn.cursor() + cursor.execute("SELECT * FROM pets WHERE pet_id = ?", (pet_id,)) + return cursor.fetchone() + + +# ------------------------------------------------------------- diff --git a/Backend/database_files/initdatabase.py b/Backend/database_files/initdatabase.py new file mode 100644 index 0000000..e90b8d4 --- /dev/null +++ b/Backend/database_files/initdatabase.py @@ -0,0 +1,61 @@ +'''INIT TABLES IN DATABASE''' + +import sqlite3 + +def create_database_tabels(db_path): + '''THE ACTUAL FUNCITON''' + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + conn.execute("PRAGMA foreign_keys = ON") + # Create tables + cursor.executescript(''' + CREATE TABLE IF NOT EXISTS accounts ( + user_id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + password TEXT NOT NULL, + name TEXT NOT NULL, + age INTEGER, + location TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS pets ( + pet_id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + age INTEGER, + gender TEXT CHECK(gender IN ('male', 'female')), + breed TEXT, + type TEXT, + location TEXT, + photo_path TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + + + CREATE TABLE IF NOT EXISTS favorites ( + favorite_id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + pet_id INTEGER, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + + + INSERT INTO pets (name, age, gender, breed, type, location, photo_path) + VALUES + ('Bella', 3, 'female', 'Golden Retriever', 'dog', 'New York', 'images/Chuck.jpg'), + ('Max', 5, 'male', 'Maine Coon', 'cat', 'Boston', 'images/gunner.jpg'), + ('Luna', 2, 'female', 'Siamese', 'cat', 'Chicago', 'images/luna.jpg'), + ('Charlie', 4, 'male', 'Beagle', 'dog', 'Los Angeles', 'images/lizzie_izzie.jpg'), + ('Daisy', 1, 'female', 'Labrador Retriever', 'dog', 'Miami', 'images/monster.jpg'); + + + ''') + + + conn.commit() + conn.close() + +if __name__ == "__main__": + create_database_tabels('projectdatabase.db') diff --git a/Backend/docker_files/Dockerfile b/Backend/docker_files/Dockerfile new file mode 100644 index 0000000..305c2f8 --- /dev/null +++ b/Backend/docker_files/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.9-slim +WORKDIR /Docker1 + + +COPY /database_files/databasefunctions.py /Docker1/database_files/databasefunctions.py +COPY app.py app.py +COPY /database_files/initdatabase.py /Docker1/database_files/initdatabase.py + + +# Copy requirements.txt from docker_files directory into the container +COPY docker_files/requirements.txt /Docker1/requirements.txt + +# Install Python dependencies inside Docker +RUN pip3 install -r /Docker1/requirements.txt + +EXPOSE 5000 + +CMD python3 /Docker1/database_files/initdatabase.py && python3 app.py diff --git a/Backend/docker_files/requirements.txt b/Backend/docker_files/requirements.txt new file mode 100644 index 0000000..2e64847 --- /dev/null +++ b/Backend/docker_files/requirements.txt @@ -0,0 +1,4 @@ +Flask==3.0.3 +requests==2.26.0 +pytest==7.2.1 +flask_cors==3.0.10 diff --git a/Backend/images/Chuck.jpg b/Backend/images/Chuck.jpg new file mode 100644 index 0000000..0c9dedc Binary files /dev/null and b/Backend/images/Chuck.jpg differ diff --git a/Backend/images/gunner.jpg b/Backend/images/gunner.jpg new file mode 100644 index 0000000..fd5455d Binary files /dev/null and b/Backend/images/gunner.jpg differ diff --git a/Backend/images/lizzie_izzie.jpg b/Backend/images/lizzie_izzie.jpg new file mode 100644 index 0000000..68ae6d3 Binary files /dev/null and b/Backend/images/lizzie_izzie.jpg differ diff --git a/Backend/images/monster.jpg b/Backend/images/monster.jpg new file mode 100644 index 0000000..155d4c3 Binary files /dev/null and b/Backend/images/monster.jpg differ diff --git a/Backend/test_app.py b/Backend/test_app.py new file mode 100644 index 0000000..da5e088 --- /dev/null +++ b/Backend/test_app.py @@ -0,0 +1,133 @@ +""" +Pytest for fake API +""" +import requests + +UNIQUE_USER_ID = None +UNIQUE_PET_ID = None + + +def test_insert_user(): + """ + Test for http://localhost:5000/add_newuser, adding user to database + """ + global UNIQUE_USER_ID + url = "http://localhost:5000" + + UNIQUE_USER_ID = requests.post((url + "/add_newuser"), + json={"username":"sam2213", + "password":"catlover20","name": "sam", + "age": 17, "location": "jamaica"},timeout=10) + + response1 = requests.post((url + "/fetch_user"),json=UNIQUE_USER_ID.json(), timeout=10) + response2 = requests.post((url + "/fetch_allusers"), timeout=10) + + assert (response1.json() in response2.json()) is True + + +def test_insert_pet(): + """ + Test for http://localhost:5000/add_newpet, adding new pet to database to database + """ + global UNIQUE_PET_ID + url = "http://localhost:5000" + UNIQUE_PET_ID = requests.post((url + "/add_newpet"), + json={"name": "sam", "age": 17, + "gender":"female","breed":"pug", + "type":"cat","location": "jamaica", + "photo_path":"images/sam.jpg"},timeout=10) + + response1 = requests.post((url + "/fetch_pet"),json=UNIQUE_PET_ID.json(), timeout=10) + response2 = requests.post((url + "/fetch_allpets"), timeout=10) + + assert (response1.json() in response2.json()) is True + +def test_insert_favorite_relation(): + """ + Test for http://localhost:5000/add_newfavorite, adding users favorited pet into the database + """ + global UNIQUE_USER_ID + global UNIQUE_PET_ID + + url = "http://localhost:5000" + + + requests.post((url + "/add_newfavorite"), + json={"user_id": UNIQUE_USER_ID.json().get("user_id"), + "pet_id": UNIQUE_PET_ID.json().get("pet_id")},timeout=10) + + response1 = requests.post((url + "/fetch_pet"),json=UNIQUE_PET_ID.json(), timeout=10) + response2 = requests.post((url + "/fetch_favorited_pets"),json=UNIQUE_USER_ID.json(),timeout=10) + + assert (response1.json() in response2.json()) is True + + +# ----------------------------------------------------------------------------- + +def test_fetch_userid(): + """ + Test for http://localhost:5000/fetch_userid, + retrieve userid accosiated with username and password + """ + + global UNIQUE_USER_ID + global UNIQUE_PET_ID + + url = "http://localhost:5000" + userid = requests.post((url + "/fetch_userid"), + json={"username": "sam2213", + "password": "catlover20"}, timeout=10) + + assert userid.json() == UNIQUE_USER_ID.json().get("user_id") + +# ---------------------------------------------------------------------------------- + +def test_remove_favorite_relation(): + """ + Test for http://localhost:5000/remove_favorite, remove users favorited pet from the database + """ + + global UNIQUE_USER_ID + global UNIQUE_PET_ID + + url = "http://localhost:5000" + + requests.delete((url + "/remove_favorite"), + json={"user_id": UNIQUE_USER_ID.json().get("user_id"), + "pet_id": UNIQUE_PET_ID.json().get("pet_id")},timeout=10) + + + response = requests.post((url + "/fetch_favorited_pets"),json=UNIQUE_USER_ID.json(),timeout=10) + + assert (response.json()) == [] + + +def test_remove_user(): + """ + Test for http://localhost:5000/remove_user + """ + + global UNIQUE_USER_ID + global UNIQUE_PET_ID + + url = "http://localhost:5000" + + requests.delete((url + "/remove_user"), json=UNIQUE_USER_ID.json(), timeout=10) + + response = requests.post((url + "/fetch_user"),json=UNIQUE_USER_ID.json(), timeout=10) + + assert response.json() is None + + +def test_remove_pet(): + """ + Test for http://localhost:5000/remove_pet + """ + + url = "http://localhost:5000" + + requests.delete((url + "/remove_pet"), json=UNIQUE_PET_ID.json(), timeout=10) + + response = requests.post((url + "/fetch_pet"),json=UNIQUE_PET_ID.json(), timeout=10) + + assert response.json() is None diff --git a/Docs/READMEMS6.md b/Docs/READMEMS6.md new file mode 100644 index 0000000..936f556 --- /dev/null +++ b/Docs/READMEMS6.md @@ -0,0 +1,28 @@ +# Link to Trello: +https://trello.com/b/j8t0Ulvk/group-51-jira-kaban-board + +### + +# Link to Figma: +https://www.figma.com/design/mKMWW1sIOVpuQKOlapBINQ/ishayu.ray + + + +### +### +### + +# Starting the Database. +1. The Dockerfile in the Backend folder populates the database. + **MAKE SURE YOU ARE IN THE BACKEND FOLDER IN YOUR TERMINAL!** + +2. Use this command below to build the image + + **docker build -f Dockerfile -t team51_backend .** + +3. Then run the docker together with this command + + **docker run -p 5000:5000 team51_backend** + + + \ No newline at end of file diff --git a/Docs/lecutre10-30CE.txt b/Docs/lecutre10-30CE.txt new file mode 100644 index 0000000..7df2202 --- /dev/null +++ b/Docs/lecutre10-30CE.txt @@ -0,0 +1,6 @@ +JSX components needed + +1. Pet Box- image, name, info about pet etc. +2. Route page- reusable image, info, detailed summary +3. Favorite page (horizontal info)- reusable +4. Quiz boxes- reusable format \ No newline at end of file diff --git a/README.md b/README.md index 7a7c605..112d09b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,15 @@ -# cse2102-fall24-Team51 -Eric Asante eoa21004 -Kyle Kirejczyk kek20009 -Ishayu Ray isr21011 -Kunal bagga kub21001 -Link to Trello: https://trello.com/b/j8t0Ulvk/group-51-jira-kaban-board -Link to Figma Prototype: https://www.figma.com/design/mKMWW1sIOVpuQKOlapBINQ/ishayu.ray \ No newline at end of file +# CSE2102-fall24-Team51 + +Eric Asante eoa21004
+Kyle Kirejczyk kek20009
+Ishayu Ray isr21011
+Kunal Bagga kub21001
+ +Link to Trello: [https://trello.com/b/j8t0Ulvk/group-51-jira-kaban-board](https://trello.com/b/j8t0Ulvk/group-51-jira-kaban-board)
+Link to Figma Prototype: [https://www.figma.com/design/mKMWW1sIOVpuQKOlapBINQ/ishayu.ray](https://www.figma.com/design/mKMWW1sIOVpuQKOlapBINQ/ishayu.ray)
+ +# Terminal commands to run DOCKERIZED API:
+Make sure you cd into Backend folder.
+1. docker build -f docker_files/Dockerfile -t team51-backend .
+2. docker run -d -p 5000:5000 team51-backend
+3. pytest diff --git a/desktop/cse2102-fall24-Team51 b/desktop/cse2102-fall24-Team51 new file mode 160000 index 0000000..92f81d9 --- /dev/null +++ b/desktop/cse2102-fall24-Team51 @@ -0,0 +1 @@ +Subproject commit 92f81d9af1a5570883485716bf419aa43bef0304