Skip to content

Commit

Permalink
Merge pull request #14 from tylerwong1/csv
Browse files Browse the repository at this point in the history
CSV Upload Implementation
  • Loading branch information
tylerwong1 authored Oct 28, 2024
2 parents 1aee85e + 228e42d commit 9a7790d
Show file tree
Hide file tree
Showing 11 changed files with 579 additions and 93 deletions.
13 changes: 13 additions & 0 deletions server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
cd server

install_windows.bat or install_unix.sh

python app.py

http --session=budgetai_session POST http://localhost:8080/user/signup name="John Doe" email="[email protected]" password="password123"

http --session=budgetai_session POST http://localhost:8080/user/login email="[email protected]" password="password123"

http --session=budgetai_session --form POST http://localhost:8080/upload file@files/chase_freedom.csv

http --session=budgetai_session POST http://localhost:8080/user/wipe password="password123"
40 changes: 30 additions & 10 deletions server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@
from functools import wraps

from dotenv import load_dotenv
from flask import Flask, jsonify, redirect, session
from flask import Flask, jsonify, redirect, request, session
from werkzeug.utils import secure_filename

from upload.upload import Upload
from users.routes import user_routes

# Application
app = Flask(__name__)
load_dotenv()
app.secret_key = os.getenv("FLASK_SECRET_KEY")
app.config["SESSION_COOKIE_NAME"] = "budgetai_session"
app.config["SESSION_TYPE"] = "filesystem"
app.config["UPLOAD_FOLDER"] = "files"


# Decorators
Expand Down Expand Up @@ -49,17 +54,32 @@ def home():
return "Home"


@app.route("/dashboard")
@login_required # Apply the login_required decorator to this route
def dashboard():
@app.route("/upload", methods=["POST"])
@login_required
def upload():
"""
Dashboard route.
Accessible only to logged-in users.
Returns a string indicating the user's dashboard, along with their session information.
Upload route for processing a CSV file.
This route accepts a file upload directly.
Returns:
JSON response indicating success or failure of the upload process.
"""
return (
"Dashboard: " + session["user"]["name"]
) # Display the user's name from the session
# Validate the file
if "file" not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files["file"]
if file.filename == "":
return jsonify({"error": "No selected file"}), 400

# Save the file to the flask upload folder
filename = secure_filename(file.filename) # Secure the file name
file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename)
file.save(file_path) # Save the file to the upload folder

# Process the CSV file
Upload().process_csv(file_path)

return jsonify({"message": "File uploaded and processed successfully"}), 200


@app.route("/status", methods=["GET"])
Expand Down
2 changes: 1 addition & 1 deletion server/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pymongo import MongoClient


