From 0ed1b94c48ac61aaed7ce616cd73cf3ab0e30242 Mon Sep 17 00:00:00 2001 From: kaye-s Date: Wed, 11 Feb 2026 17:41:22 -0600 Subject: [PATCH 01/18] test! --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index cedf85d..994422a 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import eel +#sofia eel.init('front-end') From 69a3118b255aff665e2979e9e76620eb68dc6fe4 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 11 Feb 2026 17:45:11 -0600 Subject: [PATCH 02/18] Jacob Test --- main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/main.py b/main.py index 994422a..b600c9f 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,6 @@ import eel #sofia +#jacob eel.init('front-end') From 38602780efbbca505da7c3b581f0728f506d8530 Mon Sep 17 00:00:00 2001 From: zhangtingen Date: Wed, 11 Feb 2026 17:56:19 -0600 Subject: [PATCH 03/18] tim --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index b600c9f..aae1daa 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ import eel #sofia #jacob - +#tim eel.init('front-end') From 99a712e2ce34ff082fef4071a1dc454823baec33 Mon Sep 17 00:00:00 2001 From: zhangtingen Date: Thu, 12 Feb 2026 12:59:29 -0600 Subject: [PATCH 04/18] connect openAI api to backend --- .gitignore | 1 + main.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/.gitignore b/.gitignore index 3557e5d..06125d6 100644 --- a/.gitignore +++ b/.gitignore @@ -189,3 +189,4 @@ gradle-app.setting /.vs/ node_modules/ +.env diff --git a/main.py b/main.py index aae1daa..a64024d 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,25 @@ import eel +import os +from dotenv import load_dotenv +from openai import OpenAI #sofia #jacob #tim eel.init('front-end') +load_dotenv() +client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) + +def test_openai(): + if not os.getenv("OPENAI_API_KEY"): + print("ERROR: OPENAI_API_KEY not found. Put it in .env (same folder as main.py).") + return + + resp = client.chat.completions.create( + model="gpt-4.1-mini", + messages=[{"role": "user", "content": "Say hello in one sentence."}], + ) + print("OpenAI test reply:", resp.choices[0].message.content) @eel.expose def add(num1, num2): From 3b4c170e00c2cddc1672cdaed1c605a44dc4f203 Mon Sep 17 00:00:00 2001 From: zhangtingen Date: Thu, 12 Feb 2026 21:30:00 -0600 Subject: [PATCH 05/18] connet frontend-backend-openai api --- front-end/index.html | 23 +++++++++++++---------- front-end/scripts/main.js | 11 +++++++++++ main.py | 26 +++++++++++++++++--------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/front-end/index.html b/front-end/index.html index 3a3ade3..e2ab344 100644 --- a/front-end/index.html +++ b/front-end/index.html @@ -1,25 +1,28 @@ - - Hello World! - + + - - - - - - + + + +
- \ No newline at end of file + + + + +

+
+
diff --git a/front-end/scripts/main.js b/front-end/scripts/main.js
index feeebf0..56a9f67 100644
--- a/front-end/scripts/main.js
+++ b/front-end/scripts/main.js
@@ -6,3 +6,14 @@ function operate(operator) {
 		document.querySelector('#output').innerText = result;
 	});
 }
+
+function askGPT() {
+	const prompt = document.querySelector('#prompt').value;
+
+	document.querySelector('#output').innerText = "Loading...";
+
+	eel.ask_api(prompt)(result => {
+		document.querySelector('#output').innerText = result;
+	});
+
+}
\ No newline at end of file
diff --git a/main.py b/main.py
index a64024d..7a8aab7 100644
--- a/main.py
+++ b/main.py
@@ -8,18 +8,25 @@
 eel.init('front-end')
 
 load_dotenv()
+print("API key loaded:", bool(os.getenv("OPENAI_API_KEY")))
 client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
 
-def test_openai():
-    if not os.getenv("OPENAI_API_KEY"):
-        print("ERROR: OPENAI_API_KEY not found. Put it in .env (same folder as main.py).")
-        return
+@eel.expose
+def ask_api(user_text):
+    print("ask_api received:", user_text)
 
     resp = client.chat.completions.create(
-        model="gpt-4.1-mini",
-        messages=[{"role": "user", "content": "Say hello in one sentence."}],
-    )
-    print("OpenAI test reply:", resp.choices[0].message.content)
+            model="gpt-4.1-mini",
+            messages=[{"role": "user", "content": str(user_text)}],
+        )
+
+    answer = resp.choices[0].message.content
+    print("ask_api answer:", answer)
+    return answer
+
+
+
+
 
 @eel.expose
 def add(num1, num2):
@@ -31,4 +38,5 @@ def subtract(num1, num2):
     return int(num1) - int(num2)
 
 
-eel.start('index.html', size=(1000, 600))
+if __name__ == "__main__":
+    eel.start('index.html', size=(1000, 600), mode='safari')

From 0ae235de84cd195a6a1de2cdcd6cedc70c61990c Mon Sep 17 00:00:00 2001
From: kaye-s 
Date: Mon, 16 Feb 2026 16:13:48 -0600
Subject: [PATCH 06/18] database connected to enviornment

---
 db.py            | 21 ++++++++++++++
 main.py          |  5 ++++
 models.py        | 74 ++++++++++++++++++++++++++++++++++++++++++++++++
 requirements.txt |  3 ++
 testquery.py     | 27 ++++++++++++++++++
 5 files changed, 130 insertions(+)
 create mode 100644 db.py
 create mode 100644 models.py
 create mode 100644 testquery.py

diff --git a/db.py b/db.py
new file mode 100644
index 0000000..4212ab5
--- /dev/null
+++ b/db.py
@@ -0,0 +1,21 @@
+import os
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from dotenv import load_dotenv
+
+load_dotenv()
+
+DB_URL = f"postgresql://" \
+         f"{os.getenv('DB_USER')}:" \
+         f"{os.getenv('DB_PASS')}@" \
+         f"{os.getenv('DB_HOST')}:" \
+         f"{os.getenv('DB_PORT')}/" \
+         f"{os.getenv('DB_NAME')}"
+
+engine = create_engine(
+    DB_URL,
+    echo=True,
+    connect_args={"sslmode": "require"}
+)
+
+SessionLocal = sessionmaker(bind=engine)
diff --git a/main.py b/main.py
index aae1daa..579b0b6 100644
--- a/main.py
+++ b/main.py
@@ -1,9 +1,14 @@
 import eel
+from db import SessionLocal
 #sofia
 #jacob
 #tim
 eel.init('front-end')
 
+session = SessionLocal()
+
+print("Connected successfully!")
+
 
 @eel.expose
 def add(num1, num2):
diff --git a/models.py b/models.py
new file mode 100644
index 0000000..6da1d64
--- /dev/null
+++ b/models.py
@@ -0,0 +1,74 @@
+import os
+from sqlalchemy import create_engine, Column, Integer, String, Text, DateTime, ForeignKey, Numeric, Enum
+from sqlalchemy.orm import declarative_base, relationship, sessionmaker
+from datetime import datetime
+import enum
+from dotenv import load_dotenv
+
+load_dotenv()
+
+Base = declarative_base()
+
+# Severity Enum
+class SeverityEnum(enum.Enum):
+    Low = "Low"
+    Medium = "Medium"
+    High = "High"
+    Critical = "Critical"
+
+
+class User(Base):
+    __tablename__ = "users"
+
+    user_id = Column(Integer, primary_key=True, autoincrement=True)
+    email = Column(String(255), unique=True, nullable=False)
+    password_hash = Column(Text, nullable=False)
+    created_at = Column(DateTime, default=datetime.utcnow)
+
+    submissions = relationship("CodeSubmission", back_populates="user")
+
+
+class CodeSubmission(Base):
+    __tablename__ = "code_submissions"
+
+    submission_id = Column(Integer, primary_key=True, autoincrement=True)
+    user_id = Column(Integer, ForeignKey("users.user_id", ondelete="CASCADE"), nullable=False)
+    submission_name = Column(String(255))
+    uploaded_at = Column(DateTime, default=datetime.utcnow)
+    overall_risk_score = Column(Numeric(5,2))
+    simplified_summary = Column(Text)
+    detailed_summary = Column(Text)
+
+    user = relationship("User", back_populates="submissions")
+    files = relationship("File", back_populates="submission")
+    threats = relationship("Threat", back_populates="submission")
+
+
+class File(Base):
+    __tablename__ = "files"
+
+    file_id = Column(Integer, primary_key=True, autoincrement=True)
+    submission_id = Column(Integer, ForeignKey("code_submissions.submission_id", ondelete="CASCADE"), nullable=False)
+    file_name = Column(String(255), nullable=False)
+    file_path = Column(Text, nullable=False)
+    file_type = Column(String(100))
+
+    submission = relationship("CodeSubmission", back_populates="files")
+    threats = relationship("Threat", back_populates="file")
+
+
+class Threat(Base):
+    __tablename__ = "threats"
+
+    threat_id = Column(Integer, primary_key=True, autoincrement=True)
+    submission_id = Column(Integer, ForeignKey("code_submissions.submission_id", ondelete="CASCADE"), nullable=False)
+    file_id = Column(Integer, ForeignKey("files.file_id", ondelete="SET NULL"), nullable=True)
+    title = Column(String(255), nullable=False)
+    description = Column(Text)
+    severity_level = Column(Enum(SeverityEnum), nullable=False)
+    severity_score = Column(Numeric(5,2))
+    recommendation = Column(Text)
+    line_number = Column(Integer)
+
+    submission = relationship("CodeSubmission", back_populates="threats")
+    file = relationship("File", back_populates="threats")
diff --git a/requirements.txt b/requirements.txt
index e7c13db..c0be71b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,3 +3,6 @@ pyqrcode
 pyinstaller
 pypng
 autopep8
+psycopg2-binary
+SQLAlchemy
+python-dotenv
diff --git a/testquery.py b/testquery.py
new file mode 100644
index 0000000..cf61dd8
--- /dev/null
+++ b/testquery.py
@@ -0,0 +1,27 @@
+from sqlalchemy import create_engine, text
+
+# Replace these with your Supabase info
+USER = "postgres"
+PASSWORD = "CapstoneVSecurity123"
+HOST = "db.zzraywtbowpotrqbevkz.supabase.co"
+PORT = "5432"
+DATABASE = "postgres"
+
+# This is the connection URL SQLAlchemy needs
+DB_URL = f"postgresql+psycopg2://{USER}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}"
+
+# Create a connection
+engine = create_engine(DB_URL)
+
+try:
+    with engine.connect() as conn:
+        # Run a simple query to test
+        result = conn.execute(text("SELECT NOW();"))
+        print("Connected! Server time:", result.fetchone()[0])
+except Exception as e:
+    print("Connection failed:", e)
+
+with engine.connect() as conn:
+    #conn.execute(text("INSERT INTO users(email, password_hash) VALUES ('test2@example.com', '1A2B3C');"))
+    result = conn.execute(text("SELECT * FROM users;"))
+    print(result.fetchall())
\ No newline at end of file

From 5f3d3fad0f303676c92e79b6e25dc38983b82e83 Mon Sep 17 00:00:00 2001
From: kaye-s 
Date: Wed, 18 Feb 2026 12:42:46 -0600
Subject: [PATCH 07/18] database secrets preserved

---
 testquery.py | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/testquery.py b/testquery.py
index cf61dd8..80e1505 100644
--- a/testquery.py
+++ b/testquery.py
@@ -1,17 +1,5 @@
-from sqlalchemy import create_engine, text
-
-# Replace these with your Supabase info
-USER = "postgres"
-PASSWORD = "CapstoneVSecurity123"
-HOST = "db.zzraywtbowpotrqbevkz.supabase.co"
-PORT = "5432"
-DATABASE = "postgres"
-
-# This is the connection URL SQLAlchemy needs
-DB_URL = f"postgresql+psycopg2://{USER}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}"
-
-# Create a connection
-engine = create_engine(DB_URL)
+from sqlalchemy import text
+from db import engine
 
 try:
     with engine.connect() as conn:

From d85a13a877983ca4d5be09842c740bc15c1b3af1 Mon Sep 17 00:00:00 2001
From: kaye-s 
Date: Wed, 18 Feb 2026 12:43:30 -0600
Subject: [PATCH 08/18] Revert "database connected to enviornment"

This reverts commit 0ae235de84cd195a6a1de2cdcd6cedc70c61990c.