def get_login_system_db():
def get_budgetai_db():
client = MongoClient("mongodb://localhost:27017/")
environment = os.getenv(
"FLASK_ENV", "production"
Expand Down
67 changes: 67 additions & 0 deletions server/files/chase_freedom.CSV
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Transaction Date,Post Date,Description,Category,Type,Amount,Memo
09/30/2024,10/01/2024,DOLLAR TREE,Shopping,Sale,-1.29,
09/30/2024,09/30/2024,Payment Thank You-Mobile,,Payment,1.29,
09/27/2024,09/29/2024,MTA*NYCT PAYGO,Travel,Sale,-2.90,
09/28/2024,09/29/2024,MTA*NYCT PAYGO,Travel,Sale,-2.90,
09/27/2024,09/27/2024,SP MAGNOLIABAKERY,Food & Drink,Sale,-10.70,
09/25/2024,09/26/2024,FOOD STORY NATURAL MARKE,Groceries,Sale,-11.98,
09/23/2024,09/25/2024,TRADER JOE S #539,Groceries,Sale,-27.71,
09/24/2024,09/25/2024,HOLLISTER CO.,Shopping,Sale,-57.38,
09/19/2024,09/20/2024,FOOD STORY NATURAL MARKE,Groceries,Sale,-26.96,
09/18/2024,09/19/2024,SQ *MATTO 9,Food & Drink,Sale,-10.89,
09/17/2024,09/19/2024,TST*7TH STREET BURGER,Food & Drink,Sale,-19.04,
08/28/2024,08/28/2024,Payment Thank You-Mobile,,Payment,28.10,
08/23/2024,08/25/2024,EXXON WINTER SPRINGS F,Gas,Sale,-17.60,
08/22/2024,08/23/2024,Microsoft*Xbox,Shopping,Return,19.99,
08/22/2024,08/23/2024,SQ *LIZHAIRLLC,Personal,Sale,-33.75,
08/21/2024,08/21/2024,Offer:UNIQLO USA,Fees & Adjustments,Adjustment,3.26,
08/20/2024,08/20/2024,Payment Thank You-Mobile,,Payment,19.99,
08/19/2024,08/19/2024,Payment Thank You-Mobile,,Payment,862.08,
08/19/2024,08/19/2024,Microsoft*Xbox,Shopping,Sale,-19.99,
08/17/2024,08/18/2024,AVIS RENT-A-CAR,Travel,Sale,-789.49,
08/15/2024,08/18/2024,SHELL OIL 57444213706,Gas,Sale,-40.00,
08/16/2024,08/18/2024,Uniqlo USA LLC,Shopping,Sale,-32.59,
08/13/2024,08/13/2024,Payment Thank You-Mobile,,Payment,12.40,
08/11/2024,08/12/2024,SQ *PINEAPPLE KING BAKERY,Food & Drink,Sale,-12.40,
08/07/2024,08/08/2024,Payment Thank You-Mobile,,Payment,12.66,
08/06/2024,08/06/2024,Payment Thank You-Mobile,,Payment,194.47,
08/05/2024,08/06/2024,SPOTHERO 844-356-8054,Travel,Sale,-12.66,
08/05/2024,08/05/2024,Payment Thank You - Web,,Payment,65.00,
08/02/2024,08/04/2024,SDOT PAYBYPHONE PARKING,Travel,Sale,-6.02,
08/04/2024,08/04/2024,Nike.com,Shopping,Sale,-149.80,
08/03/2024,08/04/2024,76 - AMLIB KAUSHIK INC,Gas,Sale,-61.35,
08/01/2024,08/04/2024,TST*NANAS GREEN TEA 2022,Food & Drink,Sale,-16.26,
08/02/2024,08/02/2024,Payment Thank You-Mobile,,Payment,535.24,
07/31/2024,08/02/2024,TST*MOX BOARDING HOUSE -,Food & Drink,Sale,-26.04,
07/30/2024,08/01/2024,TARGET 00027862,Shopping,Sale,-4.40,
07/31/2024,07/31/2024,Payment Thank You - Web,,Payment,170.25,
07/31/2024,07/31/2024,SPOTHERO 844-356-8054,Travel,Sale,-5.28,
07/29/2024,07/30/2024,TST* WET CLAY CAFE,Food & Drink,Sale,-30.00,
07/28/2024,07/30/2024,SAFEWAY #1477,Groceries,Sale,-29.31,
07/29/2024,07/30/2024,CENTRAL CO-OP,Groceries,Sale,-14.72,
07/29/2024,07/29/2024,Payment Thank You-Mobile,,Payment,500.00,
07/27/2024,07/29/2024,SDOT PAYBYPHONE PARKING,Travel,Sale,-9.00,
07/27/2024,07/29/2024,POTBELLY #374,Food & Drink,Sale,-10.72,
07/26/2024,07/28/2024,CAFE ALOE,Food & Drink,Sale,-25.60,
07/25/2024,07/26/2024,ETOLLAVIS U73596014,Travel,Sale,-26.75,
07/25/2024,07/26/2024,SQ *RACHEL'S GINGER BEER,Food & Drink,Sale,-6.60,
07/24/2024,07/25/2024,ETOLLAVIS U73582785,Travel,Sale,-1.00,
07/24/2024,07/25/2024,SQ *SEATTLE DONUT BOAT LL,Entertainment,Sale,-159.86,
07/23/2024,07/24/2024,Payment Thank You-Mobile,,Payment,212.00,
07/22/2024,07/22/2024,TOUR* 24HR POTTER,Shopping,Sale,-212.00,
07/22/2024,07/22/2024,Payment Thank You-Mobile,,Payment,40.00,
07/20/2024,07/21/2024,SQ *PINEAPPLE KING BAKERY,Food & Drink,Sale,-6.05,
07/18/2024,07/19/2024,AVIS RENT-A-CAR,Travel,Sale,-772.98,
07/17/2024,07/17/2024,Nike.com,Shopping,Sale,-94.12,
07/15/2024,07/16/2024,TARGET 00027862,Shopping,Return,10.90,
07/10/2024,07/12/2024,SHELL OIL 57444213706,Gas,Sale,-60.00,
07/06/2024,07/07/2024,Payment Thank You-Mobile,,Payment,10.48,
07/04/2024,07/05/2024,Payment Thank You-Mobile,,Payment,98.16,
07/03/2024,07/05/2024,SAFEWAY #1196,Groceries,Sale,-10.48,
07/02/2024,07/03/2024,SIMPLE BILLS,Bills & Utilities,Sale,-8.17,
07/02/2024,07/02/2024,Payment Thank You-Mobile,,Payment,229.03,
06/30/2024,07/02/2024,SHELL OIL 12616066002,Gas,Sale,-60.00,
07/01/2024,07/02/2024,GOOGLE *Google Storage,Professional Services,Sale,-29.99,
06/30/2024,07/01/2024,POINT LOBOS STATE NATURA,Bills & Utilities,Sale,-10.00,
06/29/2024,07/01/2024,BOARDWALK MINI GOLF,Entertainment,Sale,-20.00,
06/30/2024,07/01/2024,SQ *PARKS MANAGEMENT COMP,Entertainment,Sale,-15.00,
8 changes: 3 additions & 5 deletions server/tests/test_flask_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ def test_status(self):
- That the status code of the response is 200 (OK).
- That the response JSON contains the expected message indicating the application is running.
"""
response = self.app.get("/status") # Send GET request to /status
self.assertEqual(response.status_code, 200) # Check if status code is 200
self.assertEqual(
response.json, {"message": "Application is running"}
) # Check if response data is correct
response = self.app.get("/status")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json, {"message": "Application is running"})


if __name__ == "__main__":
Expand Down
123 changes: 123 additions & 0 deletions server/tests/test_upload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import base64
import io
import os
import unittest

from app import app
from database import get_budgetai_db


class UploadTest(unittest.TestCase):
def setUp(self):
"""
Set up a test client for the Flask app with a secret key and testing mode.
Establish an application context for testing.
Create sample transaction data and csv for testing.
"""
self.app = app.test_client()
self.app.secret_key = "test_secret_key"
self.app.testing = True
self.app_context = app.app_context()
self.app_context.push()

# Define sample transaction data
self.sample_transaction_data = {
"transaction_date": "09/30/2024",
"post_date": "10/01/2024",
"description": "DOLLAR TREE",
"category": "Shopping",
"type": "Sale",
"amount": -1.29,
"memo": "",
}

# Prepare the sample CSV content
self.sample_csv = (
"Transaction Date,Post Date,Description,Category,Type,Amount,Memo\n"
f"{self.sample_transaction_data['transaction_date']},"
f"{self.sample_transaction_data['post_date']},"
f"{self.sample_transaction_data['description']},"
f"{self.sample_transaction_data['category']},"
f"{self.sample_transaction_data['type']},"
f"{self.sample_transaction_data['amount']},"
f"{self.sample_transaction_data['memo']}\n"
)

def tearDown(self):
"""
Clean up the app context after each test to avoid side effects.
"""
self.app_context.pop()

@classmethod
def setUpClass(cls):
"""
Set up the test environment and database connection before any tests run.
Configures the application to use a test database.
"""
os.environ["FLASK_ENV"] = "test" # Use the test environment
cls.db, cls.client = get_budgetai_db()

@classmethod
def tearDownClass(cls):
"""
Clean up the database by deleting all users after all tests have run.
Close the database client connection.
"""
cls.db["transactions"].delete_many({})
cls.db["users"].delete_many({})
cls.client.close()

def test_upload(self):
# Create and login new user
self.app.post(
"/user/signup",
json={
"name": "Test User",
"email": "[email protected]",
"password": "password123",
},
)
self.app.post(
"/user/login", json={"email": "[email protected]", "password": "password123"}
)

# Create a BytesIO object from the sample CSV content
file_stream = io.BytesIO(self.sample_csv.encode("utf-8"))

# Prepare the data for the POST request
data = {"file": (file_stream, "sample.csv")} # Use a tuple to specify filename

# Send a POST request to the /upload endpoint
response = self.app.post("/upload", data=data)

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

# Check if the message in the JSON response matches the expected success message
self.assertEqual(
response.json.get("message"), "File uploaded and processed successfully"
)

# Verify if the transaction was inserted into the database
inserted_user = self.db["users"].find_one({"email": "[email protected]"})
inserted_transaction = self.db["transactions"].find_one(
{"user_id": inserted_user["_id"]}
)

# Verify inserted transaction values
self.assertEqual(
inserted_transaction["transaction_date"],
self.sample_transaction_data["transaction_date"],
)
self.assertEqual(
inserted_transaction["description"],
self.sample_transaction_data["description"],
)
self.assertEqual(
inserted_transaction["category"], self.sample_transaction_data["category"]
)


if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit 9a7790d

Please sign in to comment.