# Conflicts:
#	testquery.py
---
 db.py            | 21 --------------
 main.py          |  5 ----
 models.py        | 74 ------------------------------------------------
 requirements.txt |  3 --
 4 files changed, 103 deletions(-)
 delete mode 100644 db.py
 delete mode 100644 models.py

diff --git a/db.py b/db.py
deleted file mode 100644
index 4212ab5..0000000
--- a/db.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-from sqlalchemy import create_engine
-from sqlalchemy.orm import sessionmaker
-from dotenv import load_dotenv
-
-load_dotenv()
-
-DB_URL = f"postgresql://" \
-         f"{os.getenv('DB_USER')}:" \
-         f"{os.getenv('DB_PASS')}@" \
-         f"{os.getenv('DB_HOST')}:" \
-         f"{os.getenv('DB_PORT')}/" \
-         f"{os.getenv('DB_NAME')}"
-
-engine = create_engine(
-    DB_URL,
-    echo=True,
-    connect_args={"sslmode": "require"}
-)
-
-SessionLocal = sessionmaker(bind=engine)
diff --git a/main.py b/main.py
index 579b0b6..aae1daa 100644
--- a/main.py
+++ b/main.py
@@ -1,14 +1,9 @@
 import eel
-from db import SessionLocal
 #sofia
 #jacob
 #tim
 eel.init('front-end')
 
-session = SessionLocal()
-
-print("Connected successfully!")
-
 
 @eel.expose
 def add(num1, num2):
diff --git a/models.py b/models.py
deleted file mode 100644
index 6da1d64..0000000
--- a/models.py
+++ /dev/null
@@ -1,74 +0,0 @@
-import os
-from sqlalchemy import create_engine, Column, Integer, String, Text, DateTime, ForeignKey, Numeric, Enum
-from sqlalchemy.orm import declarative_base, relationship, sessionmaker
-from datetime import datetime
-import enum
-from dotenv import load_dotenv
-
-load_dotenv()
-
-Base = declarative_base()
-
-# Severity Enum
-class SeverityEnum(enum.Enum):
-    Low = "Low"
-    Medium = "Medium"
-    High = "High"
-    Critical = "Critical"
-
-
-class User(Base):
-    __tablename__ = "users"
-
-    user_id = Column(Integer, primary_key=True, autoincrement=True)
-    email = Column(String(255), unique=True, nullable=False)
-    password_hash = Column(Text, nullable=False)
-    created_at = Column(DateTime, default=datetime.utcnow)
-
-    submissions = relationship("CodeSubmission", back_populates="user")
-
-
-class CodeSubmission(Base):
-    __tablename__ = "code_submissions"
-
-    submission_id = Column(Integer, primary_key=True, autoincrement=True)
-    user_id = Column(Integer, ForeignKey("users.user_id", ondelete="CASCADE"), nullable=False)
-    submission_name = Column(String(255))
-    uploaded_at = Column(DateTime, default=datetime.utcnow)
-    overall_risk_score = Column(Numeric(5,2))
-    simplified_summary = Column(Text)
-    detailed_summary = Column(Text)
-
-    user = relationship("User", back_populates="submissions")
-    files = relationship("File", back_populates="submission")
-    threats = relationship("Threat", back_populates="submission")
-
-
-class File(Base):
-    __tablename__ = "files"
-
-    file_id = Column(Integer, primary_key=True, autoincrement=True)
-    submission_id = Column(Integer, ForeignKey("code_submissions.submission_id", ondelete="CASCADE"), nullable=False)
-    file_name = Column(String(255), nullable=False)
-    file_path = Column(Text, nullable=False)
-    file_type = Column(String(100))
-
-    submission = relationship("CodeSubmission", back_populates="files")
-    threats = relationship("Threat", back_populates="file")
-
-
-class Threat(Base):
-    __tablename__ = "threats"
-
-    threat_id = Column(Integer, primary_key=True, autoincrement=True)
-    submission_id = Column(Integer, ForeignKey("code_submissions.submission_id", ondelete="CASCADE"), nullable=False)
-    file_id = Column(Integer, ForeignKey("files.file_id", ondelete="SET NULL"), nullable=True)
-    title = Column(String(255), nullable=False)
-    description = Column(Text)
-    severity_level = Column(Enum(SeverityEnum), nullable=False)
-    severity_score = Column(Numeric(5,2))
-    recommendation = Column(Text)
-    line_number = Column(Integer)
-
-    submission = relationship("CodeSubmission", back_populates="threats")
-    file = relationship("File", back_populates="threats")
diff --git a/requirements.txt b/requirements.txt
index c0be71b..e7c13db 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,6 +3,3 @@ pyqrcode
 pyinstaller
 pypng
 autopep8
-psycopg2-binary
-SQLAlchemy
-python-dotenv

From 8377edc021735d1ec31fb97bfd7acc565c560162 Mon Sep 17 00:00:00 2001
From: kaye-s 
Date: Wed, 18 Feb 2026 12:49:04 -0600
Subject: [PATCH 09/18] OKAY NOW FIXED, be sure to get .env file updated with
 new credentials

---
 db.py            | 21 +++++++++++++++++++++
 requirements.txt |  3 +++
 2 files changed, 24 insertions(+)
 create mode 100644 db.py

diff --git a/db.py b/db.py
new file mode 100644
index 0000000..5afc09b
--- /dev/null
+++ b/db.py
@@ -0,0 +1,21 @@
+import os
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+from dotenv import load_dotenv
+
+load_dotenv()
+
+DB_URL = f"postgresql://" \
+         f"{os.getenv('DB_USER')}:" \
+         f"{os.getenv('DB_PASS')}@" \
+         f"{os.getenv('DB_HOST')}:" \
+         f"{os.getenv('DB_PORT')}/" \
+         f"{os.getenv('DB_NAME')}"
+
+engine = create_engine(
+    DB_URL,
+    echo=True,
+    connect_args={"sslmode": "require"}
+)
+
+SessionLocal = sessionmaker(bind=engine)
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index e7c13db..c0be71b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,3 +3,6 @@ pyqrcode
 pyinstaller
 pypng
 autopep8
+psycopg2-binary
+SQLAlchemy
+python-dotenv

From 012e4180e0d1721f071b93df7329d7154d0811e0 Mon Sep 17 00:00:00 2001
From: kaye-s 
Date: Wed, 18 Feb 2026 13:08:46 -0600
Subject: [PATCH 10/18] showuser add user database functionality linked up

---
 front-end/index.html      |  7 +++++++
 front-end/scripts/main.js | 15 +++++++++++++++
 main.py                   | 28 +++++++++++++++++++++++++++-
 3 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/front-end/index.html b/front-end/index.html
index 3a3ade3..bd942c2 100644
--- a/front-end/index.html
+++ b/front-end/index.html
@@ -19,6 +19,13 @@
     
     
     
+    
+
+ + + + + diff --git a/front-end/scripts/main.js b/front-end/scripts/main.js index feeebf0..133a2ba 100644 --- a/front-end/scripts/main.js +++ b/front-end/scripts/main.js @@ -6,3 +6,18 @@ function operate(operator) { document.querySelector('#output').innerText = result; }); } + +function loadUsers() { + eel.showUsers()(users => { + document.querySelector('#output').innerText = JSON.stringify(users, null, 2); + }); +} + +function addUsers() { + var email = document.querySelector('#email').value; + var password = document.querySelector('#pass').value; + + eel.addUsers(email, password)(response => { + document.querySelector('#output').innerText = response; + }); +} \ No newline at end of file diff --git a/main.py b/main.py index aae1daa..e94e512 100644 --- a/main.py +++ b/main.py @@ -1,18 +1,44 @@ import eel +from db import engine +from sqlalchemy import text #sofia #jacob #tim eel.init('front-end') +try: + with engine.connect() as conn: + # Run a simple query to test + result = conn.execute(text("SELECT NOW();")) + print("Connected! Server time:", result.fetchone()[0]) +except Exception as e: + print("Connection failed:", e) @eel.expose def add(num1, num2): return int(num1) + int(num2) - @eel.expose def subtract(num1, num2): return int(num1) - int(num2) +@eel.expose +def showUsers(): + with engine.connect() as conn: + result = conn.execute(text("SELECT * FROM users;")) + users = result.fetchall() + return [dict(row._mapping) for row in users] + +@eel.expose +def addUsers(email, password): + #hashing logic here + + with engine.begin() as conn: # auto-commit + conn.execute( + text("INSERT INTO users (email, password_hash) VALUES (:email, :password)"), + {"email": email, "password": password} + ) + return "User added successfully" + eel.start('index.html', size=(1000, 600)) From c7beafbe103ffc9135c4d150d26a94ada9f71869 Mon Sep 17 00:00:00 2001 From: kaye-s Date: Wed, 18 Feb 2026 13:10:59 -0600 Subject: [PATCH 11/18] password hashing --- main.py | 4 +++- requirements.txt | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index e94e512..b94ee97 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ import eel from db import engine from sqlalchemy import text +import bcrypt #sofia #jacob #tim @@ -32,11 +33,12 @@ def showUsers(): @eel.expose def addUsers(email, password): #hashing logic here + hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() with engine.begin() as conn: # auto-commit conn.execute( text("INSERT INTO users (email, password_hash) VALUES (:email, :password)"), - {"email": email, "password": password} + {"email": email, "password": hashed} ) return "User added successfully" diff --git a/requirements.txt b/requirements.txt index c0be71b..c5e047d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ autopep8 psycopg2-binary SQLAlchemy python-dotenv +bcrypt From a6c2e34d99e70891fb1da8401eb1c25de60e60fe Mon Sep 17 00:00:00 2001 From: kaye-s Date: Wed, 18 Feb 2026 17:17:14 -0600 Subject: [PATCH 12/18] Moved db queries to new html file --- front-end/db_queries.html | 32 ++++++++++++++++++++++++++++++++ front-end/index.html | 17 +++++------------ 2 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 front-end/db_queries.html diff --git a/front-end/db_queries.html b/front-end/db_queries.html new file mode 100644 index 0000000..f840170 --- /dev/null +++ b/front-end/db_queries.html @@ -0,0 +1,32 @@ + + + + + + + + + Hello World! + + + + + + + + + + + + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/front-end/index.html b/front-end/index.html index bd942c2..112bd20 100644 --- a/front-end/index.html +++ b/front-end/index.html @@ -15,18 +15,11 @@ - - - - -
-
- - - - - - + + + + + \ No newline at end of file From 8ee5c8783a76880528b755f1490b9de30b5a09fc Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 18 Feb 2026 17:27:34 -0600 Subject: [PATCH 13/18] Initial backend commit: barebones backend inside app folder GroupFive, with model for AnalysisTask including request structure, user, language etc. Some simple tests on creation, APIView endpoint for analysis task and status endpoint. Uses serializer to interpret data from frontend --- GroupFive/__init__.py | 0 GroupFive/admin.py | 3 + GroupFive/apps.py | 6 ++ GroupFive/dummy_analysis.py | 13 +++ GroupFive/migrations/0001_initial.py | 30 +++++++ GroupFive/migrations/__init__.py | 0 GroupFive/models.py | 21 +++++ GroupFive/serializers.py | 7 ++ GroupFive/tasks.py | 22 +++++ GroupFive/tests.py | 60 +++++++++++++ GroupFive/views.py | 42 +++++++++ config/__init__.py | 0 config/asgi.py | 16 ++++ config/settings.py | 125 +++++++++++++++++++++++++++ config/urls.py | 26 ++++++ config/wsgi.py | 16 ++++ manage.py | 22 +++++ 17 files changed, 409 insertions(+) create mode 100644 GroupFive/__init__.py create mode 100644 GroupFive/admin.py create mode 100644 GroupFive/apps.py create mode 100644 GroupFive/dummy_analysis.py create mode 100644 GroupFive/migrations/0001_initial.py create mode 100644 GroupFive/migrations/__init__.py create mode 100644 GroupFive/models.py create mode 100644 GroupFive/serializers.py create mode 100644 GroupFive/tasks.py create mode 100644 GroupFive/tests.py create mode 100644 GroupFive/views.py create mode 100644 config/__init__.py create mode 100644 config/asgi.py create mode 100644 config/settings.py create mode 100644 config/urls.py create mode 100644 config/wsgi.py create mode 100644 manage.py diff --git a/GroupFive/__init__.py b/GroupFive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/GroupFive/admin.py b/GroupFive/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/GroupFive/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/GroupFive/apps.py b/GroupFive/apps.py new file mode 100644 index 0000000..8220433 --- /dev/null +++ b/GroupFive/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class OurApplicationConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'GroupFive' diff --git a/GroupFive/dummy_analysis.py b/GroupFive/dummy_analysis.py new file mode 100644 index 0000000..992ba89 --- /dev/null +++ b/GroupFive/dummy_analysis.py @@ -0,0 +1,13 @@ + +def run_dummy(code, language): + + return { + "summary" : "this dummy code is better than yours", + "findings" : [ + { + "severity" : "Minimal", + "description" : "Bad code", + "fix" : "Figure it Out" + } + ] + } \ No newline at end of file diff --git a/GroupFive/migrations/0001_initial.py b/GroupFive/migrations/0001_initial.py new file mode 100644 index 0000000..b8f9b01 --- /dev/null +++ b/GroupFive/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 5.0.3 on 2026-02-18 09:51 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='AnalysisTask', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('input_code', models.TextField()), + ('language', models.CharField(max_length=50)), + ('status', models.CharField(choices=[('QUEUED', 'Queued'), ('RUNNING', 'Running'), ('COMPLETED', 'Completed'), ('FAILED', 'Failed')], max_length=20)), + ('results', models.JSONField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/GroupFive/migrations/__init__.py b/GroupFive/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/GroupFive/models.py b/GroupFive/models.py new file mode 100644 index 0000000..f6f0ea7 --- /dev/null +++ b/GroupFive/models.py @@ -0,0 +1,21 @@ +#all id related lines are noted and can be deleted or changed if user id is skipped or substituted +import uuid #for user ID +from django.db import models +from django.contrib.auth.models import User + +class AnalysisTask(models.Model): + #potential review request statuses + STATUS_OPT = [ + ("QUEUED", "Queued"), + ("RUNNING", "Running"), + ("COMPLETED", "Completed"), + ("FAILED", "Failed") + ] + + id = models.UUIDField(primary_key=True, default=uuid.uuid4) #more user id + user = models.ForeignKey(User, on_delete=models.CASCADE) #user id/user + input_code = models.TextField() #user provided code + language = models.CharField(max_length=50) #language of user provided code + status = models.CharField(max_length=20, choices=STATUS_OPT) #status of review request + results = models.JSONField(null=True, blank=True) #results of review + created_at = models.DateTimeField(auto_now_add=True) #creation timestamp diff --git a/GroupFive/serializers.py b/GroupFive/serializers.py new file mode 100644 index 0000000..255c15e --- /dev/null +++ b/GroupFive/serializers.py @@ -0,0 +1,7 @@ +#this file uses serializers to define what information we add to our AnalysisTask model from user +from rest_framework import serializers + +class AnalysisRequestSerializer(serializers.Serializer): + code = serializers.CharField() #for input code + #language definition of input code, can be commented out if language distinction added later + language = serializers.CharField() diff --git a/GroupFive/tasks.py b/GroupFive/tasks.py new file mode 100644 index 0000000..24c186e --- /dev/null +++ b/GroupFive/tasks.py @@ -0,0 +1,22 @@ +#from celery import shared_task #task queue to handle simultaneous requests, making testing annoying for now can readd later when necessary +from GroupFive.models import AnalysisTask +from .dummy_analysis import run_dummy + +#@shared_task --from celery, readd later +def run_analysis_async(task_id): + + #instance of analysisTask + task = AnalysisTask.objects.get(id=task_id) + task.status = "RUNNING" #update status + task.save() #save instance task + + try: + #call ai api rather than dummy + results = run_dummy(task.input_code, task.language) + + task.results = results #store results + task.status = "COMPLETED" #update status + except Exception(BaseException) as e: + task.status = "FAILED" + + task.save() \ No newline at end of file diff --git a/GroupFive/tests.py b/GroupFive/tests.py new file mode 100644 index 0000000..5f5da37 --- /dev/null +++ b/GroupFive/tests.py @@ -0,0 +1,60 @@ +from rest_framework.test import APITestCase +from django.contrib.auth.models import User +from rest_framework import status +from .models import * +from uuid import uuid4 + + +class InitialAnalysisTests(APITestCase): + + def setUp(self): + #create user + self.User = User.objects.create_user( + username="username", + password="password" + ) + self.client.login(username="username", password="password") + + def test_create_analysisTask(self): + + response = self.client.post("/api/GroupFive/",{ + "code" : "print('Hello World')", #code to analyze + "language" : "Python" #language of code + }, format="json") + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn("task_id", response.data) #task_id in data + self.assertEqual(response.data["status"], "QUEUED") + +class InitialWorkflowTest(APITestCase): + + def setUp(self): + #create user + self.User = User.objects.create_user( + username="username", + password="password" + ) + self.client.login(username="username", password="password") + + def test_initial_workflow(self): + response = self.client.post("/api/GroupFive/",{ + "code" : "print('Hello Again')", #code to analyze + "language" : "Python" #language of code + }, format="json") + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + task_id = response.data["task_id"] + + task = AnalysisTask.objects.get(id=task_id) + + #confirm that dummy ran + self.assertEqual(task.status, "COMPLETED") + + result_response = self.client.get(f"/api/GroupFive/{task_id}") + + #ensure task endpoint + self.assertEqual(result_response.status_code, 200) + + + diff --git a/GroupFive/views.py b/GroupFive/views.py new file mode 100644 index 0000000..01cfa8e --- /dev/null +++ b/GroupFive/views.py @@ -0,0 +1,42 @@ +#all id related lines are noted and can be deleted or changed if user id is skipped or substituted +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated #for user id +from GroupFive.models import AnalysisTask +from GroupFive.serializers import AnalysisRequestSerializer +from .tasks import run_analysis_async + + +#analysis task endpoint +class AnalysisView(APIView): + permission_classes = [IsAuthenticated] + + def post(self, request): + serializer = AnalysisRequestSerializer(data=request.data) + serializer.is_valid(raise_exception=True) #deserialize, check correct input and format, raises 400 Bad Request on fail + + task = AnalysisTask.objects.create( + user=request.user, #user + input_code=serializer.validated_data["code"], + language=serializer.validated_data["language"], + status="QUEUED" + ) + + run_analysis_async(str(task.id)) + + return Response({ + "task_id": str(task.id), + "status": task.status + }) + +#status endpoint +class StatusView(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, task_id): + task = AnalysisTask.objects.get(id=task_id, user=request.user) #user + + return Response({ + "status": task.status, + "summary": task.results if task.status == "COMPLETED" else None + }) \ No newline at end of file diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/asgi.py b/config/asgi.py new file mode 100644 index 0000000..39149a0 --- /dev/null +++ b/config/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for config project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +application = get_asgi_application() diff --git a/config/settings.py b/config/settings.py new file mode 100644 index 0000000..4a3bd85 --- /dev/null +++ b/config/settings.py @@ -0,0 +1,125 @@ +""" +Django settings for config project. + +Generated by 'django-admin startproject' using Django 5.0.3. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-y+j3zht6sr%!!2fg0&-ek^21&)yc+y+5a*-ly+@16$8$px)a$@' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'GroupFive', + 'rest_framework' +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'config.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'config.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/config/urls.py b/config/urls.py new file mode 100644 index 0000000..a770eb4 --- /dev/null +++ b/config/urls.py @@ -0,0 +1,26 @@ +""" +URL configuration for config project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +from GroupFive.views import AnalysisView + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/GroupFive/', AnalysisView.as_view(), name='GroupFive'), + path('api/GroupFive/', ) +] diff --git a/config/wsgi.py b/config/wsgi.py new file mode 100644 index 0000000..c0a9631 --- /dev/null +++ b/config/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for config project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +application = get_wsgi_application() diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..8e7ac79 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() From d74cf23214a4bb693bd096fff49f489f0374ae20 Mon Sep 17 00:00:00 2001 From: NathanEdwards2023 Date: Wed, 18 Feb 2026 18:29:36 -0600 Subject: [PATCH 14/18] Frontend UI --- front-end/index.html | 113 ++++++++++++++--- front-end/scripts/upload.js | 14 +++ front-end/styles/style.css | 245 ++++++++++++++++++++++++++++++++++++ 3 files changed, 357 insertions(+), 15 deletions(-) create mode 100644 front-end/scripts/upload.js diff --git a/front-end/index.html b/front-end/index.html index 3a3ade3..f5ee704 100644 --- a/front-end/index.html +++ b/front-end/index.html @@ -1,25 +1,108 @@ - - - - - Hello World! - - - + AutoPen Dashboard + + + - - - - - - - + + +
+
+

Penetration Testing Dashboard

+
System Status: Active
+
+ +
+
+

Total Scans

+

128

+
+
+

Critical Vulnerabilities

+

12

+
+
+

Medium Vulnerabilities

+

34

+
+
+

Low Vulnerabilities

+

56

+
+
+ + +
+
+

Upload Code for Analysis

+
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + + +
+

Recent Scan Results

+ + + + + + + + + + + + + + + + + + + + + + + +
TargetDateRisk LevelStatus
example.com02/14/2026CriticalCompleted
test-server.net02/12/2026LowCompleted
+
+
+ + \ No newline at end of file diff --git a/front-end/scripts/upload.js b/front-end/scripts/upload.js new file mode 100644 index 0000000..4d33a0b --- /dev/null +++ b/front-end/scripts/upload.js @@ -0,0 +1,14 @@ +const tabButtons = document.querySelectorAll(".tab-btn"); +const tabContents = document.querySelectorAll(".tab-content"); + +tabButtons.forEach(button => { + button.addEventListener("click", () => { + // Remove active state + tabButtons.forEach(btn => btn.classList.remove("active")); + tabContents.forEach(tab => tab.classList.remove("active")); + + // Activate selected tab + button.classList.add("active"); + document.getElementById(button.dataset.tab).classList.add("active"); + }); +}); \ No newline at end of file diff --git a/front-end/styles/style.css b/front-end/styles/style.css index e69de29..8357d12 100644 --- a/front-end/styles/style.css +++ b/front-end/styles/style.css @@ -0,0 +1,245 @@ +/* ================= RESET ================= */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +body { + background: #000000; + color: #e5e7eb; + display: flex; + min-height: 100vh; +} + +/* ================= SIDEBAR ================= */ +.sidebar { + width: 250px; + height: 100vh; + background: rgba(17, 24, 39, 0.8); + padding: 20px; + border-right: 1px solid rgba(168, 85, 247, 0.2); +} + +.sidebar h2 { + color: #e9d5ff; + margin-bottom: 40px; + text-align: center; +} + +.sidebar ul { + list-style: none; +} + +.sidebar ul li { + padding: 15px; + margin: 10px 0; + background: rgba(17, 24, 39, 0.8); + border: 1px solid rgba(168, 85, 247, 0.2); + border-radius: 8px; + cursor: pointer; + transition: 0.25s; +} + +.sidebar ul li:hover { + background: rgba(17, 24, 39, 0.95); +} + +/* ================= MAIN ================= */ +.main { + flex: 1; + padding: 40px; +} + +/* ================= HEADER ================= */ +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 40px; +} + +.header h1 { + color: #ffffff; +} + +.status { + background: rgba(17, 24, 39, 0.8); + padding: 10px 20px; + border-radius: 999px; + border: 1px solid rgba(168, 85, 247, 0.2); +} + +/* ================= UPLOAD SECTION ================= */ +.code-center { + margin-bottom: 50px; +} + +.code-box { + background: rgba(17, 24, 39, 0.8); + border: 1px solid rgba(168, 85, 247, 0.2); + border-radius: 12px; + padding: 30px; + width: 100%; + max-width: 900px; +} + +.code-box h2 { + color: #ffffff; + margin-bottom: 20px; +} + +/* ================= TABS ================= */ +.upload-tabs { + display: flex; + gap: 12px; + margin-bottom: 20px; +} + +.tab-btn { + background: transparent; + border: 1px solid rgba(168, 85, 247, 0.2); + color: #e9d5ff; + padding: 8px 22px; + border-radius: 999px; + cursor: pointer; + transition: 0.25s; +} + +.tab-btn:hover { + background: rgba(168, 85, 247, 0.15); +} + +.tab-btn.active { + background: rgba(168, 85, 247, 0.25); +} + +/* ================= TAB CONTENT ================= */ +.tab-content { + display: none; +} + +.tab-content.active { + display: block; +} + +/* ================= DROP ZONE ================= */ +.drop-zone { + display: block; + padding: 50px; + border: 2px dashed rgba(168, 85, 247, 0.8); + border-radius: 12px; + background: rgba(17, 24, 39, 0.6); + text-align: center; + cursor: pointer; + transition: 0.25s; + width: 80%; + max-width: 600px; + margin: 0 auto; +} + +.drop-zone:hover { + background: rgba(17, 24, 39, 0.9); +} + +.drop-zone p { + font-size: 18px; + margin-bottom: 6px; +} + +.drop-zone span { + font-size: 14px; + color: #c4b5fd; +} + +/* ================= TEXTAREA ================= */ +textarea { + width: 100%; + min-height: 220px; + background: rgba(17, 24, 39, 0.6); + color: #ffffff; + border: 1px solid rgba(168, 85, 247, 0.2); + border-radius: 8px; + padding: 15px; + resize: vertical; +} + +/* ================= BUTTON ================= */ +.scan-btn { + margin-top: 20px; + padding: 10px 30px; + background: rgba(168, 85, 247, 0.85); + color: white; + border: none; + border-radius: 999px; + cursor: pointer; + transition: 0.25s; +} + +.scan-btn:hover { + background: rgba(168, 85, 247, 1); +} + +/* ================= CARDS ================= */ +.cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin-bottom: 40px; +} + +.card { + background: rgba(17, 24, 39, 0.8); + border: 1px solid rgba(168, 85, 247, 0.2); + padding: 20px; + border-radius: 12px; + transition: 0.25s; +} + +.card:hover { + background: rgba(17, 24, 39, 0.95); +} + +.card h3 { + color: #ffffff; + margin-bottom: 10px; +} + +.card p { + font-size: 28px; + font-weight: bold; +} + +/* ================= TABLE ================= */ +.recent-scans { + background: rgba(17, 24, 39, 0.8); + border: 1px solid rgba(168, 85, 247, 0.2); + padding: 20px; + border-radius: 12px; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 15px; +} + +th, td { + padding: 12px; + text-align: left; +} + +th { + color: #e9d5ff; + border-bottom: 1px solid rgba(168, 85, 247, 0.2); +} + +tr:hover { + background: rgba(168, 85, 247, 0.05); +} + +/* ================= SEVERITY COLORS ================= */ +.critical { color: #fb7185; font-weight: bold; } +.medium { color: #facc15; font-weight: bold; } +.low { color: #34d399; font-weight: bold; } From 5c156bb9f45fb206a5957472bdf98b7b695373f1 Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 22 Feb 2026 16:04:18 -0600 Subject: [PATCH 15/18] Renamed API folder. Fixed urls.py and api/urls.py to pass initial tests. Added database integration lacking actual database and user --- .gitignore | 2 ++ {GroupFive => api}/__init__.py | 0 {GroupFive => api}/admin.py | 0 {GroupFive => api}/apps.py | 2 +- {GroupFive => api}/dummy_analysis.py | 0 {GroupFive => api}/migrations/0001_initial.py | 0 {GroupFive => api}/migrations/__init__.py | 0 {GroupFive => api}/models.py | 0 {GroupFive => api}/serializers.py | 0 {GroupFive => api}/tasks.py | 2 +- {GroupFive => api}/tests.py | 6 +++--- {GroupFive => api}/views.py | 13 ++++++------- config/settings.py | 13 +++++++++---- config/urls.py | 9 +++++---- 14 files changed, 27 insertions(+), 20 deletions(-) rename {GroupFive => api}/__init__.py (100%) rename {GroupFive => api}/admin.py (100%) rename {GroupFive => api}/apps.py (85%) rename {GroupFive => api}/dummy_analysis.py (100%) rename {GroupFive => api}/migrations/0001_initial.py (100%) rename {GroupFive => api}/migrations/__init__.py (100%) rename {GroupFive => api}/models.py (100%) rename {GroupFive => api}/serializers.py (100%) rename {GroupFive => api}/tasks.py (94%) rename {GroupFive => api}/tests.py (89%) rename {GroupFive => api}/views.py (89%) diff --git a/.gitignore b/.gitignore index 06125d6..4a02c0a 100644 --- a/.gitignore +++ b/.gitignore @@ -85,6 +85,8 @@ profile_default/ ipython_config.py # pyenv +#don't add database credentials +.env .python-version # pipenv diff --git a/GroupFive/__init__.py b/api/__init__.py similarity index 100% rename from GroupFive/__init__.py rename to api/__init__.py diff --git a/GroupFive/admin.py b/api/admin.py similarity index 100% rename from GroupFive/admin.py rename to api/admin.py diff --git a/GroupFive/apps.py b/api/apps.py similarity index 85% rename from GroupFive/apps.py rename to api/apps.py index 8220433..5b06c17 100644 --- a/GroupFive/apps.py +++ b/api/apps.py @@ -3,4 +3,4 @@ class OurApplicationConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' - name = 'GroupFive' + name = 'api' diff --git a/GroupFive/dummy_analysis.py b/api/dummy_analysis.py similarity index 100% rename from GroupFive/dummy_analysis.py rename to api/dummy_analysis.py diff --git a/GroupFive/migrations/0001_initial.py b/api/migrations/0001_initial.py similarity index 100% rename from GroupFive/migrations/0001_initial.py rename to api/migrations/0001_initial.py diff --git a/GroupFive/migrations/__init__.py b/api/migrations/__init__.py similarity index 100% rename from GroupFive/migrations/__init__.py rename to api/migrations/__init__.py diff --git a/GroupFive/models.py b/api/models.py similarity index 100% rename from GroupFive/models.py rename to api/models.py diff --git a/GroupFive/serializers.py b/api/serializers.py similarity index 100% rename from GroupFive/serializers.py rename to api/serializers.py diff --git a/GroupFive/tasks.py b/api/tasks.py similarity index 94% rename from GroupFive/tasks.py rename to api/tasks.py index 24c186e..ddb7a07 100644 --- a/GroupFive/tasks.py +++ b/api/tasks.py @@ -1,5 +1,5 @@ #from celery import shared_task #task queue to handle simultaneous requests, making testing annoying for now can readd later when necessary -from GroupFive.models import AnalysisTask +from api.models import AnalysisTask from .dummy_analysis import run_dummy #@shared_task --from celery, readd later diff --git a/GroupFive/tests.py b/api/tests.py similarity index 89% rename from GroupFive/tests.py rename to api/tests.py index 5f5da37..06c9e67 100644 --- a/GroupFive/tests.py +++ b/api/tests.py @@ -17,7 +17,7 @@ def setUp(self): def test_create_analysisTask(self): - response = self.client.post("/api/GroupFive/",{ + response = self.client.post("/api/analysis/",{ "code" : "print('Hello World')", #code to analyze "language" : "Python" #language of code }, format="json") @@ -37,7 +37,7 @@ def setUp(self): self.client.login(username="username", password="password") def test_initial_workflow(self): - response = self.client.post("/api/GroupFive/",{ + response = self.client.post("/api/analysis/",{ "code" : "print('Hello Again')", #code to analyze "language" : "Python" #language of code }, format="json") @@ -51,7 +51,7 @@ def test_initial_workflow(self): #confirm that dummy ran self.assertEqual(task.status, "COMPLETED") - result_response = self.client.get(f"/api/GroupFive/{task_id}") + result_response = self.client.get(f"/api/analysis/{task_id}/") #ensure task endpoint self.assertEqual(result_response.status_code, 200) diff --git a/GroupFive/views.py b/api/views.py similarity index 89% rename from GroupFive/views.py rename to api/views.py index 01cfa8e..c5d1861 100644 --- a/GroupFive/views.py +++ b/api/views.py @@ -2,15 +2,14 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated #for user id -from GroupFive.models import AnalysisTask -from GroupFive.serializers import AnalysisRequestSerializer +from api.models import AnalysisTask +from api.serializers import AnalysisRequestSerializer from .tasks import run_analysis_async - -#analysis task endpoint class AnalysisView(APIView): permission_classes = [IsAuthenticated] + #analysis task endpoint def post(self, request): serializer = AnalysisRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) #deserialize, check correct input and format, raises 400 Bad Request on fail @@ -29,14 +28,14 @@ def post(self, request): "status": task.status }) -#status endpoint class StatusView(APIView): permission_classes = [IsAuthenticated] - + #status endpoint def get(self, request, task_id): task = AnalysisTask.objects.get(id=task_id, user=request.user) #user return Response({ "status": task.status, "summary": task.results if task.status == "COMPLETED" else None - }) \ No newline at end of file + }) + diff --git a/config/settings.py b/config/settings.py index 4a3bd85..be34dd9 100644 --- a/config/settings.py +++ b/config/settings.py @@ -9,7 +9,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.0/ref/settings/ """ - +import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -37,7 +37,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'GroupFive', + 'api', 'rest_framework' ] @@ -75,10 +75,15 @@ # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases +#modified 2/22 to implement PostgreSQL DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.getenv('DB_NAME'), + 'USER': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_USER'), + 'HOST': os.getenv('DB_USER'), + 'PORT': os.getenv('DB_USER'), } } diff --git a/config/urls.py b/config/urls.py index a770eb4..e0ee957 100644 --- a/config/urls.py +++ b/config/urls.py @@ -15,12 +15,13 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import path, include -from GroupFive.views import AnalysisView +from api.views import * urlpatterns = [ path('admin/', admin.site.urls), - path('api/GroupFive/', AnalysisView.as_view(), name='GroupFive'), - path('api/GroupFive/', ) + path('api/', include('api.urls')), + + #path('/api/',) ] From ecc43b5d43787c5497482bcc59a0ab0872a70f16 Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 22 Feb 2026 16:47:33 -0600 Subject: [PATCH 16/18] api/urls.py --- api/urls.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 api/urls.py diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 0000000..5dd7cb6 --- /dev/null +++ b/api/urls.py @@ -0,0 +1,8 @@ +from django.contrib.auth import get_user +from django.urls import path +from .views import * + +urlpatterns = [ + path("analysis/", AnalysisView.as_view(), name="analysis"), + path("analysis//", StatusView.as_view(), name="analysis-status"), +] \ No newline at end of file From 80a832f53c36d64d91afb1f6bdb303c14428d60d Mon Sep 17 00:00:00 2001 From: JacobLind1 Date: Sun, 22 Feb 2026 17:23:03 -0600 Subject: [PATCH 17/18] Database Integrated with Backend, Added Requirements I had forgotten. Renamed testquery,py as it was conflicting with some python test naming. --- config/settings.py | 16 +++++++++++++--- testquery.py => db_test.py | 0 requirements.txt | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) rename testquery.py => db_test.py (100%) diff --git a/config/settings.py b/config/settings.py index be34dd9..6a40ff2 100644 --- a/config/settings.py +++ b/config/settings.py @@ -12,6 +12,11 @@ import os from pathlib import Path +#dotenv setup otherwise import and use db.py +from dotenv import load_dotenv +load_dotenv() + + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -75,15 +80,20 @@ # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases + + #modified 2/22 to implement PostgreSQL DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.getenv('DB_NAME'), 'USER': os.getenv('DB_USER'), - 'PASSWORD': os.getenv('DB_USER'), - 'HOST': os.getenv('DB_USER'), - 'PORT': os.getenv('DB_USER'), + 'PASSWORD': os.getenv('DB_PASSWORD'), + 'HOST': os.getenv('DB_HOST'), + 'PORT': os.getenv('DB_PORT'), + 'OPTIONS': { + 'sslmode': 'require', + } } } diff --git a/testquery.py b/db_test.py similarity index 100% rename from testquery.py rename to db_test.py diff --git a/requirements.txt b/requirements.txt index c5e047d..f69e2b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ psycopg2-binary SQLAlchemy python-dotenv bcrypt +djangorestframework From 2a92970f1d2e60fa808f34e4e8b4e3d0f35f3281 Mon Sep 17 00:00:00 2001 From: JacobLind1 Date: Wed, 4 Mar 2026 12:34:17 -0600 Subject: [PATCH 18/18] Fixed issue connecting with group database. --- config/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings.py b/config/settings.py index 6a40ff2..0dc2614 100644 --- a/config/settings.py +++ b/config/settings.py @@ -88,7 +88,7 @@ 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.getenv('DB_NAME'), 'USER': os.getenv('DB_USER'), - 'PASSWORD': os.getenv('DB_PASSWORD'), + 'PASSWORD': os.getenv('DB_PASS'), 'HOST': os.getenv('DB_HOST'), 'PORT': os.getenv('DB_PORT'), 'OPTIONS': {