From 9f9150c242f68f77b8e5fe2075da9d7f64390f9a Mon Sep 17 00:00:00 2001 From: yurikakan Date: Tue, 2 Dec 2025 21:38:08 -0500 Subject: [PATCH 01/27] Inserted mock data --- database-files/ngo_db.sql | 997 +++++++++++++++++++++++++++++++++++--- 1 file changed, 937 insertions(+), 60 deletions(-) diff --git a/database-files/ngo_db.sql b/database-files/ngo_db.sql index 2c9bebe085..7fc9ca7947 100644 --- a/database-files/ngo_db.sql +++ b/database-files/ngo_db.sql @@ -1,71 +1,948 @@ -DROP DATABASE IF EXISTS ngo_db; -CREATE DATABASE IF NOT EXISTS ngo_db; +DROP DATABASE IF EXISTS fithub; +CREATE DATABASE fithub; +USE fithub; -USE ngo_db; +DROP TABLE IF EXISTS Users; +-- USERS TABLE +CREATE TABLE Users ( +UserID INT AUTO_INCREMENT PRIMARY KEY, +Name VARCHAR(100) NOT NULL, +Email VARCHAR(255) NOT NULL UNIQUE, +Phone VARCHAR(20) NOT NULL, +Address VARCHAR(255) NOT NULL, +DOB DATE NOT NULL, +Gender VARCHAR(20) NOT NULL, +IsActive BOOLEAN NOT NULL, +Role VARCHAR(100) NOT NULL +); + +DROP TABLE IF EXISTS Announcements; +-- ANNOUNCEMENTS +CREATE TABLE Announcements ( +AnnouncementID INT AUTO_INCREMENT PRIMARY KEY, +AnnouncerID INT NOT NULL, +Message TEXT NOT NULL, +AnnouncedAt DATETIME NOT NULL, +FOREIGN KEY (AnnouncerID) REFERENCES Users(UserID) +); + +DROP TABLE IF EXISTS AnnouncementsReceived; +-- ANNOUNCEMENTSRECEIVED +CREATE TABLE AnnouncementsReceived ( +AnnouncementID INT NOT NULL, +UserID INT NOT NULL, +PRIMARY KEY (AnnouncementID, UserID), +FOREIGN KEY (AnnouncementID) REFERENCES Announcements(AnnouncementID), +FOREIGN KEY (UserID) REFERENCES Users(UserID) +); + +DROP TABLE IF EXISTS Items; +-- ITEMS +CREATE TABLE Items ( +ItemID INT AUTO_INCREMENT PRIMARY KEY, +Title VARCHAR(255) NOT NULL, +Category VARCHAR(100) NOT NULL, +Description TEXT NOT NULL, +Size VARCHAR(10) NOT NULL, +`Condition` VARCHAR(100) NOT NULL, +IsAvailable BOOLEAN NOT NULL, +OwnerID INT NOT NULL, +ListedAt DATETIME NOT NULL, +`Type` VARCHAR(10) NOT NULL, +FOREIGN KEY (OwnerID) REFERENCES Users(UserID) +); +DROP TABLE IF EXISTS Reports; +-- REPORTS +CREATE TABLE Reports ( +ReportID INT AUTO_INCREMENT PRIMARY KEY NOT NULL, +Note TEXT NOT NULL, +Severity INT NOT NULL, +Resolved BOOLEAN NOT NULL, +ReporterID INT NOT NULL, +ReportedUser INT NULL, +ReportedItem INT NULL, +ResolverID INT NULL, +ResolvedAt DATETIME NULL, +FOREIGN KEY (ReporterID) REFERENCES Users(UserID), +FOREIGN KEY (ReportedUser) REFERENCES Users(UserID), +FOREIGN KEY (ReportedItem) REFERENCES Items(ItemID), +FOREIGN KEY (ResolverID) REFERENCES Users(UserID) +); + +DROP TABLE IF EXISTS Images; +-- IMAGES +CREATE TABLE Images ( +ImageID INT AUTO_INCREMENT PRIMARY KEY, +ItemID INT NOT NULL, +ImageURL TEXT NOT NULL, +ImageOrderNum INT NOT NULL, +FOREIGN KEY (ItemID) REFERENCES Items(ItemID) +); + +DROP TABLE IF EXISTS Tags; +-- TAGS +CREATE TABLE Tags ( +TagID INT AUTO_INCREMENT PRIMARY KEY, +Title VARCHAR(100) NOT NULL +); + +DROP TABLE IF EXISTS ItemTags; +-- ITEMTAGS +CREATE TABLE ItemTags ( +ItemID INT NOT NULL, +TagID INT NOT NULL, +PRIMARY KEY (ItemID, TagID), +FOREIGN KEY (ItemID) REFERENCES Items(ItemID), +FOREIGN KEY (TagID) REFERENCES Tags(TagID) +); -CREATE TABLE IF NOT EXISTS WorldNGOs ( - NGO_ID INT AUTO_INCREMENT PRIMARY KEY, - Name VARCHAR(255) NOT NULL, - Country VARCHAR(100) NOT NULL, - Founding_Year INTEGER, - Focus_Area VARCHAR(100), - Website VARCHAR(255) +DROP TABLE IF EXISTS Shippings; +-- SHIPPING +CREATE TABLE Shippings ( +ShippingID INT AUTO_INCREMENT PRIMARY KEY, +Carrier VARCHAR(100) NOT NULL, +TrackingNum VARCHAR(255) NOT NULL, +DateShipped DATE NOT NULL, +DateArrived DATE NULL ); -CREATE TABLE IF NOT EXISTS Projects ( - Project_ID INT AUTO_INCREMENT PRIMARY KEY, - Project_Name VARCHAR(255) NOT NULL, - Focus_Area VARCHAR(100), - Budget DECIMAL(15, 2), - NGO_ID INT, - Start_Date DATE, - End_Date DATE, - FOREIGN KEY (NGO_ID) REFERENCES WorldNGOs(NGO_ID) +DROP TABLE IF EXISTS Orders; +-- ORDERS +CREATE TABLE Orders ( +OrderID INT AUTO_INCREMENT PRIMARY KEY, +GivenByID INT NOT NULL, +ReceiverID INT NOT NULL, +CreatedAt DATETIME NOT NULL, +ShippingID INT NULL, +FOREIGN KEY (GivenByID) REFERENCES Users(UserID), +FOREIGN KEY (ReceiverID) REFERENCES Users(UserID), +FOREIGN KEY (ShippingID) REFERENCES Shippings(ShippingID) ); -CREATE TABLE IF NOT EXISTS Donors ( - Donor_ID INT AUTO_INCREMENT PRIMARY KEY, - Donor_Name VARCHAR(255) NOT NULL, - Donor_Type ENUM('Individual', 'Organization') NOT NULL, - Donation_Amount DECIMAL(15, 2), - NGO_ID INT, - FOREIGN KEY (NGO_ID) REFERENCES WorldNGOs(NGO_ID) +DROP TABLE IF EXISTS OrderItems; +-- ORDERITEMS +CREATE TABLE OrderItems ( +OrderID INT NOT NULL, +ItemID INT NOT NULL, +PRIMARY KEY (OrderID, ItemID), +FOREIGN KEY (ItemID) REFERENCES Items(ItemID), +FOREIGN KEY (OrderID) REFERENCES Orders(OrderID) ); -INSERT INTO WorldNGOs (Name, Country, Founding_Year, Focus_Area, Website) -VALUES -('World Wildlife Fund', 'United States', 1961, 'Environmental Conservation', 'https://www.worldwildlife.org'), -('Doctors Without Borders', 'France', 1971, 'Medical Relief', 'https://www.msf.org'), -('Oxfam International', 'United Kingdom', 1995, 'Poverty and Inequality', 'https://www.oxfam.org'), -('Amnesty International', 'United Kingdom', 1961, 'Human Rights', 'https://www.amnesty.org'), -('Save the Children', 'United States', 1919, 'Child Welfare', 'https://www.savethechildren.org'), -('Greenpeace', 'Netherlands', 1971, 'Environmental Protection', 'https://www.greenpeace.org'), -('International Red Cross', 'Switzerland', 1863, 'Humanitarian Aid', 'https://www.icrc.org'), -('CARE International', 'Switzerland', 1945, 'Global Poverty', 'https://www.care-international.org'), -('Habitat for Humanity', 'United States', 1976, 'Affordable Housing', 'https://www.habitat.org'), -('Plan International', 'United Kingdom', 1937, 'Child Rights', 'https://plan-international.org'); - -INSERT INTO Projects (Project_Name, Focus_Area, Budget, NGO_ID, Start_Date, End_Date) -VALUES -('Save the Amazon', 'Environmental Conservation', 5000000.00, 1, '2022-01-01', '2024-12-31'), -('Emergency Medical Aid in Syria', 'Medical Relief', 3000000.00, 2, '2023-03-01', '2023-12-31'), -('Education for All', 'Poverty and Inequality', 2000000.00, 3, '2021-06-01', '2025-05-31'), -('Human Rights Advocacy in Asia', 'Human Rights', 1500000.00, 4, '2022-09-01', '2023-08-31'), -('Child Nutrition Program', 'Child Welfare', 2500000.00, 5, '2022-01-01', '2024-01-01'); - -INSERT INTO Donors (Donor_Name, Donor_Type, Donation_Amount, NGO_ID) -VALUES -('Bill & Melinda Gates Foundation', 'Organization', 10000000.00, 1), -('Elon Musk', 'Individual', 5000000.00, 2), -('Google.org', 'Organization', 2000000.00, 3), -('Open Society Foundations', 'Organization', 3000000.00, 4), -('Anonymous Philanthropist', 'Individual', 1000000.00, 5); - -CREATE TABLE model1_params ( - sequence_number INT, - beta_vals TEXT +DROP TABLE IF EXISTS Feedback; +-- FEEDBACK +CREATE TABLE Feedback ( +FeedbackID INT AUTO_INCREMENT PRIMARY KEY, +OrderID INT NOT NULL, +Rating INT NOT NULL, +Comment TEXT NOT NULL, +CreatedAt DATETIME NOT NULL, +CreatedByID INT NOT NULL, +FOREIGN KEY (OrderID) REFERENCES Orders(OrderID), +FOREIGN KEY (CreatedByID) REFERENCES Users(UserID), +CHECK (Rating BETWEEN 1 AND 5) ); -INSERT INTO model1_params (sequence_number, beta_vals) VALUES -(1, '[0.25, 0.45, 0.67]'); \ No newline at end of file +-- RESET ALL DATA +SET FOREIGN_KEY_CHECKS = 0; + +TRUNCATE TABLE AnnouncementsReceived; +TRUNCATE TABLE ItemTags; +TRUNCATE TABLE OrderItems; +TRUNCATE TABLE Feedback; +TRUNCATE TABLE Reports; +TRUNCATE TABLE Images; +TRUNCATE TABLE Orders; +TRUNCATE TABLE Shippings; +TRUNCATE TABLE Items; +TRUNCATE TABLE Announcements; +TRUNCATE TABLE Tags; +TRUNCATE TABLE Users; + +SET FOREIGN_KEY_CHECKS = 1; + +-- INSERT USERS +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Aisha Seth', 'aseth@fithub.org', '783-714-6861', '696 Rowland Alley', '1994-03-16', 'Female', true, 'admin'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Blair Williams', 'bwilliams@fithub.org', '607-164-4550', '3 Namekagon Terrace', '1997-11-04', 'Female', true, 'analyst'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Waylen Phipard-Shears', 'wpshears2@fithub.org', '585-571-4741', '46 Doe Crossing Trail', '2000-02-05', 'Male', true, 'admin'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Meggie Aleksankin', 'maleksankin3@stumbleupon.com', '170-363-5841', '9711 Carberry Trail', '2006-09-30', 'Female', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Kingsly Swires', 'kswires4@desdev.cn', '869-764-4936', '5 Dovetail Court', '2000-01-05', 'Male', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Gilberto Jordison', 'gjordison5@google.com.br', '215-415-1029', '08 Annamark Junction', '2007-11-03', 'Male', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Atlante Kindall', 'akindall6@about.me', '709-939-0916', '762 Anderson Park', '2000-12-04', 'Bigender', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Alexi Moreinu', 'amoreinu7@dell.com', '979-373-6083', '02727 Luster Park', '2003-02-06', 'Female', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Milly Gue', 'mgue8@businessweek.com', '879-895-9007', '52 Alpine Terrace', '2006-02-10', 'Polygender', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Doralynn Hamlyn', 'dhamlyn9@fotki.com', '323-337-9508', '813 Debs Trail', '2002-01-25', 'Female', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Mahmoud Klazenga', 'mklazengaa@jiathis.com', '641-148-8235', '705 Prairie Rose Court', '2001-12-23', 'Male', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Agustin Stickells', 'astickellsb@cnbc.com', '740-855-4464', '13 Myrtle Terrace', '2006-05-19', 'Male', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Efren Everingham', 'eeveringhamc@reverbnation.com', '191-543-6910', '95491 Goodland Circle', '2001-09-05', 'Agender', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Penn Robilart', 'probilartd@odnoklassniki.ru', '167-291-8605', '1449 Fulton Place', '2007-12-23', 'Male', false, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Hillier Thompkins', 'hthompkinse@vinaora.com', '504-931-0747', '50 Fallview Trail', '2007-08-09', 'Male', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Maryanna Sedgwick', 'msedgwickf@usnews.com', '216-694-7935', '4 Bashford Road', '2006-04-16', 'Female', false, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Quintilla Reimer', 'qreimerg@plala.or.jp', '253-962-6818', '107 Lyons Court', '2007-07-24', 'Female', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Lurette Matyushenko', 'lmatyushenkoh@woothemes.com', '615-436-3277', '26 Donald Street', '2002-02-22', 'Female', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Rhona Smissen', 'rsmisseni@statcounter.com', '714-336-8319', '0 Steensland Trail', '2008-05-02', 'Female', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Lurleen Loody', 'lloodyj@jimdo.com', '624-862-3836', '9524 Ohio Alley', '2001-09-15', 'Agender', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Parrnell O''Docherty', 'podochertyk@cnet.com', '403-914-1659', '84 Garrison Alley', '2007-02-06', 'Male', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Jarret Brimilcome', 'jbrimilcomel@reverbnation.com', '230-152-1964', '6 Prentice Park', '2000-10-10', 'Male', false, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Imogene Waddam', 'iwaddamm@soundcloud.com', '916-806-9329', '52632 Hauk Alley', '2005-08-04', 'Female', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Zebadiah Corthes', 'zcorthesn@gnu.org', '202-670-1925', '9 Shelley Court', '2003-08-07', 'Male', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Jacqui De Wolfe', 'jdeo@sourceforge.net', '915-247-4278', '49958 Brown Way', '2007-03-24', 'Female', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Rutter Bartolomeo', 'rbartolomeop@amazonaws.com', '529-202-7689', '47026 Merchant Drive', '2006-04-29', 'Male', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Lana Ferriday', 'lferridayq@dailymail.co.uk', '748-446-1428', '89 Sachs Street', '2008-10-12', 'Non-binary', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Caitlin Cadlock', 'ccadlockr@japanpost.jp', '202-514-4711', '5024 Riverside Way', '2008-08-08', 'Female', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Doria True', 'dtrues@hatena.ne.jp', '673-877-5502', '935 Old Shore Place', '2004-12-12', 'Female', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Willamina Guite', 'wguitet@apple.com', '879-495-7973', '5 Magdeline Road', '2007-12-14', 'Female', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Halli Conneely', 'hconneelyu@weebly.com', '412-281-1093', '7 Swallow Park', '2005-05-04', 'Female', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Rivkah Truscott', 'rtruscottv@wikispaces.com', '969-724-2894', '6534 Schmedeman Terrace', '2004-09-15', 'Female', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Germana Praill', 'gpraillw@webs.com', '418-980-7134', '8847 Prairieview Hill', '2003-09-10', 'Female', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Andrej Simants', 'asimantsx@tripod.com', '938-331-1813', '455 Sunfield Hill', '2004-05-03', 'Male', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Arabele Kinningley', 'akinningleyy@biglobe.ne.jp', '209-478-2254', '1184 Northridge Crossing', '2007-01-22', 'Female', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Bryant Fernao', 'bfernaoz@canalblog.com', '338-119-6662', '0 Melody Alley', '2001-12-14', 'Male', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Granthem Klossek', 'gklossek10@tumblr.com', '796-347-3135', '5 Warbler Trail', '2004-11-07', 'Male', true, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Delmar Rigbye', 'drigbye11@wisc.edu', '434-650-1264', '15275 Elgar Road', '2007-06-04', 'Male', true, 'taker'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Kimmie Bickersteth', 'kbickersteth12@bizjournals.com', '975-673-6496', '39909 Iowa Junction', '2007-12-02', 'Bigender', false, 'swapper'); +insert into Users (Name, Email, Phone, Address, DOB, Gender, IsActive, Role) values ('Inger Valeri', 'ivaleri13@baidu.com', '364-888-1308', '7012 Oak Terrace', '2006-04-25', 'Male', true, 'taker'); +SELECT * FROM Users; + +-- INSERT ANNOUNCEMENTS +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Join our upcoming virtual swap meet on Saturday!', '2025-09-28 13:21:51'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Weekend challenge: Swap at least one item and leave feedback for your partner.', '2025-08-01 16:36:36'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Pro tip: Use aesthetic tags like Y2K or Coquette or Streetwear.', '2025-03-11 20:58:34'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Stay tuned for exclusive discounts from our partner brands.', '2025-11-06 23:10:44'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Pro tip: Use aesthetic tags like Y2K or Coquette or Streetwear.', '2025-09-17 04:32:57'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Join our upcoming virtual swap meet on Saturday!', '2025-11-04 15:55:27'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Stay tuned for exclusive discounts from our partner brands.', '2025-10-25 15:57:13'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Check out our instagram for styling tips and trend forecasts.', '2025-07-17 18:26:16'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Pro tip: Use aesthetic tags like Y2K or Coquette or Streetwear.', '2025-05-22 00:51:36'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (3, 'Welcome to FitHub! List your pre-loved fits and swap with the community.', '2025-11-04 22:59:25'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Check out our instagram for styling tips and trend forecasts.', '2025-01-11 08:47:13'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Join our upcoming virtual swap meet on Saturday!', '2025-08-22 18:09:21'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Pro tip: Use aesthetic tags like Y2K or Coquette or Streetwear.', '2025-10-26 06:51:08'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Weekend challenge: Swap at least one item and leave feedback for your partner.', '2025-07-19 03:52:09'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Welcome to FitHub! List your pre-loved fits and swap with the community.', '2025-04-05 13:12:29'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Weekend challenge: Swap at least one item and leave feedback for your partner.', '2025-06-17 01:28:50'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Weekend challenge: Swap at least one item and leave feedback for your partner.', '2025-05-28 07:40:04'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (3, 'Welcome to FitHub! List your pre-loved fits and swap with the community.', '2025-08-08 08:40:40'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Check out our instagram for styling tips and trend forecasts.', '2025-09-28 00:20:16'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Congratulations on completing your first swap! Keep it up!', '2025-05-21 16:43:09'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Welcome to FitHub! List your pre-loved fits and swap with the community.', '2024-12-19 23:24:03'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (3, 'Weekend challenge: Swap at least one item and leave feedback for your partner.', '2025-06-19 04:43:36'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Maintenance scheduled for tomorrow at 2 PM.', '2025-08-27 12:21:49'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Don''t forget to update your profile with your latest fits!', '2025-01-15 02:08:41'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Pro tip: Use aesthetic tags like Y2K or Coquette or Streetwear.', '2025-03-21 12:25:44'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Join our upcoming virtual swap meet on Saturday!', '2025-08-26 14:55:50'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Maintenance scheduled for tomorrow at 2 PM.', '2025-11-13 01:45:41'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (3, 'Weekend challenge: Swap at least one item and leave feedback for your partner.', '2025-01-31 00:23:15'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Weekend challenge: Swap at least one item and leave feedback for your partner.', '2024-12-13 22:59:34'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Maintenance scheduled for tomorrow at 2 PM.', '2024-12-22 07:56:02'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Stay tuned for exclusive discounts from our partner brands.', '2025-08-30 14:14:36'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Check out our instagram for styling tips and trend forecasts.', '2025-02-03 15:17:06'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (3, 'Welcome to FitHub! List your pre-loved fits and swap with the community.', '2025-08-11 00:50:35'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Don''t forget to update your profile with your latest fits!', '2025-02-27 07:22:15'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Congratulations on completing your first swap! Keep it up!', '2025-11-02 16:02:45'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (3, 'Congratulations on completing your first swap! Keep it up!', '2025-11-03 00:38:50'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Join our upcoming virtual swap meet on Saturday!', '2025-06-16 17:46:37'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Pro tip: Use aesthetic tags like Y2K or Coquette or Streetwear.', '2025-06-29 15:13:14'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (1, 'Stay tuned for exclusive discounts from our partner brands.', '2025-07-25 01:21:11'); +insert into Announcements (AnnouncerID, Message, AnnouncedAt) values (3, 'Congratulations on completing your first swap! Keep it up!', '2025-06-17 07:06:11'); +SELECT * FROM Announcements; + +-- INSERT ANNOUNCEMENTS RECEIVED +insert into AnnouncementsReceived (AnnouncementID, UserID) values (36, 39); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (17, 3); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (19, 20); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (14, 4); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (35, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (8, 4); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (10, 13); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (40, 22); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (2, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (7, 22); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (25, 16); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (7, 6); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (34, 34); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (30, 26); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (26, 22); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (29, 11); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (22, 23); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (1, 40); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (6, 30); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (20, 21); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (14, 30); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (7, 33); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (38, 19); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (32, 17); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (23, 10); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (17, 18); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (3, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (21, 24); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (1, 10); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (22, 9); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (39, 37); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (16, 32); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (16, 14); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (33, 27); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (5, 25); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (29, 5); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (31, 21); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (16, 36); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (32, 36); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (5, 36); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (30, 31); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (10, 38); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (31, 40); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (31, 3); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (12, 7); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (16, 18); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (16, 7); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (4, 34); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (24, 37); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (35, 9); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (13, 36); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (38, 10); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (30, 30); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (2, 18); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (5, 19); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (32, 35); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (11, 9); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (4, 13); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (37, 11); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (7, 17); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (34, 5); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (24, 26); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (18, 6); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (13, 38); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (7, 37); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (33, 6); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (35, 22); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (24, 6); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (21, 28); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (25, 30); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (20, 26); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (25, 26); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (25, 22); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (13, 39); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (23, 12); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (40, 4); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (13, 11); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (13, 9); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (34, 19); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (34, 12); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (14, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (19, 10); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (18, 14); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (19, 26); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (40, 38); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (39, 33); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (30, 35); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (4, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (35, 7); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (26, 6); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (5, 9); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (26, 31); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (1, 8); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (32, 12); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (36, 8); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (21, 4); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (9, 11); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (16, 20); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (8, 19); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (35, 11); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (1, 18); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (27, 9); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (32, 19); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (11, 7); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (33, 35); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (12, 38); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (14, 14); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (19, 31); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (14, 23); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (6, 29); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (6, 26); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (33, 14); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (17, 5); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (23, 39); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (26, 5); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (9, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (22, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (21, 7); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (19, 38); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (8, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (1, 16); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (12, 33); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (3, 12); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (18, 7); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (23, 26); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (3, 30); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (30, 24); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (28, 33); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (2, 4); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (15, 23); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (21, 37); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (13, 37); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (22, 17); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (7, 16); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (1, 35); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (23, 4); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (12, 23); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (18, 26); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (35, 25); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (17, 22); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (28, 11); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (40, 7); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (24, 24); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (17, 24); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (25, 31); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (18, 5); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (18, 16); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (31, 37); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (30, 8); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (25, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (21, 22); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (6, 35); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (9, 31); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (22, 16); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (10, 39); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (35, 10); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (40, 15); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (18, 25); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (10, 34); +insert into AnnouncementsReceived (AnnouncementID, UserID) values (21, 34); +SELECT * FROM AnnouncementsReceived; + +-- INSERT ITEMS +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Converse Chuck Taylor', 'shoes', 'Red high tops, some wear on soles', 'M', 'Good', true, 8, '2025-02-13 16:45:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Vintage Band Tee', 't-shirt', 'Nirvana concert tee from the 90s', 'M', 'Good', true, 6, '2025-02-14 10:30:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Leather Moto Jacket', 'jacket', 'Black leather jacket, slightly worn', 'L', 'Very good', true, 4, '2025-02-14 13:00:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Floral Midi Dress', 'dress', 'Spring floral pattern, never worn', 'M', 'Excellent', true, 5, '2025-02-15 11:20:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('High-Waisted Mom Jeans', 'jeans', 'Light wash, straight leg fit', 'M', 'Good', true, 6, '2025-02-15 15:05:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Oversized Knit Sweater', 'sweater', 'Cream chunky knit, cozy for winter', 'L', 'Very good', true, 7, '2025-02-16 09:45:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Black Chelsea Boots', 'shoes', 'Leather ankle boots, small scuff on heel', 'M', 'Good', true, 8, '2025-02-16 14:10:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Puffer Winter Coat', 'coat', 'Long black puffer, very warm with hood', 'L', 'Excellent', true, 9, '2025-02-17 10:00:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Corduroy Mini Skirt', 'skirt', 'Brown corduroy mini skirt with pockets', 'S', 'Very good', true, 10, '2025-02-17 16:20:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Graphic Hoodie', 'hoodie', 'Gray hoodie with retro print on back', 'M', 'Good', true, 11, '2025-02-18 13:40:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Running Sneakers', 'shoes', 'White running shoes, lightly used', 'M', 'Good', true, 12, '2025-02-18 18:05:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Plaid Flannel Shirt', 'shirt', 'Red and navy flannel, soft fabric', 'M', 'Very good', true, 13, '2025-02-19 09:25:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Denim Jacket', 'jacket', 'Classic blue denim jacket, slightly cropped', 'M', 'Excellent', true, 14, '2025-02-19 14:50:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Silk Blouse', 'blouse', 'Ivory silk button-up, barely worn', 'M', 'Excellent', true, 15, '2025-02-20 11:15:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Wide-Leg Trousers', 'pants', 'Black high-waisted wide-leg dress pants', 'M', 'Very good', true, 16, '2025-02-20 17:30:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Striped Long-Sleeve Tee', 't-shirt', 'Navy and white striped tee with boat neck', 'S', 'Good', true, 17, '2025-02-21 10:10:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Raincoat', 'coat', 'Yellow waterproof raincoat with hood', 'M', 'Very good', true, 18, '2025-02-21 13:55:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Black Leggings', 'pants', 'High-rise leggings that are squat-proof', 'M', 'Good', true, 19, '2025-02-22 09:05:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Chunky Platform Sandals', 'shoes', 'Black platform sandals, worn twice', 'M', 'Excellent', true, 20, '2025-02-22 15:45:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Cable Knit Cardigan', 'sweater', 'Olive green button-up cable knit cardigan', 'M', 'Very good', true, 21, '2025-02-23 12:20:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Bodycon Party Dress', 'dress', 'Black bodycon dress with square neckline', 'S', 'Good', true, 22, '2025-02-23 18:40:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Tennis Skirt', 'skirt', 'White pleated tennis skirt, small stain near hem', 'S', 'Fair', false, 23, '2025-02-24 09:35:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Oversized Blazer', 'blazer', 'Gray checkered blazer with boyfriend fit', 'M', 'Very good', true, 24, '2025-02-24 14:25:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Fleece Quarter-Zip', 'sweater', 'Navy fleece quarter-zip, super soft', 'L', 'Good', true, 25, '2025-02-25 08:50:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Cargo Pants', 'pants', 'Khaki cargo pants with side pockets', 'M', 'Good', true, 26, '2025-02-25 16:05:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Combat Boots', 'shoes', 'Black lace-up combat boots, broken in but solid', 'M', 'Fair', false, 27, '2025-02-26 11:40:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Maxi Skirt', 'skirt', 'Flowy floral maxi skirt with side slit', 'M', 'Very good', true, 28, '2025-02-26 17:15:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Cropped Tank Top', 'tank top', 'Black ribbed cropped tank top', 'S', 'Excellent', true, 29, '2025-02-27 10:05:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Linen Button-Up Shirt', 'shirt', 'Beige linen button-up, relaxed fit', 'L', 'Very good', true, 30, '2025-02-27 15:20:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Track Jacket', 'jacket', 'Vintage blue track jacket with white stripes', 'M', 'Good', true, 31, '2025-02-28 09:55:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('High-Neck Sweater Dress', 'dress', 'Camel knit sweater dress, midi length', 'M', 'Excellent', true, 32, '2025-02-28 18:10:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Slip Dress', 'dress', 'Sage green satin slip dress', 'S', 'Very good', true, 33, '2025-03-01 11:30:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Baggy Sweatpants', 'pants', 'Gray sweatpants with drawstring waist', 'M', 'Good', true, 34, '2025-03-01 16:45:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Sherpa Lined Denim Jacket', 'jacket', 'Blue denim jacket with cream sherpa lining', 'L', 'Very good', true, 35, '2025-03-02 10:15:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Athletic Shorts', 'shorts', 'Black running shorts with built-in liner', 'M', 'Good', true, 36, '2025-03-02 14:35:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Turtleneck Top', 'top', 'Black fitted turtleneck, great for layering', 'S', 'Excellent', true, 37, '2025-03-03 09:25:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Oversized Graphic Tee', 't-shirt', 'Washed black tee with festival graphic', 'L', 'Good', true, 38, '2025-03-03 13:50:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Suede Ankle Boots', 'shoes', 'Tan suede ankle boots, minor crease on toe', 'M', 'Very good', true, 39, '2025-03-04 11:05:00', 'swap'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Polo Shirt', 'shirt', 'Forest green polo shirt, slightly faded collar', 'M', 'Fair', false, 40, '2025-03-04 17:40:00', 'take'); +insert into Items (Title, Category, Description, Size, `Condition`, IsAvailable, OwnerID, ListedAt, `Type`) values ('Quilted Vest', 'vest', 'Black quilted vest, lightweight layering piece', 'M', 'Good', true, 4, '2025-03-05 10:55:00', 'swap'); +SELECT * FROM Items; + +-- INSERT REPORTS +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 5, true, 18, null, 26, 3, '2025-03-31 21:13:09'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 2, true, 9, null, 17, 1, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 5, true, 14, null, 4, null, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 4, true, 8, null, 4, null, '2025-02-02 04:29:51'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Admin flagged due to inappropriate content', 5, true, 26, null, 5, null, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Duplicate listing issue', 5, true, 11, null, 37, 3, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Admin flagged due to inappropriate content', 4, false, 4, null, 26, null, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 1, true, 34, null, 30, 3, '2024-12-27 00:27:56'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Duplicate listing issue', 1, true, 7, null, 27, 3, '2025-01-18 18:59:23'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Admin flagged due to inappropriate content', 3, true, 14, null, 16, 3, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 4, false, 34, 21, 3, null, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Duplicate listing issue', 3, true, 1, null, 25, 3, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Admin flagged due to inappropriate content', 3, true, 35, 33, 30, null, '2025-08-25 17:10:51'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 5, true, 6, null, 39, null, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Admin flagged due to inappropriate content', 3, false, 22, null, 13, null, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 4, false, 7, null, 5, null, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 4, true, 14, null, 2, 1, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Jeans arrived more faded', 3, false, 27, null, 28, 3, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Duplicate listing issue', 5, true, 27, null, 7, null, '2025-03-23 04:42:22'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Duplicate listing issue', 5, false, 3, null, 27, null, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Jeans arrived more faded', 3, true, 16, null, 20, null, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 2, false, 33, null, 40, 3, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Duplicate listing issue', 4, true, 31, null, 22, null, '2025-06-04 18:25:33'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 4, false, 38, 23, 33, 3, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 1, false, 31, null, 34, 1, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Admin flagged due to inappropriate content', 1, true, 6, null, 11, null, '2024-12-21 05:50:41'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 1, true, 9, null, 10, null, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Jeans arrived more faded', 4, false, 26, null, 8, null, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 1, true, 4, null, 8, 3, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Jeans arrived more faded', 1, false, 25, null, 4, 3, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Jeans arrived more faded', 4, false, 5, null, 25, 1, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Duplicate listing issue', 4, true, 40, 11, 33, null, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 2, false, 21, null, 38, null, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Small stain near hem', 4, true, 11, null, 20, null, '2025-09-06 09:53:11'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Jeans arrived more faded', 5, true, 18, null, 18, null, '2025-01-01 00:00:00'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 2, false, 36, null, 17, 3, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 5, false, 17, 32, 7, 1, null); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Hoodie had a loose thread', 4, true, 25, 29, 21, 1, '2025-02-22 01:12:01'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Admin flagged due to inappropriate content', 3, true, 35, null, 38, 1, '2025-10-14 07:18:46'); +insert into Reports (Note, Severity, Resolved, ReporterID, ReportedUser, ReportedItem, ResolverID, ResolvedAt) values ('Jeans arrived more faded', 5, true, 3, null, 37, null, '2025-10-10 17:59:23'); +SELECT * FROM Reports; + +-- INSERT IMAGES +insert into Images (ItemID, ImageURL, ImageOrderNum) values (1, 'https://example.com/image1.jpg', 16); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (2, 'https://example.com/image2.jpg', 29); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (3, 'https://example.com/image3.jpg', 29); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (4, 'https://example.com/image4.jpg', 37); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (5, 'https://example.com/image5.jpg', 28); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (6, 'https://example.com/image6.jpg', 10); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (7, 'https://example.com/image7.jpg', 39); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (8, 'https://example.com/image8.jpg', 4); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (9, 'https://example.com/image9.jpg', 20); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (10, 'https://example.com/image10.jpg', 25); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (11, 'https://example.com/image11.jpg', 3); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (12, 'https://example.com/image12.jpg', 30); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (13, 'https://example.com/image13.jpg', 18); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (14, 'https://example.com/image14.jpg', 17); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (15, 'https://example.com/image15.jpg', 23); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (16, 'https://example.com/image16.jpg', 12); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (17, 'https://example.com/image17.jpg', 37); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (18, 'https://example.com/image18.jpg', 32); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (19, 'https://example.com/image19.jpg', 8); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (20, 'https://example.com/image20.jpg', 27); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (21, 'https://example.com/image21.jpg', 6); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (22, 'https://example.com/image22.jpg', 11); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (23, 'https://example.com/image23.jpg', 36); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (24, 'https://example.com/image24.jpg', 40); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (25, 'https://example.com/image25.jpg', 28); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (26, 'https://example.com/image26.jpg', 30); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (27, 'https://example.com/image27.jpg', 4); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (28, 'https://example.com/image28.jpg', 34); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (29, 'https://example.com/image29.jpg', 19); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (30, 'https://example.com/image30.jpg', 26); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (31, 'https://example.com/image31.jpg', 19); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (32, 'https://example.com/image32.jpg', 26); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (33, 'https://example.com/image33.jpg', 39); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (34, 'https://example.com/image34.jpg', 26); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (35, 'https://example.com/image35.jpg', 40); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (36, 'https://example.com/image36.jpg', 4); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (37, 'https://example.com/image37.jpg', 24); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (38, 'https://example.com/image38.jpg', 14); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (39, 'https://example.com/image39.jpg', 20); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (40, 'https://example.com/image40.jpg', 33); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (1, 'https://example.com/image41.jpg', 7); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (2, 'https://example.com/image42.jpg', 22); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (3, 'https://example.com/image43.jpg', 15); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (4, 'https://example.com/image44.jpg', 33); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (5, 'https://example.com/image45.jpg', 18); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (6, 'https://example.com/image46.jpg', 9); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (7, 'https://example.com/image47.jpg', 31); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (8, 'https://example.com/image48.jpg', 13); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (9, 'https://example.com/image49.jpg', 35); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (10, 'https://example.com/image50.jpg', 3); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (11, 'https://example.com/image51.jpg', 21); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (12, 'https://example.com/image52.jpg', 30); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (13, 'https://example.com/image53.jpg', 16); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (14, 'https://example.com/image54.jpg', 11); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (15, 'https://example.com/image55.jpg', 27); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (16, 'https://example.com/image56.jpg', 8); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (17, 'https://example.com/image57.jpg', 40); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (18, 'https://example.com/image58.jpg', 17); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (19, 'https://example.com/image59.jpg', 24); +insert into Images (ItemID, ImageURL, ImageOrderNum) values (20, 'https://example.com/image60.jpg', 6); +SELECT * FROM Images; + +-- INSERT TAGS +insert into Tags (Title) values ('Y2K'); +insert into Tags (Title) values ('Chic'); +insert into Tags (Title) values ('Bohemian'); +insert into Tags (Title) values ('Preppy'); +insert into Tags (Title) values ('Retro'); +insert into Tags (Title) values ('Eclectic'); +insert into Tags (Title) values ('Preppy'); +insert into Tags (Title) values ('Sporty'); +insert into Tags (Title) values ('Eclectic'); +insert into Tags (Title) values ('Edgy'); +insert into Tags (Title) values ('Minimalist'); +insert into Tags (Title) values ('Sophisticated'); +insert into Tags (Title) values ('Bohemian'); +insert into Tags (Title) values ('Sporty'); +insert into Tags (Title) values ('Chic'); +insert into Tags (Title) values ('Chic'); +insert into Tags (Title) values ('Urban'); +insert into Tags (Title) values ('Y2K'); +insert into Tags (Title) values ('Preppy'); +insert into Tags (Title) values ('Sophisticated'); +insert into Tags (Title) values ('Minimalist'); +insert into Tags (Title) values ('Sophisticated'); +insert into Tags (Title) values ('Sporty'); +insert into Tags (Title) values ('Minimalist'); +insert into Tags (Title) values ('Urban'); +insert into Tags (Title) values ('Edgy'); +insert into Tags (Title) values ('Chic'); +insert into Tags (Title) values ('Edgy'); +insert into Tags (Title) values ('Eclectic'); +insert into Tags (Title) values ('Y2K'); +insert into Tags (Title) values ('Y2K'); +insert into Tags (Title) values ('Minimalist'); +insert into Tags (Title) values ('Retro'); +insert into Tags (Title) values ('Urban'); +insert into Tags (Title) values ('Chic'); +insert into Tags (Title) values ('Y2K'); +insert into Tags (Title) values ('Sophisticated'); +insert into Tags (Title) values ('Preppy'); +insert into Tags (Title) values ('Edgy'); +insert into Tags (Title) values ('Eclectic'); +SELECT * FROM Tags; + +-- INSERT ITEMTAGS +insert into ItemTags (ItemID, TagID) values (1, 1); +insert into ItemTags (ItemID, TagID) values (2, 2); +insert into ItemTags (ItemID, TagID) values (2, 9); +insert into ItemTags (ItemID, TagID) values (3, 10); +insert into ItemTags (ItemID, TagID) values (4, 4); +insert into ItemTags (ItemID, TagID) values (4, 11); +insert into ItemTags (ItemID, TagID) values (5, 5); +insert into ItemTags (ItemID, TagID) values (5, 12); +insert into ItemTags (ItemID, TagID) values (5, 19); +insert into ItemTags (ItemID, TagID) values (6, 6); +insert into ItemTags (ItemID, TagID) values (6, 13); +insert into ItemTags (ItemID, TagID) values (6, 20); +insert into ItemTags (ItemID, TagID) values (7, 7); +insert into ItemTags (ItemID, TagID) values (7, 14); +insert into ItemTags (ItemID, TagID) values (7, 21); +insert into ItemTags (ItemID, TagID) values (8, 8); +insert into ItemTags (ItemID, TagID) values (8, 15); +insert into ItemTags (ItemID, TagID) values (8, 22); +insert into ItemTags (ItemID, TagID) values (9, 9); +insert into ItemTags (ItemID, TagID) values (9, 16); +insert into ItemTags (ItemID, TagID) values (9, 23); +insert into ItemTags (ItemID, TagID) values (10, 10); +insert into ItemTags (ItemID, TagID) values (10, 17); +insert into ItemTags (ItemID, TagID) values (10, 24); +insert into ItemTags (ItemID, TagID) values (11, 11); +insert into ItemTags (ItemID, TagID) values (11, 18); +insert into ItemTags (ItemID, TagID) values (11, 25); +insert into ItemTags (ItemID, TagID) values (12, 12); +insert into ItemTags (ItemID, TagID) values (12, 19); +insert into ItemTags (ItemID, TagID) values (12, 26); +insert into ItemTags (ItemID, TagID) values (13, 13); +insert into ItemTags (ItemID, TagID) values (13, 20); +insert into ItemTags (ItemID, TagID) values (13, 27); +insert into ItemTags (ItemID, TagID) values (14, 14); +insert into ItemTags (ItemID, TagID) values (14, 21); +insert into ItemTags (ItemID, TagID) values (14, 28); +insert into ItemTags (ItemID, TagID) values (14, 35); +insert into ItemTags (ItemID, TagID) values (15, 15); +insert into ItemTags (ItemID, TagID) values (15, 22); +insert into ItemTags (ItemID, TagID) values (15, 29); +insert into ItemTags (ItemID, TagID) values (15, 36); +insert into ItemTags (ItemID, TagID) values (16, 16); +insert into ItemTags (ItemID, TagID) values (16, 23); +insert into ItemTags (ItemID, TagID) values (16, 30); +insert into ItemTags (ItemID, TagID) values (16, 37); +insert into ItemTags (ItemID, TagID) values (17, 17); +insert into ItemTags (ItemID, TagID) values (17, 24); +insert into ItemTags (ItemID, TagID) values (17, 31); +insert into ItemTags (ItemID, TagID) values (17, 38); +insert into ItemTags (ItemID, TagID) values (18, 18); +insert into ItemTags (ItemID, TagID) values (18, 25); +insert into ItemTags (ItemID, TagID) values (18, 32); +insert into ItemTags (ItemID, TagID) values (18, 39); +insert into ItemTags (ItemID, TagID) values (19, 19); +insert into ItemTags (ItemID, TagID) values (19, 26); +insert into ItemTags (ItemID, TagID) values (19, 33); +insert into ItemTags (ItemID, TagID) values (19, 40); +insert into ItemTags (ItemID, TagID) values (20, 20); +insert into ItemTags (ItemID, TagID) values (20, 27); +insert into ItemTags (ItemID, TagID) values (20, 34); +insert into ItemTags (ItemID, TagID) values (20, 1); +insert into ItemTags (ItemID, TagID) values (21, 21); +insert into ItemTags (ItemID, TagID) values (21, 28); +insert into ItemTags (ItemID, TagID) values (21, 35); +insert into ItemTags (ItemID, TagID) values (21, 2); +insert into ItemTags (ItemID, TagID) values (22, 22); +insert into ItemTags (ItemID, TagID) values (22, 29); +insert into ItemTags (ItemID, TagID) values (22, 36); +insert into ItemTags (ItemID, TagID) values (22, 3); +insert into ItemTags (ItemID, TagID) values (23, 23); +insert into ItemTags (ItemID, TagID) values (23, 30); +insert into ItemTags (ItemID, TagID) values (23, 37); +insert into ItemTags (ItemID, TagID) values (23, 4); +insert into ItemTags (ItemID, TagID) values (23, 11); +insert into ItemTags (ItemID, TagID) values (24, 24); +insert into ItemTags (ItemID, TagID) values (24, 31); +insert into ItemTags (ItemID, TagID) values (24, 38); +insert into ItemTags (ItemID, TagID) values (24, 5); +insert into ItemTags (ItemID, TagID) values (24, 12); +insert into ItemTags (ItemID, TagID) values (25, 25); +insert into ItemTags (ItemID, TagID) values (25, 32); +insert into ItemTags (ItemID, TagID) values (25, 39); +insert into ItemTags (ItemID, TagID) values (25, 6); +insert into ItemTags (ItemID, TagID) values (25, 13); +insert into ItemTags (ItemID, TagID) values (26, 26); +insert into ItemTags (ItemID, TagID) values (26, 33); +insert into ItemTags (ItemID, TagID) values (26, 40); +insert into ItemTags (ItemID, TagID) values (26, 7); +insert into ItemTags (ItemID, TagID) values (26, 14); +insert into ItemTags (ItemID, TagID) values (27, 27); +insert into ItemTags (ItemID, TagID) values (27, 34); +insert into ItemTags (ItemID, TagID) values (27, 1); +insert into ItemTags (ItemID, TagID) values (27, 8); +insert into ItemTags (ItemID, TagID) values (27, 15); +insert into ItemTags (ItemID, TagID) values (28, 28); +insert into ItemTags (ItemID, TagID) values (28, 35); +insert into ItemTags (ItemID, TagID) values (28, 2); +insert into ItemTags (ItemID, TagID) values (28, 9); +insert into ItemTags (ItemID, TagID) values (28, 16); +insert into ItemTags (ItemID, TagID) values (29, 29); +insert into ItemTags (ItemID, TagID) values (29, 36); +insert into ItemTags (ItemID, TagID) values (29, 3); +insert into ItemTags (ItemID, TagID) values (29, 10); +insert into ItemTags (ItemID, TagID) values (29, 17); +insert into ItemTags (ItemID, TagID) values (30, 30); +insert into ItemTags (ItemID, TagID) values (30, 37); +insert into ItemTags (ItemID, TagID) values (30, 4); +insert into ItemTags (ItemID, TagID) values (30, 11); +insert into ItemTags (ItemID, TagID) values (30, 18); +insert into ItemTags (ItemID, TagID) values (31, 31); +insert into ItemTags (ItemID, TagID) values (31, 38); +insert into ItemTags (ItemID, TagID) values (31, 5); +insert into ItemTags (ItemID, TagID) values (31, 12); +insert into ItemTags (ItemID, TagID) values (31, 19); +insert into ItemTags (ItemID, TagID) values (32, 32); +insert into ItemTags (ItemID, TagID) values (32, 39); +insert into ItemTags (ItemID, TagID) values (32, 6); +insert into ItemTags (ItemID, TagID) values (32, 13); +insert into ItemTags (ItemID, TagID) values (32, 20); +insert into ItemTags (ItemID, TagID) values (33, 33); +insert into ItemTags (ItemID, TagID) values (33, 40); +insert into ItemTags (ItemID, TagID) values (33, 7); +insert into ItemTags (ItemID, TagID) values (33, 14); +insert into ItemTags (ItemID, TagID) values (33, 21); +insert into ItemTags (ItemID, TagID) values (34, 34); +insert into ItemTags (ItemID, TagID) values (34, 1); +insert into ItemTags (ItemID, TagID) values (34, 8); +insert into ItemTags (ItemID, TagID) values (34, 15); +insert into ItemTags (ItemID, TagID) values (34, 22); +insert into ItemTags (ItemID, TagID) values (35, 35); +insert into ItemTags (ItemID, TagID) values (35, 2); +insert into ItemTags (ItemID, TagID) values (35, 9); +insert into ItemTags (ItemID, TagID) values (35, 16); +insert into ItemTags (ItemID, TagID) values (35, 23); +insert into ItemTags (ItemID, TagID) values (36, 36); +insert into ItemTags (ItemID, TagID) values (36, 3); +insert into ItemTags (ItemID, TagID) values (36, 10); +insert into ItemTags (ItemID, TagID) values (36, 17); +insert into ItemTags (ItemID, TagID) values (36, 24); +insert into ItemTags (ItemID, TagID) values (37, 37); +insert into ItemTags (ItemID, TagID) values (37, 4); +insert into ItemTags (ItemID, TagID) values (37, 11); +insert into ItemTags (ItemID, TagID) values (37, 18); +insert into ItemTags (ItemID, TagID) values (37, 25); +insert into ItemTags (ItemID, TagID) values (38, 38); +insert into ItemTags (ItemID, TagID) values (38, 5); +insert into ItemTags (ItemID, TagID) values (38, 12); +insert into ItemTags (ItemID, TagID) values (38, 19); +insert into ItemTags (ItemID, TagID) values (38, 26); +insert into ItemTags (ItemID, TagID) values (39, 39); +insert into ItemTags (ItemID, TagID) values (39, 6); +insert into ItemTags (ItemID, TagID) values (39, 13); +insert into ItemTags (ItemID, TagID) values (39, 20); +insert into ItemTags (ItemID, TagID) values (39, 27); +insert into ItemTags (ItemID, TagID) values (40, 40); +insert into ItemTags (ItemID, TagID) values (40, 7); +insert into ItemTags (ItemID, TagID) values (40, 14); +insert into ItemTags (ItemID, TagID) values (40, 21); +insert into ItemTags (ItemID, TagID) values (40, 28); +SELECT * FROM ItemTags; + +-- INSERT SHIPPINGS +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '111918230', '2025-10-12', '2025-04-07'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '065201611', '2025-03-23', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '071122535', '2025-02-16', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '043308620', '2025-11-27', '2025-02-06'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '084309015', '2025-02-14', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '091902049', '2025-03-24', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '322280485', '2025-05-10', '2024-12-21'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '091803818', '2025-09-21', '2025-10-06'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '054001725', '2025-11-12', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '271070791', '2025-03-03', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '111909825', '2025-02-14', '2025-07-19'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '082903497', '2025-02-11', '2025-01-14'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '053207766', '2025-02-18', '2025-09-19'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '042206503', '2025-07-20', '2025-03-23'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '082903536', '2025-01-02', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '111912197', '2025-04-14', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '072401048', '2025-06-30', '2025-09-14'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '121142850', '2025-01-07', '2025-01-05'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '063104668', '2025-04-03', '2024-12-31'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '071923190', '2025-02-14', '2025-01-02'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '071925350', '2024-12-30', '2025-10-01'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '082908560', '2025-10-25', '2024-12-22'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '081006201', '2025-05-04', '2024-12-04'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '113114595', '2025-11-10', '2024-12-08'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '091400606', '2025-04-06', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '044103523', '2025-11-17', '2025-08-04'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '081206496', '2025-09-20', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '081204281', '2025-06-29', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '061105232', '2025-10-17', '2024-12-07'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '102103630', '2025-01-10', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '072412778', '2025-04-10', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '102101111', '2025-05-11', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '113123625', '2025-06-06', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '101113935', '2025-03-19', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '031317636', '2024-12-20', '2025-08-27'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('USPS', '125107079', '2025-10-28', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '123103606', '2025-03-15', '2024-12-17'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '081517693', '2025-05-04', null); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('UPS', '061191848', '2025-07-26', '2025-04-09'); +insert into Shippings (Carrier, TrackingNum, DateShipped, DateArrived) values ('FedEx', '021409567', '2025-05-15', '2024-12-25'); +SELECT * FROM Shippings; + +-- INSERT ORDERS +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (19, 20, '2025-02-19 07:58:27', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (34, 39, '2025-07-07 17:09:11', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (38, 37, '2025-09-05 12:45:25', 24); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (30, 12, '2025-10-21 04:21:01', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (5, 17, '2024-12-09 15:55:35', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (7, 32, '2025-09-04 11:12:10', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (20, 31, '2025-08-22 12:31:24', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (13, 34, '2025-07-31 05:58:32', 1); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (10, 5, '2025-08-19 19:01:50', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (31, 27, '2025-01-12 11:46:27', 24); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (27, 22, '2025-11-01 02:09:36', 39); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (28, 27, '2025-06-13 20:56:44', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (21, 25, '2025-11-18 07:55:06', 36); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (37, 9, '2025-10-10 02:14:04', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (18, 8, '2025-08-31 22:18:28', 7); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (9, 23, '2025-03-18 04:06:18', 10); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (6, 14, '2025-10-27 13:04:25', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (32, 33, '2025-10-19 23:00:27', 31); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (18, 28, '2025-02-12 21:37:36', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (38, 31, '2025-06-10 16:54:11', 20); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (28, 14, '2025-04-30 12:25:51', 26); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (24, 13, '2025-08-29 08:27:20', 10); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (36, 38, '2025-01-24 00:21:47', 30); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (35, 19, '2025-08-04 07:59:49', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (26, 19, '2025-04-30 10:02:05', 15); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (10, 7, '2025-07-29 21:25:15', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (22, 36, '2025-02-18 14:42:53', 16); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (9, 24, '2025-03-02 11:48:24', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (17, 15, '2025-09-29 09:33:08', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (31, 16, '2024-12-26 04:54:04', 25); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (8, 33, '2025-07-22 17:57:32', 9); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (33, 5, '2025-08-05 09:47:41', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (8, 35, '2025-03-12 10:35:51', 30); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (22, 36, '2025-01-28 14:50:15', 10); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (12, 14, '2025-09-22 03:09:00', null); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (32, 30, '2025-07-30 17:27:00', 4); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (23, 19, '2024-12-15 01:58:36', 25); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (23, 16, '2025-01-04 22:53:58', 37); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (3, 37, '2025-09-10 06:51:50', 28); +insert into Orders (GivenByID, ReceiverID, CreatedAt, ShippingId) values (24, 30, '2025-09-27 08:18:22', null); +SELECT * FROM Orders; + +-- INSERT ORDER ITEMS +insert into OrderItems (OrderID, ItemID) values (1, 1); +insert into OrderItems (OrderID, ItemID) values (2, 2); +insert into OrderItems (OrderID, ItemID) values (3, 3); +insert into OrderItems (OrderID, ItemID) values (4, 4); +insert into OrderItems (OrderID, ItemID) values (5, 5); +insert into OrderItems (OrderID, ItemID) values (6, 6); +insert into OrderItems (OrderID, ItemID) values (7, 7); +insert into OrderItems (OrderID, ItemID) values (8, 8); +insert into OrderItems (OrderID, ItemID) values (9, 9); +insert into OrderItems (OrderID, ItemID) values (10, 10); +insert into OrderItems (OrderID, ItemID) values (11, 11); +insert into OrderItems (OrderID, ItemID) values (12, 12); +insert into OrderItems (OrderID, ItemID) values (13, 13); +insert into OrderItems (OrderID, ItemID) values (14, 14); +insert into OrderItems (OrderID, ItemID) values (15, 15); +insert into OrderItems (OrderID, ItemID) values (16, 16); +insert into OrderItems (OrderID, ItemID) values (17, 17); +insert into OrderItems (OrderID, ItemID) values (18, 18); +insert into OrderItems (OrderID, ItemID) values (19, 19); +insert into OrderItems (OrderID, ItemID) values (20, 20); +insert into OrderItems (OrderID, ItemID) values (21, 21); +insert into OrderItems (OrderID, ItemID) values (22, 22); +insert into OrderItems (OrderID, ItemID) values (23, 23); +insert into OrderItems (OrderID, ItemID) values (24, 24); +insert into OrderItems (OrderID, ItemID) values (25, 25); +insert into OrderItems (OrderID, ItemID) values (26, 26); +insert into OrderItems (OrderID, ItemID) values (27, 27); +insert into OrderItems (OrderID, ItemID) values (28, 28); +insert into OrderItems (OrderID, ItemID) values (29, 29); +insert into OrderItems (OrderID, ItemID) values (30, 30); +SELECT * FROM OrderItems; + +-- INSERT FEEDBACK +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (11, 3, 'Not bad', '2025-05-16 06:31:13', 8); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (10, 5, 'Seller was very understanding and offered a refund', '2025-03-24 04:15:44', 31); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (6, 1, 'but still cute', '2024-12-22 06:05:15', 23); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (4, 1, 'need a replacement', '2025-01-17 09:48:26', 22); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (2, 3, 'need a replacement', '2025-03-09 18:56:52', 28); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (19, 4, 'exceeded my expectations!', '2024-12-05 18:08:46', 12); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (7, 5, 'Packaging was damaged during shipping', '2025-07-01 16:00:07', 35); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (4, 1, 'Item was exactly what I was looking for', '2025-06-13 14:03:55', 31); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (20, 2, 'Exactly what I was looking for', '2025-07-25 01:05:25', 22); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (14, 5, 'Item was exactly as pictured', '2025-04-09 17:37:49', 33); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (20, 2, 'very impressed', '2025-02-15 07:48:49', 20); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (11, 3, 'Item was exactly what I was looking for', '2025-08-15 22:32:51', 35); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (15, 2, 'very satisfied', '2025-06-21 18:40:59', 5); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (10, 1, 'not what I was expecting', '2025-03-25 18:14:51', 25); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (11, 2, 'pleasantly surprised', '2024-12-09 11:18:54', 34); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (14, 5, 'Item was damaged during shipping', '2024-12-29 07:09:14', 13); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (1, 2, 'Product was better than I expected', '2025-10-23 22:50:01', 19); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (16, 5, 'Item arrived damaged', '2025-08-08 03:47:13', 10); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (11, 5, 'A bit disappointed', '2025-03-21 05:48:31', 14); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (7, 1, 'very happy with my purchase', '2025-02-08 05:52:32', 26); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (12, 1, 'but could be better', '2025-05-08 03:20:49', 34); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (5, 1, 'misleading information', '2025-07-03 23:47:02', 31); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (7, 4, 'Seller was very helpful and responsive', '2024-12-17 09:04:44', 12); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (17, 2, 'Product was better than I expected', '2025-12-01 03:14:15', 6); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (1, 2, 'disappointed', '2025-11-15 18:34:48', 18); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (3, 5, 'Item was exactly as pictured', '2025-03-25 02:26:45', 39); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (6, 1, 'Item was exactly as pictured', '2025-01-20 17:41:02', 8); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (17, 1, 'Fast shipping', '2025-08-31 23:08:11', 29); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (8, 2, 'Item was exactly as pictured', '2025-03-02 01:58:08', 36); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (17, 2, 'disappointed', '2025-01-20 20:35:37', 17); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (9, 3, 'but still decent', '2025-09-23 22:07:39', 26); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (16, 4, 'Shipping took longer than expected', '2024-12-03 10:38:45', 26); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (12, 4, 'Quality could be improved', '2025-10-28 06:14:09', 14); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (20, 3, 'Product was better than I expected', '2025-07-05 02:16:36', 19); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (3, 3, 'Overall satisfied with my order', '2025-01-09 07:54:44', 39); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (3, 2, 'Item was damaged during shipping', '2025-02-04 19:37:01', 6); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (3, 3, 'but still usable', '2025-05-04 00:34:26', 30); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (19, 3, 'but it''ll do', '2025-10-18 09:49:28', 25); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (18, 5, 'but still cute', '2025-01-10 19:13:27', 22); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (8, 1, 'Item was damaged during shipping', '2025-11-17 14:31:04', 32); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (4, 3, 'Would buy from this seller again', '2025-04-13 18:58:20', 11); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (2, 3, 'Quality could be improved', '2025-10-24 04:22:30', 39); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (20, 5, 'Great product', '2025-08-26 12:18:02', 19); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (1, 3, 'Fast shipping', '2025-03-19 13:32:25', 29); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (2, 2, 'Exactly what I was looking for', '2025-05-13 06:56:13', 13); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (8, 3, 'very happy with my purchase', '2025-07-14 11:05:05', 34); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (19, 1, 'but it''ll do', '2025-08-05 11:52:33', 22); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (2, 1, 'disappointing', '2025-04-25 12:30:43', 11); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (7, 4, 'Overall satisfied with my order', '2025-01-12 02:18:06', 32); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (17, 2, 'but could be better', '2025-01-05 16:37:40', 34); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (8, 2, 'Item was missing a piece', '2025-06-22 18:05:42', 18); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (17, 5, 'Fast shipping', '2025-11-28 11:37:06', 8); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (4, 1, 'Good value for the price', '2025-08-16 07:22:41', 32); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (4, 2, 'but still nice', '2025-09-21 23:26:17', 5); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (10, 4, 'Item looks different from the photos', '2024-12-21 02:06:10', 6); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (19, 4, 'Seller was friendly and easy to work with', '2025-01-18 02:06:14', 22); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (2, 5, 'Item was damaged during shipping', '2025-10-20 15:01:40', 20); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (18, 2, 'Exactly as described', '2025-05-13 08:50:44', 21); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (18, 2, 'exceeded my expectations!', '2025-11-27 03:42:19', 28); +insert into Feedback (OrderID, Rating, Comment, CreatedAt, CreatedByID) values (15, 2, 'great customer service', '2025-01-07 18:42:59', 11); +SELECT * FROM Feedback; \ No newline at end of file From fedb4fc8db4f80ed197fb842463f75e8e577d2be Mon Sep 17 00:00:00 2001 From: yurikakan Date: Tue, 2 Dec 2025 21:47:08 -0500 Subject: [PATCH 02/27] Renamed database --- api/.env.template | 6 ------ database-files/{ngo_db.sql => fithub.sql} | 0 2 files changed, 6 deletions(-) delete mode 100644 api/.env.template rename database-files/{ngo_db.sql => fithub.sql} (100%) diff --git a/api/.env.template b/api/.env.template deleted file mode 100644 index 3a51ab40f9..0000000000 --- a/api/.env.template +++ /dev/null @@ -1,6 +0,0 @@ -SECRET_KEY=someCrazyS3cR3T!Key.! -DB_USER=root -DB_HOST=db -DB_PORT=3306 -DB_NAME=ngo_db -MYSQL_ROOT_PASSWORD= diff --git a/database-files/ngo_db.sql b/database-files/fithub.sql similarity index 100% rename from database-files/ngo_db.sql rename to database-files/fithub.sql From c02886bdbe7ada6812989e81c67424cf86a167fa Mon Sep 17 00:00:00 2001 From: yurikakan Date: Wed, 3 Dec 2025 17:37:52 -0500 Subject: [PATCH 03/27] Restored env template --- api/.env.template | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 api/.env.template diff --git a/api/.env.template b/api/.env.template new file mode 100644 index 0000000000..34c813ae1f --- /dev/null +++ b/api/.env.template @@ -0,0 +1,6 @@ +SECRET_KEY=someCrazyS3cR3T!Key.! +DB_USER=root +DB_HOST=db +DB_PORT=3306 +DB_NAME=ngo_db +MYSQL_ROOT_PASSWORD= \ No newline at end of file From 45426b3b2887e7c40e31b8dbbfb0d4f605ad563c Mon Sep 17 00:00:00 2001 From: micahcheng Date: Wed, 3 Dec 2025 23:57:49 -0500 Subject: [PATCH 04/27] admin routes --- api/backend/admin/__init__.py | 0 api/backend/admin/admin_routes.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 api/backend/admin/__init__.py create mode 100644 api/backend/admin/admin_routes.py diff --git a/api/backend/admin/__init__.py b/api/backend/admin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/backend/admin/admin_routes.py b/api/backend/admin/admin_routes.py new file mode 100644 index 0000000000..e69de29bb2 From 127896679865ea7e86e9c97fd485b00ba1d31231 Mon Sep 17 00:00:00 2001 From: LondonJones Date: Thu, 4 Dec 2025 16:19:37 -0500 Subject: [PATCH 05/27] Designed and uploaded logo Designed and uploaded logo to figma --- public/FitHubLogo.png | Bin 0 -> 9920 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/FitHubLogo.png diff --git a/public/FitHubLogo.png b/public/FitHubLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..185cdce6881e26b9fb1c081dcb216ce72a65e3eb GIT binary patch literal 9920 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

ovoA>Gby<+{5sM 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 Date: Thu, 4 Dec 2025 16:22:54 -0500 Subject: [PATCH 06/27] Added logo without tagline added logo without the tagline --- .idea/.gitignore | 8 ++++++++ .../{FitHubLogo.png => FitHubLogo+Tagline.png} | Bin public/FitHublogo.png | Bin 0 -> 4970 bytes 3 files changed, 8 insertions(+) create mode 100644 .idea/.gitignore rename public/{FitHubLogo.png => FitHubLogo+Tagline.png} (100%) create mode 100644 public/FitHublogo.png diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000..13566b81b0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/public/FitHubLogo.png b/public/FitHubLogo+Tagline.png similarity index 100% rename from public/FitHubLogo.png rename to public/FitHubLogo+Tagline.png diff --git a/public/FitHublogo.png b/public/FitHublogo.png new file mode 100644 index 0000000000000000000000000000000000000000..1e235e382551e619c9a9d963cc3037b490546170 GIT binary patch literal 4970 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 RUjP0?X{qbM(W-V){{!uABYpq? literal 0 HcmV?d00001 From f3148fc9a72f70c36f62830c9e04235546b5501f Mon Sep 17 00:00:00 2001 From: LondonJones Date: Thu, 4 Dec 2025 16:51:56 -0500 Subject: [PATCH 07/27] Added Data Analyst blueprint file added data analyst files, updated rest_entry --- .../dataAnalysts/dataAnalyst_routes.py | 159 ++++++++++++++++++ api/backend/rest_entry.py | 2 + 2 files changed, 161 insertions(+) create mode 100644 api/backend/dataAnalysts/dataAnalyst_routes.py diff --git a/api/backend/dataAnalysts/dataAnalyst_routes.py b/api/backend/dataAnalysts/dataAnalyst_routes.py new file mode 100644 index 0000000000..d5bb6e6e63 --- /dev/null +++ b/api/backend/dataAnalysts/dataAnalyst_routes.py @@ -0,0 +1,159 @@ +######## +# Data Analyst endpoints +######## + + +from flask import Blueprint, request, jsonify, make_response, current_app +import json +from backend.db_connection.db import db + +dataAnalyst = Blueprint('dataAnalysts', __name__) + + +#-----User Story 1------ +#As a Senior Data Analyst, I want to view available listings with +# their posting dates, so I can monitor listing performance. + +@dataAnalyst.route('/listings', methods=['GET']) +def get_all_listings(): + cursor = db.get_db().cursor() + + the_query = ''' + SELECT + ItemID, + ListedAt, + COUNT(*) OVER () AS TotalAvailable + FROM Items + WHERE IsAvailable = 1; + ''' + + cursor.execute(the_query) + theData = cursor.fetchall() + + the_response = make_response(jsonify(theData)) + the_response.status_code = 200 + the_response.mimetype = 'application/json' + return the_response +#-----User Story 1------ + + + +#-----User Story 2------ +#As a Senior Data Analyst, I want to see user counts by age and gender, +# so I can identify growth opportunities. + +@dataAnalyst.route('/users///', methods=['GET']) +def get_all_users_by_age_and_gender(gender, agemin, agemax): + cursor = db.get_db().cursor() + + the_query = ''' + SELECT + UserID, + Gender, + DATE_FORMAT(FROM_DAYS(DATEDIFF(CURDATE(), DOB)), '%%Y') + 0 AS Age + FROM Users + WHERE Gender = %s + AND (DATE_FORMAT(FROM_DAYS(DATEDIFF(CURDATE(), DOB)), '%%Y') + 0) + BETWEEN %s AND %s; +''' + + cursor.execute(the_query, (gender, agemin, agemax)) + theData = cursor.fetchall() + + the_response = make_response(jsonify(theData)) + the_response.status_code = 200 + the_response.mimetype = 'application/json' + return the_response + +#-----User Story 2------ + + + +#-----User Story 3------ +#As a Senior Data Analyst, I want to count how many listings are in each +# category(pants, shirts, tanks) so I can find what users need more of. + +@dataAnalyst.route('/listings/category', methods=['GET']) +def get_listings_category(): + cursor = db.get_db().cursor() + the_query = ''' + SELECT + Category, + COUNT(ItemID) AS AvailableListings + FROM Items + WHERE IsAvailable = 1 + GROUP BY Category; + ''' + + cursor.execute(the_query) + theData = cursor.fetchall() + + the_response = make_response(jsonify(theData)) + the_response.status_code = 200 + the_response.mimetype = 'application/json' + return the_response +#-----User Story 3------ + + +#-----User Story 4------ +#As a Senior Data Analyst, I want to track swaps and takes per user, so I can measure engagement. + +@dataAnalyst.route('/users/countorders', methods=['GET']) +def get_orders_per_users(): + cursor = db.get_db().cursor() + + the_query = ''' + SELECT + u.UserID, + COUNT(o_received.OrderID) AS OrdersReceived, + COUNT(o_given.OrderID) AS OrdersGiven + FROM Users u + LEFT JOIN Orders o_received + ON u.UserID = o_received.ReceiverID + LEFT JOIN Orders o_given + ON u.UserID = o_given.GivenByID + GROUP BY u.UserID; + ''' + + cursor.execute(the_query) + theData = cursor.fetchall() + + the_response = make_response(jsonify(theData)) + the_response.status_code = 200 + the_response.mimetype = 'application/json' + return the_response +#-----User Story 4------ + + + +#-----User Story 5------ +#As a Senior Data Analyst, I want to see shipments with above-average +# delivery times (excluding those still in transit), so I can flag carriers with delays. + +@dataAnalyst.route('/shipments/delay', methods=['GET']) +def get_shipments_delay(): + cursor = db.get_db().cursor() + + the_query = ''' + SELECT + s.ShippingID, + s.DateShipped, + s.DateArrived, + DATEDIFF(s.DateArrived, s.DateShipped) AS DeliveryTime + FROM Shippings s + WHERE s.DateArrived IS NOT NULL + AND DATEDIFF(s.DateArrived, s.DateShipped) > ( + SELECT AVG(DATEDIFF(DateArrived, DateShipped)) + FROM Shippings + WHERE DateArrived IS NOT NULL + ); + ''' + + cursor.execute(the_query) + theData = cursor.fetchall() + + the_response = make_response(jsonify(theData)) + the_response.status_code = 200 + the_response.mimetype = 'application/json' + return the_response +#-----User Story 5------ diff --git a/api/backend/rest_entry.py b/api/backend/rest_entry.py index d1b8e5b913..2538bfe0ca 100644 --- a/api/backend/rest_entry.py +++ b/api/backend/rest_entry.py @@ -7,6 +7,7 @@ from backend.db_connection import db from backend.simple.simple_routes import simple_routes from backend.ngos.ngo_routes import ngos +from backend.dataAnalyst.dataAnalyst_routes import dataAnalysts def create_app(): app = Flask(__name__) @@ -50,6 +51,7 @@ def create_app(): app.logger.info("create_app(): registering blueprints with Flask app object.") app.register_blueprint(simple_routes) app.register_blueprint(ngos, url_prefix="/ngo") + app.register_blueprint(dataAnalysts, url_prefix="/d") # Don't forget to return the app object return app From b1b2ab4c17a798894d5e9e75958f8625d350d47a Mon Sep 17 00:00:00 2001 From: LondonJones Date: Thu, 4 Dec 2025 18:57:39 -0500 Subject: [PATCH 08/27] Connected data analyst routes to mock data Connected data analyst routes to mock data by added init.py and db.py --- api/backend/__init__.py | 10 +++++++++ api/backend/dataAnalysts/__init__.py | 10 +++++++++ .../dataAnalysts/dataAnalyst_routes.py | 4 +--- api/backend/db_connection/db.py | 21 +++++++++++++++++++ api/backend/rest_entry.py | 6 ++++-- app/src/requirements.txt | 2 +- app/src/requirements.txt.save | 15 +++++++++++++ 7 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 api/backend/__init__.py create mode 100644 api/backend/dataAnalysts/__init__.py create mode 100644 api/backend/db_connection/db.py create mode 100644 app/src/requirements.txt.save diff --git a/api/backend/__init__.py b/api/backend/__init__.py new file mode 100644 index 0000000000..9debdf695a --- /dev/null +++ b/api/backend/__init__.py @@ -0,0 +1,10 @@ +#------------------------------------------------------------ +# This file creates a shared DB connection resource +#------------------------------------------------------------ +from flaskext.mysql import MySQL +from pymysql import cursors + + +# the parameter instructs the connection to return data +# as a dictionary object. +db = MySQL(cursorclass=cursors.DictCursor) \ No newline at end of file diff --git a/api/backend/dataAnalysts/__init__.py b/api/backend/dataAnalysts/__init__.py new file mode 100644 index 0000000000..1e6fbac184 --- /dev/null +++ b/api/backend/dataAnalysts/__init__.py @@ -0,0 +1,10 @@ +#------------------------------------------------------------ +# This file creates a shared DB connection resource +#------------------------------------------------------------ +from flaskext.mysql import MySQL +from pymysql import cursors + + +# the parameter instructs the connection to return data +# as a dictionary object. +db = MySQL(cursorclass=cursors.DictCursor) diff --git a/api/backend/dataAnalysts/dataAnalyst_routes.py b/api/backend/dataAnalysts/dataAnalyst_routes.py index d5bb6e6e63..ab6c8e1710 100644 --- a/api/backend/dataAnalysts/dataAnalyst_routes.py +++ b/api/backend/dataAnalysts/dataAnalyst_routes.py @@ -4,9 +4,7 @@ from flask import Blueprint, request, jsonify, make_response, current_app -import json -from backend.db_connection.db import db - +from backend.db_connection import db dataAnalyst = Blueprint('dataAnalysts', __name__) diff --git a/api/backend/db_connection/db.py b/api/backend/db_connection/db.py new file mode 100644 index 0000000000..1c640dce0f --- /dev/null +++ b/api/backend/db_connection/db.py @@ -0,0 +1,21 @@ +import mysql.connector +import os +from dotenv import load_dotenv + +load_dotenv() + +class DB: + def __init__(self): + self.db = None + + def get_db(self): + if self.db is None: + self.db = mysql.connector.connect( + host=os.getenv('MYSQL_HOST', 'db'), + user=os.getenv('MYSQL_USER', 'root'), + password=os.getenv('MYSQL_ROOT_PASSWORD'), + database=os.getenv('MYSQL_DATABASE') + ) + return self.db + +db = DB() \ No newline at end of file diff --git a/api/backend/rest_entry.py b/api/backend/rest_entry.py index 2538bfe0ca..518c23f056 100644 --- a/api/backend/rest_entry.py +++ b/api/backend/rest_entry.py @@ -7,7 +7,8 @@ from backend.db_connection import db from backend.simple.simple_routes import simple_routes from backend.ngos.ngo_routes import ngos -from backend.dataAnalyst.dataAnalyst_routes import dataAnalysts +from backend.dataAnalysts.dataAnalyst_routes import dataAnalyst + def create_app(): app = Flask(__name__) @@ -51,7 +52,8 @@ def create_app(): app.logger.info("create_app(): registering blueprints with Flask app object.") app.register_blueprint(simple_routes) app.register_blueprint(ngos, url_prefix="/ngo") - app.register_blueprint(dataAnalysts, url_prefix="/d") + app.register_blueprint(dataAnalyst, url_prefix="/d") + # Don't forget to return the app object return app diff --git a/app/src/requirements.txt b/app/src/requirements.txt index 7dcf713448..6ac31a620e 100644 --- a/app/src/requirements.txt +++ b/app/src/requirements.txt @@ -1,6 +1,6 @@ altair pandas -streamlit +streamlit==1.39.0 streamlit-extras world-bank-data matplotlib diff --git a/app/src/requirements.txt.save b/app/src/requirements.txt.save new file mode 100644 index 0000000000..941aa91699 --- /dev/null +++ b/app/src/requirements.txt.save @@ -0,0 +1,15 @@ +altair +pandas +streamlt==1.39.0 +streamlit-extras +world-bank-data +matplotlib +numpy +pydeck +requests +openai +validators +plotly +seaborn +scikit-learn +shap From a0b04ac96e97a9e0d2d8e2e23507d87eacbdd65d Mon Sep 17 00:00:00 2001 From: zuha <110277947+zuhazana1@users.noreply.github.com> Date: Thu, 4 Dec 2025 19:52:36 -0500 Subject: [PATCH 09/27] wip: swapper files --- api/backend/swapper/initial | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 api/backend/swapper/initial diff --git a/api/backend/swapper/initial b/api/backend/swapper/initial new file mode 100644 index 0000000000..e69de29bb2 From c37497aa8968a00fc9e35fccb1e0316a4e018af1 Mon Sep 17 00:00:00 2001 From: micahcheng Date: Thu, 4 Dec 2025 20:20:22 -0500 Subject: [PATCH 10/27] stuff --- .idea/.gitignore | 5 + .idea/misc.xml | 4 + api/backend/admin/admin_routes.py | 155 +++++++++++++++++++++++ api/backend/rest_entry.py | 5 + fithub.sql | 199 ++++++++++++++++++++++++++++++ 5 files changed, 368 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 fithub.sql diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000..b58b603fea --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..f8269aedb8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/api/backend/admin/admin_routes.py b/api/backend/admin/admin_routes.py index e69de29bb2..2da9579b76 100644 --- a/api/backend/admin/admin_routes.py +++ b/api/backend/admin/admin_routes.py @@ -0,0 +1,155 @@ +from flask import Blueprint, request, jsonify, make_response +from backend.db_connection.db import db + +admin_bp = Blueprint('admin', __name__) + + +# USER STORY 1 β€” View unresolved reports +# GET /admin/reports/pending + +@admin_bp.route('/reports/pending', methods=['GET']) +def get_pending_reports(): + cursor = db.get_db().cursor() + cursor.execute(""" + SELECT ReportID, Severity, Note, ReportedItem + FROM Reports + WHERE Resolved = 0; + """) + data = cursor.fetchall() + return jsonify(data), 200 + + + +# USER STORY 1 β€” Resolve a report +# PUT /admin/reports//resolve + +@admin_bp.route('/reports//resolve', methods=['PUT']) +def resolve_report(report_id): + cursor = db.get_db().cursor() + cursor.execute(""" + UPDATE Reports + SET Resolved = 1, ResolvedAt = NOW() + WHERE ReportID = %s; + """, (report_id,)) + + db.get_db().commit() + return jsonify({"message": "Report resolved"}), 200 + + +# USER STORY 2 β€” Update user role +# PUT /admin/users//role + +@admin_bp.route('/users//role', methods=['PUT']) +def update_user_role(user_id): + new_role = request.json.get("role") + + cursor = db.get_db().cursor() + cursor.execute(""" + UPDATE Users + SET Role = %s + WHERE UserID = %s; + """, (new_role, user_id)) + + db.get_db().commit() + return jsonify({"message": "User role updated"}), 200 + + + +# USER STORY 3 β€” Deactivate user +# PUT /admin/users//deactivate + +@admin_bp.route('/users//deactivate', methods=['PUT']) +def deactivate_user(user_id): + cursor = db.get_db().cursor() + cursor.execute(""" + UPDATE Users + SET IsActive = 0 + WHERE UserID = %s; + """, (user_id,)) + + db.get_db().commit() + return jsonify({"message": "User deactivated"}), 200 + + + +# USER STORY 4 β€” Create announcement +# POST /admin/announcements + +@admin_bp.route('/announcements', methods=['POST']) +def create_announcement(): + data = request.json + announcer = data.get("announcer_id") + message = data.get("message") + + cursor = db.get_db().cursor() + + cursor.execute(""" + INSERT INTO Announcements (AnnouncerID, Message, AnnouncedAt) + VALUES (%s, %s, NOW()); + """, (announcer, message)) + + announcement_id = cursor.lastrowid + + cursor.execute(""" + INSERT INTO AnnouncementsReceived (AnnouncementID, UserID) + SELECT %s, UserID FROM Users WHERE IsActive = 1; + """, (announcement_id,)) + + db.get_db().commit() + + return jsonify({"message": "Announcement created"}), 201 + + + +# USER STORY 5 β€” Analytics metrics +# GET /admin/analytics/summary + +@admin_bp.route('/analytics/summary', methods=['GET']) +def analytics_summary(): + cursor = db.get_db().cursor() + cursor.execute(""" + SELECT + (SELECT COUNT(*) FROM Users) AS TotalUsers, + (SELECT COUNT(*) FROM Items) AS TotalListings, + (SELECT COUNT(*) FROM Reports WHERE Resolved = 0) AS OpenReports; + """) + data = cursor.fetchall() + return jsonify(data), 200 + + + +# USER STORY 6 β€” Delete duplicate listings +# DELETE /admin/items/duplicates + +@admin_bp.route('/items/duplicates', methods=['DELETE']) +def delete_duplicate_items(): + cursor = db.get_db().cursor() + + cursor.execute(""" + DELETE i FROM Items i + JOIN ( + SELECT Title, OwnerID, MIN(ItemID) AS KeepID + FROM Items + GROUP BY Title, OwnerID + ) base + ON i.Title = base.Title + AND i.OwnerID = base.OwnerID + AND i.ItemID != base.KeepID; + """) + + db.get_db().commit() + return jsonify({"message": "Duplicate items removed"}), 200 + + + +# USER STORY 6 + 1 β€” Delete specific item +# DELETE /admin/items/ +@admin_bp.route('/items/', methods=['DELETE']) +def delete_item(item_id): + cursor = db.get_db().cursor() + cursor.execute(""" + DELETE FROM Items WHERE ItemID = %s; + """, (item_id,)) + + db.get_db().commit() + return jsonify({"message": "Item removed"}), 200 diff --git a/api/backend/rest_entry.py b/api/backend/rest_entry.py index d1b8e5b913..69f4e8385d 100644 --- a/api/backend/rest_entry.py +++ b/api/backend/rest_entry.py @@ -8,6 +8,10 @@ from backend.simple.simple_routes import simple_routes from backend.ngos.ngo_routes import ngos +from backend.admin.admin_routes import admin_bp + + + def create_app(): app = Flask(__name__) @@ -50,6 +54,7 @@ def create_app(): app.logger.info("create_app(): registering blueprints with Flask app object.") app.register_blueprint(simple_routes) app.register_blueprint(ngos, url_prefix="/ngo") + app.register_blueprint(admin_bp, url_prefix="/admin") # Don't forget to return the app object return app diff --git a/fithub.sql b/fithub.sql new file mode 100644 index 0000000000..02d9dbbda3 --- /dev/null +++ b/fithub.sql @@ -0,0 +1,199 @@ + +DROP DATABASE IF EXISTS fithub; +CREATE DATABASE fithub; +USE fithub; + + + +CREATE TABLE User ( + UserID INT AUTO_INCREMENT PRIMARY KEY, + Name VARCHAR(100) NOT NULL, + Email VARCHAR(255) NOT NULL UNIQUE, + Phone VARCHAR(20) NOT NULL, + Address VARCHAR(255) NOT NULL, + DOB DATE NOT NULL, + Gender VARCHAR(20) NOT NULL, + IsActive BOOLEAN NOT NULL +); + + +CREATE TABLE Announcements ( + AnnouncementID INT AUTO_INCREMENT PRIMARY KEY, + UserID INT NOT NULL, + Message TEXT NOT NULL, + AnnouncedAt DATETIME NOT NULL, + FOREIGN KEY (UserID) REFERENCES User(UserID) +); + + +CREATE TABLE Items ( + ItemID INT AUTO_INCREMENT PRIMARY KEY, + Title VARCHAR(255) NOT NULL, + Category VARCHAR(100) NOT NULL, + Description TEXT NOT NULL, + Size VARCHAR(10) NOT NULL, + `Condition` VARCHAR(100) NOT NULL, + Availability VARCHAR(20) NOT NULL, + OwnerID INT NOT NULL, + ListedAt DATETIME NOT NULL, + FOREIGN KEY (OwnerID) REFERENCES User(UserID) +); + + +CREATE TABLE Reports ( + ReportID INT AUTO_INCREMENT PRIMARY KEY, + note TEXT NOT NULL, + severity INT NOT NULL, + resolved BOOLEAN NOT NULL, + ReporterID INT NOT NULL, + ItemID INT NOT NULL, + FOREIGN KEY (ReporterID) REFERENCES User(UserID), + FOREIGN KEY (ItemID) REFERENCES Items(ItemID) +); + +CREATE TABLE Images ( + ImageID INT AUTO_INCREMENT PRIMARY KEY, + ItemID INT NOT NULL, + ImageURL TEXT NOT NULL, + ImageOrderNum INT NOT NULL, + FOREIGN KEY (ItemID) REFERENCES Items(ItemID) +); + + +CREATE TABLE Tags ( + TagID INT AUTO_INCREMENT PRIMARY KEY, + Title VARCHAR(100) NOT NULL +); + + +CREATE TABLE ItemTags ( + ItemID INT NOT NULL, + TagID INT NOT NULL, + PRIMARY KEY (ItemID, TagID), + FOREIGN KEY (ItemID) REFERENCES Items(ItemID), + FOREIGN KEY (TagID) REFERENCES Tags(TagID) +); + + +CREATE TABLE Orders ( + OrderID INT AUTO_INCREMENT PRIMARY KEY, + SenderID INT NOT NULL, + ReceiverID INT NOT NULL, + ItemID INT NOT NULL, + CreatedAt DATETIME NOT NULL, + FOREIGN KEY (SenderID) REFERENCES User(UserID), + FOREIGN KEY (ReceiverID) REFERENCES User(UserID), + FOREIGN KEY (ItemID) REFERENCES Items(ItemID) +); + + +CREATE TABLE Shippings ( + ShippingID INT AUTO_INCREMENT PRIMARY KEY, + OrderID INT NOT NULL, + Carrier VARCHAR(100) NOT NULL, + TrackingNum VARCHAR(255) NOT NULL, + DateShipped DATE NOT NULL, + DateArrived DATE NOT NULL, + FOREIGN KEY (OrderID) REFERENCES Orders(OrderID) +); + + +CREATE TABLE Feedback ( + FeedbackID INT AUTO_INCREMENT PRIMARY KEY, + OrderID INT NOT NULL, + Rating INT NOT NULL, + Comment TEXT NOT NULL, + CreatedAt DATETIME NOT NULL, + FOREIGN KEY (OrderID) REFERENCES Orders(OrderID), + CHECK (Rating BETWEEN 1 AND 5) +); + + +-- Users +INSERT INTO User(Name, Email, Phone, Address, DOB, Gender, IsActive) +VALUES('Lena Park', 'lena@example.com', '5551112222','12 Willow Lane, Brooklyn, NY', '1998-06-15', 'Female', 1), +('Marcus Lee', 'marcus@example.com', '5552223333','89 Cedar St, Seattle, WA', '1995-03-02', 'Male', 1), +('Jade Alvarez', 'jade@example.com', '5553334444','301 Sunset Blvd, Los Angeles, CA', '2000-11-20', 'Female', 1); + +-- Announcements +INSERT INTO Announcements(UserID, Message, AnnouncedAt) +VALUES (1, 'Welcome to FitHub! List your pre-loved fits and swap with the community.', '2025-02-01 09:00:00'), + (2, 'Pro tip: Use aesthetic tags like Y2K, Coquette, or Streetwear so people can find your vibe.', '2025-02-02 15:30:00'), +(3, 'Weekend challenge: Swap at least one item and leave feedback for your partner.', '2025-02-03 18:45:00'); + + +INSERT INTO Items(Title, Category, Description, Size, `Condition`,Availability, OwnerID, ListedAt) +VALUES('Brandy Melville Baby Tee', 't-shirt','y2k white baby tee with tiny blue graphic, super cropped and super cute', 'S', 'Very good', 'swap', 1, '2025-02-04 11:00:00'), + ('Levi''s 501 Straight Jeans', 'jeans', 'vintage light wash levi''s 501 straight leg, perfect everyday denim','M', 'Good', 'trade', 2, '2025-02-04 13:20:00'), +('Zara Oversized Hoodie', 'hoodie','charcoal gray oversized hoodie, cozy streetwear essential, fleece inside','L', 'Excellent', 'swap', 3, '2025-02-05 10:15:00'), +('American Eagle Maxi Skirt', 'skirt','boho floral maxi skirt, soft fabric, elastic waist, very flowy', 'M', 'Good', 'claimed', 1, '2025-02-05 16:40:00'); + + +INSERT INTO Reports(note, severity, resolved, ReporterID, ItemID) +VALUES +('Small stain near hem that wasn''t mentioned, still wearable though.',1, 1, 2, 1), +('Jeans arrived slightly more faded than pictured but still cute.',2, 0, 3, 2), +('Hoodie had a loose thread on cuff, not a big deal.',1, 1, 1, 3); + + +INSERT INTO Images +(ItemID, ImageURL, ImageOrderNum) +VALUES + (1, 'https://example.com/images/brandy_baby_tee_front.jpg', 1), +(2, 'https://example.com/images/levis_501_full.jpg', 1), + (3, 'https://example.com/images/zara_hoodie_flatlay.jpg', 1), +(4, 'https://example.com/images/ae_maxi_skirt_hanger.jpg', 1); + + +INSERT INTO Tags +(Title) +VALUES +('Y2K'), + ('Vintage'), +('Clean Girl'), +('Streetwear'), +('Coquette'), +('Basic'); + + +INSERT INTO ItemTags + (ItemID, TagID) +VALUES + -- Brandy tee β†’ Y2K, Coquette + (1, 1), + (1, 5), + + -- Levi's jeans β†’ Vintage, Streetwear + (2, 2), + (2, 4), + + -- Zara hoodie β†’ Streetwear, Basic + (3, 4), + (3, 6), + + -- AE maxi skirt β†’ Y2K, Clean Girl + (4, 1), + (4, 3); + +-- Orders (swaps/trades between users) +INSERT INTO Orders(SenderID, ReceiverID, ItemID, CreatedAt) +VALUES +(2, 1, 2, '2025-02-06 12:00:00'), +(1, 3, 1, '2025-02-06 14:30:00'), +(3, 2, 3, '2025-02-07 09:45:00'); + +-- Shippings (for each order) +INSERT INTO Shippings (OrderID, Carrier, TrackingNum, DateShipped, DateArrived) +VALUES +(1, 'USPS', 'USPS9400111899223000000001', '2025-02-06', '2025-02-08'), +(2, 'UPS', '1ZSWAP000000000001', '2025-02-06', '2025-02-09'), +(3, 'FedEx','FEDEXTRADE123456789', '2025-02-07', '2025-02-09'); + +-- Feedback (post-swap reviews) +INSERT INTO Feedback + (OrderID, Rating, Comment, CreatedAt) +VALUES +(1, 5, 'Jeans fit perfectly, exactly the vintage vibe I wanted. Would swap again!', '2025-02-09 18:00:00'), +(2, 4, 'Baby tee is so cute and very Y2K. Slightly more cropped than expected but still love it.', '2025-02-10 11:20:00'), + (3, 5, 'Hoodie is insanely soft, looks just like photos. Great communication too.', + '2025-02-10 20:45:00'); From ea25e40cb3e3b506036481dbbf278a82ce28810d Mon Sep 17 00:00:00 2001 From: micahcheng Date: Thu, 4 Dec 2025 23:25:12 -0500 Subject: [PATCH 11/27] Fix MySQL authentication and implement admin endpoints with foreign key handling --- .idea/FitHub.iml | 12 ++++ .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/misc.xml | 3 + .idea/modules.xml | 8 +++ .idea/vcs.xml | 6 ++ Dockerfile | 16 ++++++ api/Dockerfile | 14 ----- api/backend/admin/admin_routes.py | 27 ++++++--- api/backend/db_connection/db.py | 10 ++-- api/backend/rest_entry.py | 16 +++--- app/Dockerfile | 22 ------- docker-compose.yaml | 37 ------------ docker-compose.yml | 57 +++++++++++++++++++ 13 files changed, 143 insertions(+), 91 deletions(-) create mode 100644 .idea/FitHub.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 Dockerfile delete mode 100644 api/Dockerfile delete mode 100644 app/Dockerfile delete mode 100644 docker-compose.yaml create mode 100644 docker-compose.yml diff --git a/.idea/FitHub.iml b/.idea/FitHub.iml new file mode 100644 index 0000000000..8b8c395472 --- /dev/null +++ b/.idea/FitHub.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000000..105ce2da2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index f8269aedb8..16842813a7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,7 @@ + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000..8ca56c2588 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..35eb1ddfbb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..b844699f31 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY api/requirements.txt ./requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +COPY api/ ./api/ + +# Make python treat /app/api as the module root +ENV PYTHONPATH="/app/api" + +EXPOSE 4000 + +CMD ["python", "-u", "api/backend_app.py"] + diff --git a/api/Dockerfile b/api/Dockerfile deleted file mode 100644 index da960bce51..0000000000 --- a/api/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /apicode - -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -EXPOSE 4000 - -# Run Python in unbuffered mode to ensure logs are immediately visible -CMD ["python", "-u", "backend_app.py"] - diff --git a/api/backend/admin/admin_routes.py b/api/backend/admin/admin_routes.py index 2da9579b76..06b7df3213 100644 --- a/api/backend/admin/admin_routes.py +++ b/api/backend/admin/admin_routes.py @@ -142,14 +142,27 @@ def delete_duplicate_items(): +# USER STORY 6 + 1 β€” Delete specific item +# DELETE /admin/items/ # USER STORY 6 + 1 β€” Delete specific item # DELETE /admin/items/ @admin_bp.route('/items/', methods=['DELETE']) def delete_item(item_id): - cursor = db.get_db().cursor() - cursor.execute(""" - DELETE FROM Items WHERE ItemID = %s; - """, (item_id,)) - - db.get_db().commit() - return jsonify({"message": "Item removed"}), 200 + try: + cursor = db.get_db().cursor() + + # Delete in order of dependencies (child tables first) + cursor.execute("DELETE FROM Images WHERE ItemID = %s;", (item_id,)) + cursor.execute("DELETE FROM ItemTags WHERE ItemID = %s;", (item_id,)) + cursor.execute("DELETE FROM OrderItems WHERE ItemID = %s;", (item_id,)) + cursor.execute("DELETE FROM Reports WHERE ReportedItem = %s;", (item_id,)) + cursor.execute("DELETE FROM Items WHERE ItemID = %s;", (item_id,)) + + db.get_db().commit() + return jsonify({"message": "Item removed"}), 200 + + except Exception as e: + db.get_db().rollback() # Roll back on error + return jsonify({"error": str(e)}), 500 + finally: + cursor.close() \ No newline at end of file diff --git a/api/backend/db_connection/db.py b/api/backend/db_connection/db.py index 1c640dce0f..a507da6fe4 100644 --- a/api/backend/db_connection/db.py +++ b/api/backend/db_connection/db.py @@ -9,12 +9,14 @@ def __init__(self): self.db = None def get_db(self): - if self.db is None: + if self.db is None or not self.db.is_connected(): self.db = mysql.connector.connect( host=os.getenv('MYSQL_HOST', 'db'), - user=os.getenv('MYSQL_USER', 'root'), - password=os.getenv('MYSQL_ROOT_PASSWORD'), - database=os.getenv('MYSQL_DATABASE') + user=os.getenv('MYSQL_USER', 'fithub'), + password=os.getenv('MYSQL_PASSWORD', 'fithub'), + database=os.getenv('MYSQL_DATABASE', 'fithub'), + auth_plugin='mysql_native_password', + autocommit=False ) return self.db diff --git a/api/backend/rest_entry.py b/api/backend/rest_entry.py index 518c23f056..cd6a91799f 100644 --- a/api/backend/rest_entry.py +++ b/api/backend/rest_entry.py @@ -8,6 +8,8 @@ from backend.simple.simple_routes import simple_routes from backend.ngos.ngo_routes import ngos from backend.dataAnalysts.dataAnalyst_routes import dataAnalyst +from backend.admin.admin_routes import admin_bp + def create_app(): @@ -18,7 +20,7 @@ def create_app(): # Configure file logging if needed # Uncomment the code in the setup_logging function - # setup_logging(app) + # setup_logging(app) # Load environment variables # This function reads all the values from inside @@ -35,7 +37,7 @@ def create_app(): # # these are for the DB object to be able to connect to MySQL. # app.config['MYSQL_DATABASE_USER'] = 'root' - app.config["MYSQL_DATABASE_USER"] = os.getenv("DB_USER").strip() + app.config["MYSQL_DATABASE_USER"] = os.getenv("MYSQL_USER", "fithub").strip() app.config["MYSQL_DATABASE_PASSWORD"] = os.getenv("MYSQL_ROOT_PASSWORD").strip() app.config["MYSQL_DATABASE_HOST"] = os.getenv("DB_HOST").strip() app.config["MYSQL_DATABASE_PORT"] = int(os.getenv("DB_PORT").strip()) @@ -53,6 +55,7 @@ def create_app(): app.register_blueprint(simple_routes) app.register_blueprint(ngos, url_prefix="/ngo") app.register_blueprint(dataAnalyst, url_prefix="/d") + app.register_blueprint(admin_bp, url_prefix="/a") # Don't forget to return the app object @@ -61,7 +64,7 @@ def create_app(): def setup_logging(app): """ Configure logging for the Flask application in both files and console (Docker Desktop for this project) - + Args: app: Flask application instance to configure logging for """ @@ -77,8 +80,8 @@ def setup_logging(app): # file_handler.setFormatter(logging.Formatter( # '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' # )) - - # Make sure we are capturing all levels of logging into the log files. + + # Make sure we are capturing all levels of logging into the log files. # file_handler.setLevel(logging.DEBUG) # Capture all levels in file # app.logger.addHandler(file_handler) @@ -91,5 +94,4 @@ def setup_logging(app): # console_handler.setLevel(logging.DEBUG) # app.logger.addHandler(console_handler) pass - - \ No newline at end of file + diff --git a/app/Dockerfile b/app/Dockerfile deleted file mode 100644 index d60510b150..0000000000 --- a/app/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /appcode - -RUN apt-get update && apt-get install -y \ - build-essential \ - curl \ - git \ - libfreetype6-dev \ - pkg-config \ - && rm -rf /var/lib/apt/lists/* - -# RUN mkdir /requirements -# COPY ./src/requirements.txt /requirements/requirements.txt - -COPY ./src/requirements.txt . - -RUN pip3 install -r requirements.txt - -EXPOSE 8501 - -CMD ["streamlit", "run", "Home.py", "--server.port=8501", "--server.address=0.0.0.0"] diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index 7dbddc70b9..0000000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: project-app-team-repo - -services: - app: - build: ./app - container_name: web-app - hostname: web-app - volumes: ["./app/src:/appcode"] - environment: - - WATCHPACK_POLLING=true - ports: - - 8501:8501 - - api: - build: ./api - container_name: web-api - hostname: web-api - volumes: ["./api:/apicode"] - environment: - - WATCHPACK_POLLING=true - ports: - - 4000:4000 - - db: - env_file: - - ./api/.env - image: mysql:9 - container_name: mysql_db - hostname: db - volumes: - - "./database-files:/docker-entrypoint-initdb.d/:ro" - - "mysql_data:/var/lib/mysql" - ports: - - 3200:3306 - -volumes: - mysql_data: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..6323d6252f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,57 @@ +version: "3.9" + +services: + db: + image: mysql:8.0 + container_name: fithub-db + hostname: db + restart: always + command: --default-authentication-plugin=mysql_native_password + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: fithub + MYSQL_USER: fithub + MYSQL_PASSWORD: fithub + ports: + - "3310:3306" + volumes: + - ./database-files:/docker-entrypoint-initdb.d/:ro + - mysql_data:/var/lib/mysql + + backend: + build: + context: . + dockerfile: Dockerfile + container_name: fithub-backend + restart: always + environment: + MYSQL_HOST: db + MYSQL_PORT: 3306 + MYSQL_USER: fithub + MYSQL_PASSWORD: fithub + MYSQL_DATABASE: fithub + SECRET_KEY: devsecret + # Add these for compatibility: + DB_USER: fithub + DB_HOST: db + DB_PORT: 3306 + DB_NAME: fithub + DB_PASSWORD: fithub + ports: + - "5001:4000" + depends_on: + - db + + app: + image: node:18 + working_dir: /app + volumes: + - ./app:/app + ports: + - "8600:8501" + command: ["npm", "start"] + environment: + - WATCHPACK_POLLING=true + +volumes: + mysql_data: \ No newline at end of file From c47f27b604ff8d8dc578b7703045d08a7643495b Mon Sep 17 00:00:00 2001 From: micahcheng Date: Fri, 5 Dec 2025 10:48:03 -0500 Subject: [PATCH 12/27] fixed more --- Dockerfile | 5 +++-- api/backend/rest_entry.py | 10 ++++------ docker-compose.yml | 10 +++------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index b844699f31..7a2f478ba6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,11 @@ FROM python:3.11-slim WORKDIR /app +# Copy and install requirements COPY api/requirements.txt ./requirements.txt RUN pip install --no-cache-dir -r requirements.txt +# Copy api folder (will be overridden by volume mount in development) COPY api/ ./api/ # Make python treat /app/api as the module root @@ -12,5 +14,4 @@ ENV PYTHONPATH="/app/api" EXPOSE 4000 -CMD ["python", "-u", "api/backend_app.py"] - +CMD ["python", "-u", "api/backend_app.py"] \ No newline at end of file diff --git a/api/backend/rest_entry.py b/api/backend/rest_entry.py index cd6a91799f..c2f63e2f81 100644 --- a/api/backend/rest_entry.py +++ b/api/backend/rest_entry.py @@ -38,12 +38,10 @@ def create_app(): # # these are for the DB object to be able to connect to MySQL. # app.config['MYSQL_DATABASE_USER'] = 'root' app.config["MYSQL_DATABASE_USER"] = os.getenv("MYSQL_USER", "fithub").strip() - app.config["MYSQL_DATABASE_PASSWORD"] = os.getenv("MYSQL_ROOT_PASSWORD").strip() - app.config["MYSQL_DATABASE_HOST"] = os.getenv("DB_HOST").strip() - app.config["MYSQL_DATABASE_PORT"] = int(os.getenv("DB_PORT").strip()) - app.config["MYSQL_DATABASE_DB"] = os.getenv( - "DB_NAME" - ).strip() # Change this to your DB name + app.config["MYSQL_DATABASE_PASSWORD"] = os.getenv("MYSQL_PASSWORD", "fithub").strip() + app.config["MYSQL_DATABASE_HOST"] = os.getenv("MYSQL_HOST", "db").strip() + app.config["MYSQL_DATABASE_PORT"] = int(os.getenv("MYSQL_PORT", "3306").strip()) + app.config["MYSQL_DATABASE_DB"] = os.getenv("MYSQL_DATABASE", "fithub").strip() # Initialize the database object with the settings above. app.logger.info("current_app(): starting the database connection") diff --git a/docker-compose.yml b/docker-compose.yml index 6323d6252f..c2de0ede58 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,6 +24,8 @@ services: dockerfile: Dockerfile container_name: fithub-backend restart: always + volumes: + - ./api:/app/api environment: MYSQL_HOST: db MYSQL_PORT: 3306 @@ -31,14 +33,8 @@ services: MYSQL_PASSWORD: fithub MYSQL_DATABASE: fithub SECRET_KEY: devsecret - # Add these for compatibility: - DB_USER: fithub - DB_HOST: db - DB_PORT: 3306 - DB_NAME: fithub - DB_PASSWORD: fithub ports: - - "5001:4000" + - "4000:4000" # Changed from 5001:4000 depends_on: - db From 05b088aff36690b2ae521d7a6b657d7694cc5016 Mon Sep 17 00:00:00 2001 From: micahcheng Date: Fri, 5 Dec 2025 10:55:37 -0500 Subject: [PATCH 13/27] deleted some stuff --- Dockerfile | 7 ++----- docker-compose.yml | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7a2f478ba6..91db6cf041 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,12 @@ FROM python:3.11-slim WORKDIR /app - -# Copy and install requirements +s COPY api/requirements.txt ./requirements.txt RUN pip install --no-cache-dir -r requirements.txt -# Copy api folder (will be overridden by volume mount in development) -COPY api/ ./api/ -# Make python treat /app/api as the module root +COPY api/ ./api/ ENV PYTHONPATH="/app/api" EXPOSE 4000 diff --git a/docker-compose.yml b/docker-compose.yml index c2de0ede58..d9c01c73e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,7 +34,7 @@ services: MYSQL_DATABASE: fithub SECRET_KEY: devsecret ports: - - "4000:4000" # Changed from 5001:4000 + - "4000:4000" depends_on: - db From 11086629ebb068506afdbcb5df31d86f5c6bf574 Mon Sep 17 00:00:00 2001 From: zuha <110277947+zuhazana1@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:31:24 -0500 Subject: [PATCH 14/27] first three quiries --- api/backend/swapper/{initial => _init_.py} | 0 api/backend/swapper/swapper_routes.py | 45 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) rename api/backend/swapper/{initial => _init_.py} (100%) create mode 100644 api/backend/swapper/swapper_routes.py diff --git a/api/backend/swapper/initial b/api/backend/swapper/_init_.py similarity index 100% rename from api/backend/swapper/initial rename to api/backend/swapper/_init_.py diff --git a/api/backend/swapper/swapper_routes.py b/api/backend/swapper/swapper_routes.py new file mode 100644 index 0000000000..f678b8d32d --- /dev/null +++ b/api/backend/swapper/swapper_routes.py @@ -0,0 +1,45 @@ +######## +# swapper endpoints +######## + + +from flask import Blueprint, request, jsonify, make_response, current_app +from backend.db_connection import db +swapper = Blueprint('swappers', __name__) + + +#-----User Story 1------ +#As a Senior Data Analyst, I want to view available listings with +# their posting dates, so I can monitor listing performance. + +@swapper.route('/listings////', methods=['GET']) +def get_all_listings(): + cursor = db.get_db().cursor() + + the_query = ''' + SET @SizeFilter = 'M'; + SET @ConditionFilter = 'Good'; + SET @TagFilter = 'Vintage'; + + + SELECT DISTINCT i.ItemID, i.Title, i.Category, i.Description, i.Size, i.`Condition`, i.`Type`, u.Name AS OwnerName + FROM Items i + INNER JOIN Users u ON i.OwnerID = u.UserID + LEFT JOIN ItemTags it ON i.ItemID = it.ItemID + LEFT JOIN Tags t ON it.TagID = t.TagID + WHERE i.IsAvailable = 1 + AND i.`Type` = 'Swap' + AND i.Size = @SizeFilter + AND i.`Condition` = @ConditionFilter + AND t.Title = @TagFilter; + + ''' + + cursor.execute(the_query) + theData = cursor.fetchall() + + the_response = make_response(jsonify(theData)) + the_response.status_code = 200 + the_response.mimetype = 'application/json' + return the_response +#-----User Story 1------ From f70e249a195d36719fcd7351ed5f7063924107fc Mon Sep 17 00:00:00 2001 From: micahcheng Date: Fri, 5 Dec 2025 23:31:52 -0500 Subject: [PATCH 15/27] made code more consistent --- Dockerfile | 6 +++--- api/Dockerfile | 4 ++++ app/Dockerfile | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 api/Dockerfile create mode 100644 app/Dockerfile diff --git a/Dockerfile b/Dockerfile index 91db6cf041..7d2ce37423 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ FROM python:3.11-slim WORKDIR /app -s -COPY api/requirements.txt ./requirements.txt + +COPY ../api/requirements.txt ./requirements.txt RUN pip install --no-cache-dir -r requirements.txt -COPY api/ ./api/ +COPY ../api ./api/ ENV PYTHONPATH="/app/api" EXPOSE 4000 diff --git a/api/Dockerfile b/api/Dockerfile new file mode 100644 index 0000000000..77b0adc230 --- /dev/null +++ b/api/Dockerfile @@ -0,0 +1,4 @@ +FROM ubuntu:latest +LABEL authors="micahcheng" + +ENTRYPOINT ["top", "-b"] \ No newline at end of file diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100644 index 0000000000..7db7ff1f07 --- /dev/null +++ b/app/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY api/requirements.txt ./requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + + +COPY api/ ./api/ +ENV PYTHONPATH="/app/api" + +EXPOSE 4000 + +CMD ["python", "-u", "api/backend_app.py"] \ No newline at end of file From aa7d4e244a294a3474a33a3f5e93e1519fb74899 Mon Sep 17 00:00:00 2001 From: micahcheng Date: Fri, 5 Dec 2025 23:35:16 -0500 Subject: [PATCH 16/27] consistency is key --- Dockerfile | 6 +-- api/Dockerfile | 14 ++++-- api/backend/admin/admin_routes.py | 72 +++++++++++++++---------------- api/backend/db_connection/db.py | 13 +++--- app/Dockerfile | 20 ++++++--- docker-compose.yml | 60 ++++++++------------------ 6 files changed, 88 insertions(+), 97 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7d2ce37423..de2037346c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,13 +2,13 @@ FROM python:3.11-slim WORKDIR /app -COPY ../api/requirements.txt ./requirements.txt +COPY api/requirements.txt ./requirements.txt RUN pip install --no-cache-dir -r requirements.txt +COPY api/ ./api/ -COPY ../api ./api/ ENV PYTHONPATH="/app/api" EXPOSE 4000 -CMD ["python", "-u", "api/backend_app.py"] \ No newline at end of file +CMD ["python", "-u", "api/backend_app.py"] diff --git a/api/Dockerfile b/api/Dockerfile index 77b0adc230..ebaffe1d77 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,4 +1,12 @@ -FROM ubuntu:latest -LABEL authors="micahcheng" +FROM python:3.11-slim -ENTRYPOINT ["top", "-b"] \ No newline at end of file +WORKDIR /apicode + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 4000 + +CMD ["python", "-u", "backend_app.py"] diff --git a/api/backend/admin/admin_routes.py b/api/backend/admin/admin_routes.py index 06b7df3213..a16f5b109f 100644 --- a/api/backend/admin/admin_routes.py +++ b/api/backend/admin/admin_routes.py @@ -3,10 +3,7 @@ admin_bp = Blueprint('admin', __name__) - # USER STORY 1 β€” View unresolved reports -# GET /admin/reports/pending - @admin_bp.route('/reports/pending', methods=['GET']) def get_pending_reports(): cursor = db.get_db().cursor() @@ -16,13 +13,13 @@ def get_pending_reports(): WHERE Resolved = 0; """) data = cursor.fetchall() - return jsonify(data), 200 + the_response = make_response(jsonify(data)) + the_response.status_code = 200 + return the_response # USER STORY 1 β€” Resolve a report -# PUT /admin/reports//resolve - @admin_bp.route('/reports//resolve', methods=['PUT']) def resolve_report(report_id): cursor = db.get_db().cursor() @@ -31,18 +28,19 @@ def resolve_report(report_id): SET Resolved = 1, ResolvedAt = NOW() WHERE ReportID = %s; """, (report_id,)) - + db.get_db().commit() - return jsonify({"message": "Report resolved"}), 200 + the_response = make_response(jsonify({"message": "Report resolved"})) + the_response.status_code = 200 + return the_response -# USER STORY 2 β€” Update user role -# PUT /admin/users//role +# USER STORY 2 β€” Update user role @admin_bp.route('/users//role', methods=['PUT']) def update_user_role(user_id): new_role = request.json.get("role") - + cursor = db.get_db().cursor() cursor.execute(""" UPDATE Users @@ -51,13 +49,13 @@ def update_user_role(user_id): """, (new_role, user_id)) db.get_db().commit() - return jsonify({"message": "User role updated"}), 200 + the_response = make_response(jsonify({"message": "User role updated"})) + the_response.status_code = 200 + return the_response # USER STORY 3 β€” Deactivate user -# PUT /admin/users//deactivate - @admin_bp.route('/users//deactivate', methods=['PUT']) def deactivate_user(user_id): cursor = db.get_db().cursor() @@ -68,13 +66,13 @@ def deactivate_user(user_id): """, (user_id,)) db.get_db().commit() - return jsonify({"message": "User deactivated"}), 200 + the_response = make_response(jsonify({"message": "User deactivated"})) + the_response.status_code = 200 + return the_response # USER STORY 4 β€” Create announcement -# POST /admin/announcements - @admin_bp.route('/announcements', methods=['POST']) def create_announcement(): data = request.json @@ -97,13 +95,12 @@ def create_announcement(): db.get_db().commit() - return jsonify({"message": "Announcement created"}), 201 - + the_response = make_response(jsonify({"message": "Announcement created"})) + the_response.status_code = 201 + return the_response -# USER STORY 5 β€” Analytics metrics -# GET /admin/analytics/summary - +# USER STORY 5 β€” Analytics summary @admin_bp.route('/analytics/summary', methods=['GET']) def analytics_summary(): cursor = db.get_db().cursor() @@ -114,17 +111,16 @@ def analytics_summary(): (SELECT COUNT(*) FROM Reports WHERE Resolved = 0) AS OpenReports; """) data = cursor.fetchall() - return jsonify(data), 200 - + the_response = make_response(jsonify(data)) + the_response.status_code = 200 + return the_response -# USER STORY 6 β€” Delete duplicate listings -# DELETE /admin/items/duplicates +# USER STORY 6 β€” Delete duplicate items @admin_bp.route('/items/duplicates', methods=['DELETE']) def delete_duplicate_items(): cursor = db.get_db().cursor() - cursor.execute(""" DELETE i FROM Items i JOIN ( @@ -138,20 +134,18 @@ def delete_duplicate_items(): """) db.get_db().commit() - return jsonify({"message": "Duplicate items removed"}), 200 + the_response = make_response(jsonify({"message": "Duplicate items removed"})) + the_response.status_code = 200 + return the_response # USER STORY 6 + 1 β€” Delete specific item -# DELETE /admin/items/ -# USER STORY 6 + 1 β€” Delete specific item -# DELETE /admin/items/ @admin_bp.route('/items/', methods=['DELETE']) def delete_item(item_id): try: cursor = db.get_db().cursor() - # Delete in order of dependencies (child tables first) cursor.execute("DELETE FROM Images WHERE ItemID = %s;", (item_id,)) cursor.execute("DELETE FROM ItemTags WHERE ItemID = %s;", (item_id,)) cursor.execute("DELETE FROM OrderItems WHERE ItemID = %s;", (item_id,)) @@ -159,10 +153,14 @@ def delete_item(item_id): cursor.execute("DELETE FROM Items WHERE ItemID = %s;", (item_id,)) db.get_db().commit() - return jsonify({"message": "Item removed"}), 200 + + the_response = make_response(jsonify({"message": "Item removed"})) + the_response.status_code = 200 + return the_response except Exception as e: - db.get_db().rollback() # Roll back on error - return jsonify({"error": str(e)}), 500 - finally: - cursor.close() \ No newline at end of file + db.get_db().rollback() + + the_response = make_response(jsonify({"error": str(e)})) + the_response.status_code = 500 + return the_response diff --git a/api/backend/db_connection/db.py b/api/backend/db_connection/db.py index a507da6fe4..de2223ecce 100644 --- a/api/backend/db_connection/db.py +++ b/api/backend/db_connection/db.py @@ -1,5 +1,5 @@ -import mysql.connector import os +import mysql.connector from dotenv import load_dotenv load_dotenv() @@ -11,13 +11,14 @@ def __init__(self): def get_db(self): if self.db is None or not self.db.is_connected(): self.db = mysql.connector.connect( - host=os.getenv('MYSQL_HOST', 'db'), - user=os.getenv('MYSQL_USER', 'fithub'), - password=os.getenv('MYSQL_PASSWORD', 'fithub'), - database=os.getenv('MYSQL_DATABASE', 'fithub'), + host=os.getenv("DB_HOST", "db"), + port=int(os.getenv("DB_PORT", 3306)), + user=os.getenv("DB_USER", "root"), + password=os.getenv("DB_PASSWORD", "1234"), + database=os.getenv("DB_NAME", "fithub"), auth_plugin='mysql_native_password', autocommit=False ) return self.db -db = DB() \ No newline at end of file +db = DB() diff --git a/app/Dockerfile b/app/Dockerfile index 7db7ff1f07..3a9142fabb 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -1,14 +1,20 @@ FROM python:3.11-slim -WORKDIR /app +WORKDIR /appcode -COPY api/requirements.txt ./requirements.txt -RUN pip install --no-cache-dir -r requirements.txt +RUN apt-get update && apt-get install -y \ + build-essential \ + curl \ + git \ + libfreetype6-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* +COPY ./src/requirements.txt . +RUN pip3 install -r requirements.txt -COPY api/ ./api/ -ENV PYTHONPATH="/app/api" +COPY ./src ./src -EXPOSE 4000 +EXPOSE 8501 -CMD ["python", "-u", "api/backend_app.py"] \ No newline at end of file +CMD ["streamlit", "run", "src/Home.py", "--server.port=8501", "--server.address=0.0.0.0"] diff --git a/docker-compose.yml b/docker-compose.yml index d9c01c73e0..6443e06d90 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,53 +1,31 @@ -version: "3.9" - services: + app: + build: ./app + container_name: fithub-app + volumes: + - ./app/src:/appcode/src + ports: + - "8501:8501" + + api: + build: ./api + container_name: fithub-api + ports: + - "4000:4000" + volumes: + - ./api:/apicode + db: image: mysql:8.0 container_name: fithub-db - hostname: db - restart: always - command: --default-authentication-plugin=mysql_native_password environment: - MYSQL_ROOT_PASSWORD: root + MYSQL_ROOT_PASSWORD: 1234 MYSQL_DATABASE: fithub - MYSQL_USER: fithub - MYSQL_PASSWORD: fithub - ports: - - "3310:3306" volumes: - ./database-files:/docker-entrypoint-initdb.d/:ro - mysql_data:/var/lib/mysql - - backend: - build: - context: . - dockerfile: Dockerfile - container_name: fithub-backend - restart: always - volumes: - - ./api:/app/api - environment: - MYSQL_HOST: db - MYSQL_PORT: 3306 - MYSQL_USER: fithub - MYSQL_PASSWORD: fithub - MYSQL_DATABASE: fithub - SECRET_KEY: devsecret - ports: - - "4000:4000" - depends_on: - - db - - app: - image: node:18 - working_dir: /app - volumes: - - ./app:/app ports: - - "8600:8501" - command: ["npm", "start"] - environment: - - WATCHPACK_POLLING=true + - "3200:3306" volumes: - mysql_data: \ No newline at end of file + mysql_data: From 19a9e68bfbaad75645c9d31ffec4fb811c72d3af Mon Sep 17 00:00:00 2001 From: micahcheng Date: Sat, 6 Dec 2025 03:15:39 -0500 Subject: [PATCH 17/27] added streamlit+nav+home connections? --- app/src/Home.py | 4 +- app/src/assets/fitlogo.png | Bin 0 -> 10009 bytes app/src/modules/nav.py | 56 ++++++++++++- app/src/pages/05_Admin_Home.py | 34 ++++++++ app/src/pages/10_Reports_Management.py | 55 +++++++++++++ app/src/pages/12_API_Test.py | 106 ++++++++++++++++++++++++- app/src/pages/20_Admin_User_Tools.py | 83 +++++++++++++++++++ app/src/pages/30_Item_Cleanup.py | 39 +++++++++ 8 files changed, 372 insertions(+), 5 deletions(-) create mode 100644 app/src/assets/fitlogo.png create mode 100644 app/src/pages/05_Admin_Home.py create mode 100644 app/src/pages/10_Reports_Management.py create mode 100644 app/src/pages/20_Admin_User_Tools.py create mode 100644 app/src/pages/30_Item_Cleanup.py diff --git a/app/src/Home.py b/app/src/Home.py index abe97588aa..c0fe35aa19 100644 --- a/app/src/Home.py +++ b/app/src/Home.py @@ -71,9 +71,9 @@ type = 'primary', use_container_width=True): st.session_state['authenticated'] = True - st.session_state['role'] = 'administrator' + st.session_state['role'] = 'admin' st.session_state['first_name'] = 'SysAdmin' - st.switch_page('pages/20_Admin_Home.py') + st.switch_page('pages/05_Admin_Home.py') diff --git a/app/src/assets/fitlogo.png b/app/src/assets/fitlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..8191e839ee4501511b7a70d9b43ecbf8ec1e760a GIT binary patch literal 10009 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 literal 0 HcmV?d00001 diff --git a/app/src/modules/nav.py b/app/src/modules/nav.py index 9b674e3d89..df95f181cc 100644 --- a/app/src/modules/nav.py +++ b/app/src/modules/nav.py @@ -12,7 +12,7 @@ def HomeNav(): def AboutPageNav(): st.sidebar.page_link("pages/30_About.py", label="About", icon="🧠") - +''' #### ------------------------ Examples for Role of pol_strat_advisor ------------------------ def PolStratAdvHomeNav(): @@ -69,6 +69,9 @@ def AdminPageNav(): ) + + + # --------------------------------Links Function ----------------------------------------------- def SideBarLinks(show_home=False): """ @@ -76,7 +79,7 @@ def SideBarLinks(show_home=False): """ # add a logo to the sidebar always - st.sidebar.image("assets/logo.png", width=150) + st.sidebar.image("assets/fitlogo.png", width=150) # If there is no logged in user, redirect to the Home (Landing) page if "authenticated" not in st.session_state: @@ -119,3 +122,52 @@ def SideBarLinks(show_home=False): del st.session_state["role"] del st.session_state["authenticated"] st.switch_page("Home.py") + +''' +#Admin Nav Micah +def ReportsManagementNav(): + st.sidebar.page_link("pages/10_Reports_Management.py", label="Reports Management") +def AdminUserToolsNav(): + st.sidebar.page_link("pages/20_Admin_User_Tools.py", label="Admin User Tools" ) +def ItemCleanupNav(): + st.sidebar.page_link("pages/30_Item_Cleanup.py", label="Item Cleanup" ) +def AdminHomeNav(): + st.sidebar.page_link("pages/05_Admin_Home.py", label= "Admin Home") +# --------------------------------Links Function ----------------------------------------------- +def SideBarLinks(show_home=False): + """ + This function handles adding links to the sidebar of the app based upon the logged-in user's role, which was put in the streamlit session_state object when logging in. + """ + + # add a logo to the sidebar always + st.sidebar.image("assets/fitlogo.png", width=150) + + # If there is no logged in user, redirect to the Home (Landing) page + if "authenticated" not in st.session_state: + st.session_state.authenticated = False + st.switch_page("Home.py") + + if show_home: + # Show the Home page link (the landing page) + HomeNav() + + # Show the other page navigators depending on the users' role. + if st.session_state["authenticated"]: + + + # If the user is an administrator, give them access to the administrator pages + if st.session_state["role"] == "admin": + AdminHomeNav() + AdminUserToolsNav() + ItemCleanupNav() + ReportsManagementNav() + + # Always show the About page at the bottom of the list of links + AboutPageNav() + + if st.session_state["authenticated"]: + # Always show a logout button if there is a logged in user + if st.sidebar.button("Logout"): + del st.session_state["role"] + del st.session_state["authenticated"] + st.switch_page("Home.py") \ No newline at end of file diff --git a/app/src/pages/05_Admin_Home.py b/app/src/pages/05_Admin_Home.py new file mode 100644 index 0000000000..e23e8a06c8 --- /dev/null +++ b/app/src/pages/05_Admin_Home.py @@ -0,0 +1,34 @@ +import logging +logger = logging.getLogger(__name__) + +import streamlit as st +from streamlit_extras.app_logo import add_logo +from modules.nav import SideBarLinks + +# Load sidebar navigation (includes admin links if authenticated) +SideBarLinks() + +# Add logo to sidebar +add_logo("public/FitHublogo.png") + +# Log admin dashboard access +logger.info(f"Admin Dashboard loaded by {st.session_state.get('first_name', 'Unknown')}") + +# Title +st.title("Admin Dashboard") + +st.markdown("### ") + +if st.button("Manage Reports", use_container_width=True): + logger.info("Navigating to Reports Management") + st.switch_page("pages/21_Admin_Manage_Reports.py") + +if st.button("User Roles & Access Tools", use_container_width=True): + logger.info("Navigating to User Tools") + st.switch_page("pages/22_Admin_User_Tools.py") + +if st.button("Item Cleanup Tools", use_container_width=True): + logger.info("Navigating to Item Cleanup Tools") + st.switch_page("pages/23_Admin_Item_Cleanup.py") + +st.markdown("

DistinctDevs Β©2025
", unsafe_allow_html=True) diff --git a/app/src/pages/10_Reports_Management.py b/app/src/pages/10_Reports_Management.py new file mode 100644 index 0000000000..7250e1c73f --- /dev/null +++ b/app/src/pages/10_Reports_Management.py @@ -0,0 +1,55 @@ +import logging +logger = logging.getLogger(__name__) + +import streamlit as st +import requests +from streamlit_extras.app_logo import add_logo +from modules.nav import SideBarLinks + +SideBarLinks() + +st.title("Reports Management") + +API_BASE = "http://api:4000/a" + +# =============================== +# USER STORY 1 β€” VIEW PENDING REPORTS +# =============================== +st.write("## View Pending Reports") + +try: + response = requests.get(f"{API_BASE}/reports/pending") + if response.status_code == 200: + reports = response.json() + + st.write(f"Found **{len(reports)} pending reports**") + + # Display each report inside an expander + for report in reports: + with st.expander(f"Report #{report['ReportID']} β€” Severity {report['Severity']}"): + st.write(f"**Note:** {report['Note']}") + st.write(f"**Reported Item:** {report['ReportedItem']}") + + else: + st.error("Failed to fetch reports from API.") + +except requests.exceptions.RequestException: + st.error("Could not connect to API server.") + st.info("Make sure the API is running.") + +# =============================== +# USER STORY 1 β€” RESOLVE REPORT +# =============================== +st.write("## Resolve a Report") + +col1, col2 = st.columns(2) +with col1: + report_id = st.number_input("Enter Report ID", min_value=1, step=1) + +with col2: + if st.button("Resolve Report"): + try: + resp = requests.put(f"{API_BASE}/reports/{report_id}/resolve") + st.write(resp.json()) + except: + st.error("Could not resolve the report.") diff --git a/app/src/pages/12_API_Test.py b/app/src/pages/12_API_Test.py index cbf17b4cda..f0330105fd 100644 --- a/app/src/pages/12_API_Test.py +++ b/app/src/pages/12_API_Test.py @@ -9,11 +9,13 @@ st.write("# Accessing a REST API from Within Streamlit") """ + + Simply retrieving data from a REST api running in a separate Docker Container. If the container isn't running, this will be very unhappy. But the Streamlit app should not totally die. -""" + data = {} try: @@ -23,3 +25,105 @@ data = {"a":{"b": "123", "c": "hello"}, "z": {"b": "456", "c": "goodbye"}} st.dataframe(data) +""" + + +# ============================ +# USER STORY 1 β€” View Pending Reports +# ============================ +st.write("### View Pending Reports") +get_pending_reports = requests.get("http://api:4000/a/reports/pending") +try: + st.write(get_pending_reports.json()) +except: + st.write("Could not connect to database to get pending reports.") + + + +# ============================ +# USER STORY 1 β€” Resolve Report +# ============================ +st.write("### Resolve Report (example: report_id = 1)") +resolve_report = requests.put("http://api:4000/a/reports/1/resolve") +try: + st.write(resolve_report.json()) +except: + st.write("Could not resolve report.") + + + +# ============================ +# USER STORY 2 β€” Update User Role +# ============================ +st.write("### Update User Role (example: user_id = 1)") +update_user_role = requests.put( + "http://api:4000/a/users/1/role", + json={"role": "admin"} +) +try: + st.write(update_user_role.json()) +except: + st.write("Could not update user role.") + + + +# ============================ +# USER STORY 3 β€” Deactivate User +# ============================ +st.write("### Deactivate User (example: user_id = 1)") +deactivate_user = requests.put("http://api:4000/a/users/1/deactivate") +try: + st.write(deactivate_user.json()) +except: + st.write("Could not deactivate user.") + + + +# ============================ +# USER STORY 4 β€” Create Announcement +# ============================ +st.write("### Create Announcement") +create_announcement = requests.post( + "http://api:4000/a/announcements", + json={"announcer_id": 1, "message": "Test announcement"} +) +try: + st.write(create_announcement.json()) +except: + st.write("Could not create announcement.") + + + +# ============================ +# USER STORY 5 β€” Analytics Summary +# ============================ +st.write("### Analytics Summary") +analytics_summary = requests.get("http://api:4000/a/analytics/summary") +try: + st.write(analytics_summary.json()) +except: + st.write("Could not load analytics summary.") + + + +# ============================ +# USER STORY 6 β€” Delete Duplicate Items +# ============================ +st.write("### Delete Duplicate Items") +delete_duplicates = requests.delete("http://api:4000/a/items/duplicates") +try: + st.write(delete_duplicates.json()) +except: + st.write("Could not delete duplicate items.") + + + +# ============================ +# USER STORY 6+1 β€” Delete Specific Item +# ============================ +st.write("### Delete Specific Item (example: item_id = 1)") +delete_item = requests.delete("http://api:4000/a/items/1") +try: + st.write(delete_item.json()) +except: + st.write("Could not delete item.") diff --git a/app/src/pages/20_Admin_User_Tools.py b/app/src/pages/20_Admin_User_Tools.py new file mode 100644 index 0000000000..c4ddc77f62 --- /dev/null +++ b/app/src/pages/20_Admin_User_Tools.py @@ -0,0 +1,83 @@ +import logging +logger = logging.getLogger(__name__) +import streamlit as st +import requests +from streamlit_extras.app_logo import add_logo +from modules.nav import SideBarLinks + +SideBarLinks() + +API_BASE = "http://api:4000/a" + +st.title("User Management") + +col_left, col_right = st.columns([1, 1]) + +with col_left: + + st.subheader("Update User Role") + + user_id_role = st.text_input("β€œEnter ID”", key="role_user_id") + + st.write("") # spacing + + # Role buttons (Figma style) + role_selected = st.radio( + "Select Role", + ["Admin", "User", "Analyst"], + label_visibility="collapsed" + ) + + # Convert Figma labels β†’ backend values + role_map = { + "Admin": "admin", + "User": "user", + "Analyst": "worker" # your DB role + } + + chosen_role = role_map[role_selected] + + st.write("") # spacing + + if st.button("Update Roles", use_container_width=True): + if not user_id_role: + st.error("Please enter a User ID.") + else: + try: + payload = {"role": chosen_role} + response = requests.put( + f"{API_BASE}/users/{user_id_role}/role", + json=payload + ) + if response.status_code == 200: + st.success("User role updated successfully!") + else: + st.error(f"Failed to update role: {response.text}") + + except requests.exceptions.RequestException as e: + st.error(f"Error connecting to API: {e}") + + +with col_right: + + st.subheader("") + + user_id_deactivate = st.text_input("β€œEnter ID”", key="deactivate_user_id") + + st.write("") + + if st.button("Deactivate User", use_container_width=True): + if not user_id_deactivate: + st.error("Please enter a User ID.") + else: + try: + response = requests.put( + f"{API_BASE}/users/{user_id_deactivate}/deactivate" + ) + if response.status_code == 200: + st.success("User deactivated successfully!") + else: + st.error(f"Failed to deactivate user: {response.text}") + + except requests.exceptions.RequestException as e: + st.error(f"Error connecting to API: {e}") diff --git a/app/src/pages/30_Item_Cleanup.py b/app/src/pages/30_Item_Cleanup.py new file mode 100644 index 0000000000..4b264aa2ee --- /dev/null +++ b/app/src/pages/30_Item_Cleanup.py @@ -0,0 +1,39 @@ +import logging +logger = logging.getLogger(__name__) + +import streamlit as st +import requests +from streamlit_extras.app_logo import add_logo +from modules.nav import SideBarLinks + +SideBarLinks() + +st.title("Item Cleanup Tools") + +API_BASE = "http://api:4000/a" + +# =============================== +# USER STORY 6 β€” DELETE DUPLICATES +# =============================== +st.write("## Remove Duplicate Items") + +if st.button("Delete Duplicate Items"): + try: + resp = requests.delete(f"{API_BASE}/items/duplicates") + st.write(resp.json()) + except: + st.error("Error deleting duplicate items.") + +# =============================== +# USER STORY 6+1 β€” DELETE SPECIFIC ITEM +# =============================== +st.write("## Delete Specific Item") + +item_id = st.number_input("Enter Item ID to Delete", min_value=1, step=1) + +if st.button("Delete Item"): + try: + resp = requests.delete(f"{API_BASE}/items/{item_id}") + st.write(resp.json()) + except: + st.error("Error deleting item.") From 1bfa1cd3fd972b5c9395d0a3cd7d1b39e2242b42 Mon Sep 17 00:00:00 2001 From: micahcheng Date: Sat, 6 Dec 2025 15:34:27 -0500 Subject: [PATCH 18/27] apis --- app/src/modules/nav.py | 152 ++++++----------------------------- app/src/pages/12_API_Test.py | 81 ++++++++----------- 2 files changed, 57 insertions(+), 176 deletions(-) diff --git a/app/src/modules/nav.py b/app/src/modules/nav.py index df95f181cc..e84734d94f 100644 --- a/app/src/modules/nav.py +++ b/app/src/modules/nav.py @@ -1,173 +1,71 @@ -# Idea borrowed from https://github.com/fsmosca/sample-streamlit-authenticator - -# This file has function to add certain functionality to the left side bar of the app +# Navigation functions for Streamlit sidebar import streamlit as st -#### ------------------------ General ------------------------ +# ------------------------ General Navigation ------------------------ + def HomeNav(): - st.sidebar.page_link("Home.py", label="Home", icon="🏠") + st.sidebar.page_link("Home.py", label="Home") def AboutPageNav(): - st.sidebar.page_link("pages/30_About.py", label="About", icon="🧠") -''' - -#### ------------------------ Examples for Role of pol_strat_advisor ------------------------ -def PolStratAdvHomeNav(): - st.sidebar.page_link( - "pages/00_Pol_Strat_Home.py", label="Political Strategist Home", icon="πŸ‘€" - ) - - -def WorldBankVizNav(): - st.sidebar.page_link( - "pages/01_World_Bank_Viz.py", label="World Bank Visualization", icon="🏦" - ) - - -def MapDemoNav(): - st.sidebar.page_link("pages/02_Map_Demo.py", label="Map Demonstration", icon="πŸ—ΊοΈ") - - -## ------------------------ Examples for Role of usaid_worker ------------------------ - -def usaidWorkerHomeNav(): - st.sidebar.page_link( - "pages/10_USAID_Worker_Home.py", label="USAID Worker Home", icon="🏠" - ) - -def NgoDirectoryNav(): - st.sidebar.page_link("pages/14_NGO_Directory.py", label="NGO Directory", icon="πŸ“") - -def AddNgoNav(): - st.sidebar.page_link("pages/15_Add_NGO.py", label="Add New NGO", icon="βž•") - -def ApiTestNav(): - st.sidebar.page_link("pages/12_API_Test.py", label="Test the API", icon="πŸ›œ") - -def PredictionNav(): - st.sidebar.page_link( - "pages/11_Prediction.py", label="Regression Prediction", icon="πŸ“ˆ" - ) - -def ClassificationNav(): - st.sidebar.page_link( - "pages/13_Classification.py", label="Classification Demo", icon="🌺" - ) - - - - - -#### ------------------------ System Admin Role ------------------------ -def AdminPageNav(): - st.sidebar.page_link("pages/20_Admin_Home.py", label="System Admin", icon="πŸ–₯️") - st.sidebar.page_link( - "pages/21_ML_Model_Mgmt.py", label="ML Model Management", icon="🏒" - ) + st.sidebar.page_link("pages/30_About.py", label="About") +# ------------------------ Admin Navigation ------------------------ +def AdminHomeNav(): + st.sidebar.page_link("pages/05_Admin_Home.py", label="Admin Home") -# --------------------------------Links Function ----------------------------------------------- -def SideBarLinks(show_home=False): - """ - This function handles adding links to the sidebar of the app based upon the logged-in user's role, which was put in the streamlit session_state object when logging in. - """ +def ReportsManagementNav(): + st.sidebar.page_link("pages/10_Reports_Management.py", label="Reports Management") - # add a logo to the sidebar always - st.sidebar.image("assets/fitlogo.png", width=150) - # If there is no logged in user, redirect to the Home (Landing) page - if "authenticated" not in st.session_state: - st.session_state.authenticated = False - st.switch_page("Home.py") +def AdminUserToolsNav(): + st.sidebar.page_link("pages/20_Admin_User_Tools.py", label="Admin User Tools") - if show_home: - # Show the Home page link (the landing page) - HomeNav() - # Show the other page navigators depending on the users' role. - if st.session_state["authenticated"]: +def ItemCleanupNav(): + st.sidebar.page_link("pages/30_Item_Cleanup.py", label="Item Cleanup") - # Show World Bank Link and Map Demo Link if the user is a political strategy advisor role. - if st.session_state["role"] == "pol_strat_advisor": - PolStratAdvHomeNav() - WorldBankVizNav() - MapDemoNav() - - # If the user role is usaid worker, show the Api Testing page - if st.session_state["role"] == "usaid_worker": - usaidWorkerHomeNav() - NgoDirectoryNav() - AddNgoNav() - PredictionNav() - ApiTestNav() - ClassificationNav() - - - # If the user is an administrator, give them access to the administrator pages - if st.session_state["role"] == "administrator": - AdminPageNav() - - # Always show the About page at the bottom of the list of links - AboutPageNav() - if st.session_state["authenticated"]: - # Always show a logout button if there is a logged in user - if st.sidebar.button("Logout"): - del st.session_state["role"] - del st.session_state["authenticated"] - st.switch_page("Home.py") +# ------------------------ Sidebar Link Controller ------------------------ -''' -#Admin Nav Micah -def ReportsManagementNav(): - st.sidebar.page_link("pages/10_Reports_Management.py", label="Reports Management") -def AdminUserToolsNav(): - st.sidebar.page_link("pages/20_Admin_User_Tools.py", label="Admin User Tools" ) -def ItemCleanupNav(): - st.sidebar.page_link("pages/30_Item_Cleanup.py", label="Item Cleanup" ) -def AdminHomeNav(): - st.sidebar.page_link("pages/05_Admin_Home.py", label= "Admin Home") -# --------------------------------Links Function ----------------------------------------------- def SideBarLinks(show_home=False): """ - This function handles adding links to the sidebar of the app based upon the logged-in user's role, which was put in the streamlit session_state object when logging in. + Adds sidebar navigation links depending on authentication + and the user's role stored in session_state. """ - # add a logo to the sidebar always + # Always show the logo st.sidebar.image("assets/fitlogo.png", width=150) - # If there is no logged in user, redirect to the Home (Landing) page + # If no user authentication flag exists, redirect to home if "authenticated" not in st.session_state: st.session_state.authenticated = False st.switch_page("Home.py") + # Optional: show home link if show_home: - # Show the Home page link (the landing page) HomeNav() - # Show the other page navigators depending on the users' role. + # Show links based on role if st.session_state["authenticated"]: - - # If the user is an administrator, give them access to the administrator pages if st.session_state["role"] == "admin": AdminHomeNav() + ReportsManagementNav() AdminUserToolsNav() ItemCleanupNav() - ReportsManagementNav() - # Always show the About page at the bottom of the list of links + # Always show About page AboutPageNav() + # Logout if st.session_state["authenticated"]: - # Always show a logout button if there is a logged in user if st.sidebar.button("Logout"): del st.session_state["role"] del st.session_state["authenticated"] - st.switch_page("Home.py") \ No newline at end of file + st.switch_page("Home.py") diff --git a/app/src/pages/12_API_Test.py b/app/src/pages/12_API_Test.py index f0330105fd..51c009b2fa 100644 --- a/app/src/pages/12_API_Test.py +++ b/app/src/pages/12_API_Test.py @@ -7,123 +7,106 @@ SideBarLinks() -st.write("# Accessing a REST API from Within Streamlit") -""" +st.write("# API Test Page") +st.write("Testing all Admin API routes from Streamlit.") +API_BASE = "http://api:4000/a" -Simply retrieving data from a REST api running in a separate Docker Container. - -If the container isn't running, this will be very unhappy. But the Streamlit app -should not totally die. - - -data = {} -try: - data = requests.get('http://web-api:4000/data').json() -except: - st.write("**Important**: Could not connect to sample api, so using dummy data.") - data = {"a":{"b": "123", "c": "hello"}, "z": {"b": "456", "c": "goodbye"}} - -st.dataframe(data) -""" - +st.markdown("---") # ============================ # USER STORY 1 β€” View Pending Reports # ============================ st.write("### View Pending Reports") -get_pending_reports = requests.get("http://api:4000/a/reports/pending") + try: - st.write(get_pending_reports.json()) + response = requests.get(f"{API_BASE}/reports/pending") + st.write(response.json()) except: - st.write("Could not connect to database to get pending reports.") - + st.write("Could not connect to API.") # ============================ # USER STORY 1 β€” Resolve Report # ============================ st.write("### Resolve Report (example: report_id = 1)") -resolve_report = requests.put("http://api:4000/a/reports/1/resolve") + try: - st.write(resolve_report.json()) + response = requests.put(f"{API_BASE}/reports/1/resolve") + st.write(response.json()) except: st.write("Could not resolve report.") - # ============================ # USER STORY 2 β€” Update User Role # ============================ -st.write("### Update User Role (example: user_id = 1)") -update_user_role = requests.put( - "http://api:4000/a/users/1/role", - json={"role": "admin"} -) +st.write("### Update User Role (example: user_id = 1 β†’ admin)") + try: - st.write(update_user_role.json()) + payload = {"role": "admin"} + response = requests.put(f"{API_BASE}/users/1/role", json=payload) + st.write(response.json()) except: st.write("Could not update user role.") - # ============================ # USER STORY 3 β€” Deactivate User # ============================ st.write("### Deactivate User (example: user_id = 1)") -deactivate_user = requests.put("http://api:4000/a/users/1/deactivate") + try: - st.write(deactivate_user.json()) + response = requests.put(f"{API_BASE}/users/1/deactivate") + st.write(response.json()) except: st.write("Could not deactivate user.") - # ============================ # USER STORY 4 β€” Create Announcement # ============================ st.write("### Create Announcement") -create_announcement = requests.post( - "http://api:4000/a/announcements", - json={"announcer_id": 1, "message": "Test announcement"} -) + try: - st.write(create_announcement.json()) + payload = {"announcer_id": 1, "message": "Test announcement"} + response = requests.post(f"{API_BASE}/announcements", json=payload) + st.write(response.json()) except: st.write("Could not create announcement.") - # ============================ # USER STORY 5 β€” Analytics Summary # ============================ st.write("### Analytics Summary") -analytics_summary = requests.get("http://api:4000/a/analytics/summary") + try: - st.write(analytics_summary.json()) + response = requests.get(f"{API_BASE}/analytics/summary") + st.write(response.json()) except: st.write("Could not load analytics summary.") - # ============================ # USER STORY 6 β€” Delete Duplicate Items # ============================ st.write("### Delete Duplicate Items") -delete_duplicates = requests.delete("http://api:4000/a/items/duplicates") + try: - st.write(delete_duplicates.json()) + response = requests.delete(f"{API_BASE}/items/duplicates") + st.write(response.json()) except: st.write("Could not delete duplicate items.") - # ============================ # USER STORY 6+1 β€” Delete Specific Item # ============================ st.write("### Delete Specific Item (example: item_id = 1)") -delete_item = requests.delete("http://api:4000/a/items/1") + try: - st.write(delete_item.json()) + response = requests.delete(f"{API_BASE}/items/1") + st.write(response.json()) except: st.write("Could not delete item.") From ef7a49841b208ac7095157bcfdc3197db60f5647 Mon Sep 17 00:00:00 2001 From: yurikakan Date: Sat, 6 Dec 2025 15:36:45 -0500 Subject: [PATCH 19/27] Fixed main with Docker + Cleaned files --- Dockerfile | 27 +++++++++++++++----- api/Dockerfile | 5 ++-- api/backend/admin/admin_routes.py | 18 ++++++------- api/backend/rest_entry.py | 16 +++++++----- app/Dockerfile | 6 ++--- app/src/assets/FitHublogo.png | Bin 0 -> 4970 bytes app/src/pages/02_Map_Demo.py | 2 +- app/src/pages/05_Admin_Home.py | 2 +- docker-compose.yml | 41 +++++++++++++++++------------- 9 files changed, 70 insertions(+), 47 deletions(-) create mode 100644 app/src/assets/FitHublogo.png diff --git a/Dockerfile b/Dockerfile index de2037346c..63409b8617 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,27 @@ FROM python:3.11-slim -WORKDIR /app +WORKDIR /appcode -COPY api/requirements.txt ./requirements.txt -RUN pip install --no-cache-dir -r requirements.txt +# downloads +RUN apt-get update && apt-get install -y \ + build-essential \ + curl \ + git \ + libfreetype6-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* -COPY api/ ./api/ +# RUN mkdir /requirements +# COPY ./src/requirements.txt /requirements/requirements.txt -ENV PYTHONPATH="/app/api" +COPY ./src/requirements.txt . -EXPOSE 4000 +RUN pip3 install -r requirements.txt -CMD ["python", "-u", "api/backend_app.py"] +# lists all the packages +RUN ls + +EXPOSE 8501 + +# docker container runs streamlit +CMD ["streamlit", "run", "Home.py", "--server.port=8501", "--server.address=0.0.0.0"] \ No newline at end of file diff --git a/api/Dockerfile b/api/Dockerfile index ebaffe1d77..3302e1c4ef 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -2,11 +2,12 @@ FROM python:3.11-slim WORKDIR /apicode -COPY requirements.txt . +COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 4000 -CMD ["python", "-u", "backend_app.py"] +# Run Python in unbuffered mode to ensure logs are immediately visible +CMD ["python", "-u", "backend_app.py"] \ No newline at end of file diff --git a/api/backend/admin/admin_routes.py b/api/backend/admin/admin_routes.py index a16f5b109f..b364f1f30d 100644 --- a/api/backend/admin/admin_routes.py +++ b/api/backend/admin/admin_routes.py @@ -1,10 +1,10 @@ from flask import Blueprint, request, jsonify, make_response from backend.db_connection.db import db -admin_bp = Blueprint('admin', __name__) +admin = Blueprint('admin', __name__) # USER STORY 1 β€” View unresolved reports -@admin_bp.route('/reports/pending', methods=['GET']) +@admin.route('/reports/pending', methods=['GET']) def get_pending_reports(): cursor = db.get_db().cursor() cursor.execute(""" @@ -20,7 +20,7 @@ def get_pending_reports(): # USER STORY 1 β€” Resolve a report -@admin_bp.route('/reports//resolve', methods=['PUT']) +@admin.route('/reports//resolve', methods=['PUT']) def resolve_report(report_id): cursor = db.get_db().cursor() cursor.execute(""" @@ -37,7 +37,7 @@ def resolve_report(report_id): # USER STORY 2 β€” Update user role -@admin_bp.route('/users//role', methods=['PUT']) +@admin.route('/users//role', methods=['PUT']) def update_user_role(user_id): new_role = request.json.get("role") @@ -56,7 +56,7 @@ def update_user_role(user_id): # USER STORY 3 β€” Deactivate user -@admin_bp.route('/users//deactivate', methods=['PUT']) +@admin.route('/users//deactivate', methods=['PUT']) def deactivate_user(user_id): cursor = db.get_db().cursor() cursor.execute(""" @@ -73,7 +73,7 @@ def deactivate_user(user_id): # USER STORY 4 β€” Create announcement -@admin_bp.route('/announcements', methods=['POST']) +@admin.route('/announcements', methods=['POST']) def create_announcement(): data = request.json announcer = data.get("announcer_id") @@ -101,7 +101,7 @@ def create_announcement(): # USER STORY 5 β€” Analytics summary -@admin_bp.route('/analytics/summary', methods=['GET']) +@admin.route('/analytics/summary', methods=['GET']) def analytics_summary(): cursor = db.get_db().cursor() cursor.execute(""" @@ -118,7 +118,7 @@ def analytics_summary(): # USER STORY 6 β€” Delete duplicate items -@admin_bp.route('/items/duplicates', methods=['DELETE']) +@admin.route('/items/duplicates', methods=['DELETE']) def delete_duplicate_items(): cursor = db.get_db().cursor() cursor.execute(""" @@ -141,7 +141,7 @@ def delete_duplicate_items(): # USER STORY 6 + 1 β€” Delete specific item -@admin_bp.route('/items/', methods=['DELETE']) +@admin.route('/items/', methods=['DELETE']) def delete_item(item_id): try: cursor = db.get_db().cursor() diff --git a/api/backend/rest_entry.py b/api/backend/rest_entry.py index c2f63e2f81..22dd79f473 100644 --- a/api/backend/rest_entry.py +++ b/api/backend/rest_entry.py @@ -8,7 +8,9 @@ from backend.simple.simple_routes import simple_routes from backend.ngos.ngo_routes import ngos from backend.dataAnalysts.dataAnalyst_routes import dataAnalyst -from backend.admin.admin_routes import admin_bp +from backend.admin.admin_routes import admin + +logging.basicConfig(level=logging.DEBUG) @@ -37,11 +39,11 @@ def create_app(): # # these are for the DB object to be able to connect to MySQL. # app.config['MYSQL_DATABASE_USER'] = 'root' - app.config["MYSQL_DATABASE_USER"] = os.getenv("MYSQL_USER", "fithub").strip() - app.config["MYSQL_DATABASE_PASSWORD"] = os.getenv("MYSQL_PASSWORD", "fithub").strip() - app.config["MYSQL_DATABASE_HOST"] = os.getenv("MYSQL_HOST", "db").strip() - app.config["MYSQL_DATABASE_PORT"] = int(os.getenv("MYSQL_PORT", "3306").strip()) - app.config["MYSQL_DATABASE_DB"] = os.getenv("MYSQL_DATABASE", "fithub").strip() + app.config["MYSQL_DATABASE_USER"] = os.getenv("DB_USER", "root").strip() + app.config["MYSQL_DATABASE_PASSWORD"] = os.getenv("MYSQL_ROOT_PASSWORD", "password").strip() + app.config["MYSQL_DATABASE_HOST"] = os.getenv("DB_HOST", "db").strip() + app.config["MYSQL_DATABASE_PORT"] = int(os.getenv("DB_PORT", "3306").strip()) + app.config["MYSQL_DATABASE_DB"] = os.getenv("DB_NAME", "fithub").strip() # Initialize the database object with the settings above. app.logger.info("current_app(): starting the database connection") @@ -53,7 +55,7 @@ def create_app(): app.register_blueprint(simple_routes) app.register_blueprint(ngos, url_prefix="/ngo") app.register_blueprint(dataAnalyst, url_prefix="/d") - app.register_blueprint(admin_bp, url_prefix="/a") + app.register_blueprint(admin, url_prefix="/a") # Don't forget to return the app object diff --git a/app/Dockerfile b/app/Dockerfile index 3a9142fabb..8db98a4339 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-slim -WORKDIR /appcode +WORKDIR /app RUN apt-get update && apt-get install -y \ build-essential \ @@ -13,8 +13,8 @@ RUN apt-get update && apt-get install -y \ COPY ./src/requirements.txt . RUN pip3 install -r requirements.txt -COPY ./src ./src +COPY ./src ./ EXPOSE 8501 -CMD ["streamlit", "run", "src/Home.py", "--server.port=8501", "--server.address=0.0.0.0"] +CMD ["streamlit", "run", "Home.py", "--server.port=8501", "--server.address=0.0.0.0"] diff --git a/app/src/assets/FitHublogo.png b/app/src/assets/FitHublogo.png new file mode 100644 index 0000000000000000000000000000000000000000..1e235e382551e619c9a9d963cc3037b490546170 GIT binary patch literal 4970 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 RUjP0?X{qbM(W-V){{!uABYpq? literal 0 HcmV?d00001 diff --git a/app/src/pages/02_Map_Demo.py b/app/src/pages/02_Map_Demo.py index 5ca09a9633..e9908375a5 100644 --- a/app/src/pages/02_Map_Demo.py +++ b/app/src/pages/02_Map_Demo.py @@ -10,7 +10,7 @@ SideBarLinks() # add the logo -add_logo("assets/logo.png", height=400) +add_logo("src/assets/logo.png", height=400) # set up the page st.markdown("# Mapping Demo") diff --git a/app/src/pages/05_Admin_Home.py b/app/src/pages/05_Admin_Home.py index e23e8a06c8..a129cd837b 100644 --- a/app/src/pages/05_Admin_Home.py +++ b/app/src/pages/05_Admin_Home.py @@ -9,7 +9,7 @@ SideBarLinks() # Add logo to sidebar -add_logo("public/FitHublogo.png") +add_logo("assets/FitHublogo.png") # Log admin dashboard access logger.info(f"Admin Dashboard loaded by {st.session_state.get('first_name', 'Unknown')}") diff --git a/docker-compose.yml b/docker-compose.yml index 6443e06d90..ed638732f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,31 +1,38 @@ +name: fithub + services: app: build: ./app - container_name: fithub-app - volumes: - - ./app/src:/appcode/src + container_name: web-app + hostname: web-app + volumes: ["./app/src:/app"] + environment: + - WATCHPACK_POLLING=true ports: - - "8501:8501" + - 8501:8501 api: build: ./api - container_name: fithub-api + container_name: web-api + hostname: web-api + # mapping on folder - insert into docker like harddrive + volumes: ["./api:/apicode"] + environment: + - WATCHPACK_POLLING=true ports: - - "4000:4000" - volumes: - - ./api:/apicode + - 4000:4000 db: - image: mysql:8.0 - container_name: fithub-db - environment: - MYSQL_ROOT_PASSWORD: 1234 - MYSQL_DATABASE: fithub + env_file: + - ./api/.env + image: mysql:9 + container_name: mysql_db + hostname: db volumes: - - ./database-files:/docker-entrypoint-initdb.d/:ro - - mysql_data:/var/lib/mysql + - "./database-files:/docker-entrypoint-initdb.d/:ro" + - "mysql_data:/var/lib/mysql" ports: - - "3200:3306" + - 3200:3306 volumes: - mysql_data: + mysql_data: \ No newline at end of file From a4d1d89130316f4a966d214dc71ab4d8c2f087a0 Mon Sep 17 00:00:00 2001 From: LondonJones Date: Sat, 6 Dec 2025 17:03:35 -0500 Subject: [PATCH 20/27] Created Streamlit Homepage Added Homepage, Add pages for personas dashboards, changed wording for data analyst routes, fixed fithuub logo in nav --- .idea/misc.xml | 2 +- .../dataAnalysts/dataAnalyst_routes.py | 2 +- app/src/Home.py | 184 ++++++++++++------ app/src/assets/FitHubLogo+Tagline.png | Bin 0 -> 9920 bytes app/src/assets/FitHublogo.png | Bin 0 -> 4970 bytes app/src/assets/fitlogo.png | Bin 10009 -> 0 bytes app/src/modules/nav.py | 4 +- app/src/pages/00_AdminDash.py | 0 app/src/pages/00_DADash.py | 0 app/src/pages/00_SwapperDash.py | 0 app/src/pages/00_TakerDash.py | 0 ...20_Admin_Home.py => 20_Admin_Dashboard.py} | 0 12 files changed, 128 insertions(+), 64 deletions(-) create mode 100644 app/src/assets/FitHubLogo+Tagline.png create mode 100644 app/src/assets/FitHublogo.png delete mode 100644 app/src/assets/fitlogo.png create mode 100644 app/src/pages/00_AdminDash.py create mode 100644 app/src/pages/00_DADash.py create mode 100644 app/src/pages/00_SwapperDash.py create mode 100644 app/src/pages/00_TakerDash.py rename app/src/pages/{20_Admin_Home.py => 20_Admin_Dashboard.py} (100%) diff --git a/.idea/misc.xml b/.idea/misc.xml index 16842813a7..eeba9c4765 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/api/backend/dataAnalysts/dataAnalyst_routes.py b/api/backend/dataAnalysts/dataAnalyst_routes.py index ab6c8e1710..721fe4a223 100644 --- a/api/backend/dataAnalysts/dataAnalyst_routes.py +++ b/api/backend/dataAnalysts/dataAnalyst_routes.py @@ -126,7 +126,7 @@ def get_orders_per_users(): #-----User Story 5------ #As a Senior Data Analyst, I want to see shipments with above-average -# delivery times (excluding those still in transit), so I can flag carriers with delays. +# delivery times (excluding those still in transit), so I can flag carriers who have a pattern of delays. @dataAnalyst.route('/shipments/delay', methods=['GET']) def get_shipments_delay(): diff --git a/app/src/Home.py b/app/src/Home.py index c0fe35aa19..391b6b96d1 100644 --- a/app/src/Home.py +++ b/app/src/Home.py @@ -1,79 +1,143 @@ -################################################## -# This is the main/entry-point file for the -# sample application for your project -################################################## - -# Set up basic logging infrastructure import logging -logging.basicConfig(format='%(filename)s:%(lineno)s:%(levelname)s -- %(message)s', level=logging.INFO) -logger = logging.getLogger(__name__) - -# import the main streamlit library as well -# as SideBarLinks function from src/modules folder import streamlit as st from modules.nav import SideBarLinks -# streamlit supports reguarl and wide layout (how the controls -# are organized/displayed on the screen). -st.set_page_config(layout = 'wide') +# Logging +logging.basicConfig(format='%(filename)s:%(lineno)s:%(levelname)s -- %(message)s', level=logging.INFO) +logger = logging.getLogger(__name__) -# If a user is at this page, we assume they are not -# authenticated. So we change the 'authenticated' value -# in the streamlit session_state to false. +# Page config +st.set_page_config(layout='wide') st.session_state['authenticated'] = False -# Use the SideBarLinks function from src/modules/nav.py to control -# the links displayed on the left-side panel. -# IMPORTANT: ensure src/.streamlit/config.toml sets -# showSidebarNavigation = false in the [client] section +# Sidebar SideBarLinks(show_home=True) # *************************************************** -# The major content of this page +# CSS Styling +# *************************************************** +st.markdown(""" + +""", unsafe_allow_html=True) + # *************************************************** +# Main content +# *************************************************** + +# Logo centered at top +st.markdown('
', unsafe_allow_html=True) +st.markdown('
', unsafe_allow_html=True) -# set the title of the page and provide a simple prompt. -logger.info("Loading the Home page of the app") -st.title('CS 3200 Project Template') -st.write('\n\n') -# st.write('### Overview:') -# st.write('\n') -st.write('#### HI! As which user would you like to log in?') +# Swap into Style text +st.markdown('
Swap into style and swap roles
', unsafe_allow_html=True) -# For each of the user personas for which we are implementing -# functionality, we put a button on the screen that the user -# can click to MIMIC logging in as that mock user. +# Admin emoji + button +col1, col2 = st.columns([80, 1000], gap="small") +with col1: + st.markdown('
πŸ§‘πŸ»β€πŸ’Ό
', unsafe_allow_html=True) +with col2: + if st.button("Admin", key="Admin", use_container_width=True): + st.session_state['authenticated'] = True + st.session_state['role'] = 'Admin: Aisha' + st.session_state['first_name'] = 'Aisha' + logger.info("Logging in as Admin Persona") + st.switch_page('pages/00_AdminDash.py') -if st.button("Act as John, a Political Strategy Advisor", - type = 'primary', - use_container_width=True): - # when user clicks the button, they are now considered authenticated - st.session_state['authenticated'] = True - # we set the role of the current user - st.session_state['role'] = 'pol_strat_advisor' - # we add the first name of the user (so it can be displayed on - # subsequent pages). - st.session_state['first_name'] = 'John' - # finally, we ask streamlit to switch to another page, in this case, the - # landing page for this particular user type - logger.info("Logging in as Political Strategy Advisor Persona") - st.switch_page('pages/00_Pol_Strat_Home.py') +st.markdown('
', unsafe_allow_html=True) -if st.button('Act as Mohammad, an USAID worker', - type = 'primary', - use_container_width=True): - st.session_state['authenticated'] = True - st.session_state['role'] = 'usaid_worker' - st.session_state['first_name'] = 'Mohammad' - st.switch_page('pages/10_USAID_Worker_Home.py') +# Data Analyst emoji + button +col1, col2 = st.columns([80, 1000], gap="small") +with col1: + st.markdown('
πŸ‘©πŸ½β€πŸ’»
', unsafe_allow_html=True) +with col2: + if st.button("Data Analyst", key="Data Analyst", use_container_width=True): + st.session_state['authenticated'] = True + st.session_state['role'] = 'Data Analyst: Blair' + st.session_state['first_name'] = 'Blair' + logger.info("Logging in as Data Analyst Persona") + st.switch_page('pages/00_DADash.py') -if st.button('Act as System Administrator', - type = 'primary', - use_container_width=True): - st.session_state['authenticated'] = True - st.session_state['role'] = 'admin' - st.session_state['first_name'] = 'SysAdmin' - st.switch_page('pages/05_Admin_Home.py') +st.markdown('
', unsafe_allow_html=True) +# Swapper emoji + button +col1, col2 = st.columns([80, 1000], gap="small") +with col1: + st.markdown('
πŸ™‹πŸΌβ€β™‚οΈ
', unsafe_allow_html=True) +with col2: + if st.button("Swapper", key="Swapper", use_container_width=True): + st.session_state['authenticated'] = True + st.session_state['role'] = 'Swapper: Andrea' + st.session_state['first_name'] = 'Andrea' + logger.info("Logging in as Swapper Persona") + st.switch_page('pages/00_SwapperDash.py') +st.markdown('
', unsafe_allow_html=True) +# Taker emoji + button +col1, col2 = st.columns([80, 1000], gap="small") +with col1: + st.markdown('
πŸ™‹πŸΌβ€β™€οΈ
', unsafe_allow_html=True) +with col2: + if st.button("Taker", key="Taker", use_container_width=True): + st.session_state['authenticated'] = True + st.session_state['role'] = 'Taker: Alice' + st.session_state['first_name'] = 'Alice' + logger.info("Logging in as Taker Persona") + st.switch_page('pages/00_TakerDash.py') \ No newline at end of file diff --git a/app/src/assets/FitHubLogo+Tagline.png b/app/src/assets/FitHubLogo+Tagline.png new file mode 100644 index 0000000000000000000000000000000000000000..185cdce6881e26b9fb1c081dcb216ce72a65e3eb GIT binary patch literal 9920 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

ovoA>Gby<+{5sM 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 RUjP0?X{qbM(W-V){{!uABYpq? literal 0 HcmV?d00001 diff --git a/app/src/assets/fitlogo.png b/app/src/assets/fitlogo.png deleted file mode 100644 index 8191e839ee4501511b7a70d9b43ecbf8ec1e760a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10009 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 diff --git a/app/src/modules/nav.py b/app/src/modules/nav.py index df95f181cc..18783d894d 100644 --- a/app/src/modules/nav.py +++ b/app/src/modules/nav.py @@ -63,7 +63,7 @@ def ClassificationNav(): #### ------------------------ System Admin Role ------------------------ def AdminPageNav(): - st.sidebar.page_link("pages/20_Admin_Home.py", label="System Admin", icon="πŸ–₯️") + st.sidebar.page_link("pages/20_Admin_Dashboard.py", label="System Admin", icon="πŸ–₯️") st.sidebar.page_link( "pages/21_ML_Model_Mgmt.py", label="ML Model Management", icon="🏒" ) @@ -140,7 +140,7 @@ def SideBarLinks(show_home=False): """ # add a logo to the sidebar always - st.sidebar.image("assets/fitlogo.png", width=150) + st.sidebar.image("assets/FitHublogo.png", width=150) # If there is no logged in user, redirect to the Home (Landing) page if "authenticated" not in st.session_state: diff --git a/app/src/pages/00_AdminDash.py b/app/src/pages/00_AdminDash.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/src/pages/00_DADash.py b/app/src/pages/00_DADash.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/src/pages/00_SwapperDash.py b/app/src/pages/00_SwapperDash.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/src/pages/00_TakerDash.py b/app/src/pages/00_TakerDash.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/src/pages/20_Admin_Home.py b/app/src/pages/20_Admin_Dashboard.py similarity index 100% rename from app/src/pages/20_Admin_Home.py rename to app/src/pages/20_Admin_Dashboard.py From 3de7ac5cbea1f966d31a7825d5d8b34fae47ae69 Mon Sep 17 00:00:00 2001 From: micahcheng Date: Sat, 6 Dec 2025 17:09:46 -0500 Subject: [PATCH 21/27] admin home stuff --- app/src/modules/nav.py | 2 +- app/src/pages/05_Admin_Home.py | 18 ++--- app/src/pages/20_Admin_User_Tools.py | 109 +++++++++++---------------- app/src/pages/30_Item_Cleanup.py | 8 +- 4 files changed, 58 insertions(+), 79 deletions(-) diff --git a/app/src/modules/nav.py b/app/src/modules/nav.py index e84734d94f..f60aba7b0c 100644 --- a/app/src/modules/nav.py +++ b/app/src/modules/nav.py @@ -40,7 +40,7 @@ def SideBarLinks(show_home=False): """ # Always show the logo - st.sidebar.image("assets/fitlogo.png", width=150) + st.sidebar.image("assets/fitlogo.png", width=204) # If no user authentication flag exists, redirect to home if "authenticated" not in st.session_state: diff --git a/app/src/pages/05_Admin_Home.py b/app/src/pages/05_Admin_Home.py index e23e8a06c8..0f48e62fc0 100644 --- a/app/src/pages/05_Admin_Home.py +++ b/app/src/pages/05_Admin_Home.py @@ -4,7 +4,7 @@ import streamlit as st from streamlit_extras.app_logo import add_logo from modules.nav import SideBarLinks - +st.set_page_config(layout = 'wide') # Load sidebar navigation (includes admin links if authenticated) SideBarLinks() @@ -19,16 +19,16 @@ st.markdown("### ") -if st.button("Manage Reports", use_container_width=True): - logger.info("Navigating to Reports Management") - st.switch_page("pages/21_Admin_Manage_Reports.py") +if st.button('Manage Reports', + type='primary', + use_container_width=True): + st.switch_page('pages/10_Reports_Management.py') +if st.button("User Roles", use_container_width=True): + logger.info("Navigating to User Roles page") + st.switch_page("pages/20_Admin_User_Tools.py") -if st.button("User Roles & Access Tools", use_container_width=True): - logger.info("Navigating to User Tools") - st.switch_page("pages/22_Admin_User_Tools.py") if st.button("Item Cleanup Tools", use_container_width=True): logger.info("Navigating to Item Cleanup Tools") - st.switch_page("pages/23_Admin_Item_Cleanup.py") + st.switch_page("pages/30_Item_Cleanup.py") -st.markdown("

DistinctDevs Β©2025
", unsafe_allow_html=True) diff --git a/app/src/pages/20_Admin_User_Tools.py b/app/src/pages/20_Admin_User_Tools.py index c4ddc77f62..22306b7249 100644 --- a/app/src/pages/20_Admin_User_Tools.py +++ b/app/src/pages/20_Admin_User_Tools.py @@ -1,83 +1,62 @@ import logging logger = logging.getLogger(__name__) + import streamlit as st -import requests -from streamlit_extras.app_logo import add_logo from modules.nav import SideBarLinks +import requests + +st.set_page_config(layout="wide") SideBarLinks() API_BASE = "http://api:4000/a" -st.title("User Management") +st.title("Admin: User Management") -col_left, col_right = st.columns([1, 1]) +# create 2 columns +col1, col2 = st.columns(2) -with col_left: - st.subheader("Update User Role") +# UPDATE USER ROLE - user_id_role = st.text_input("β€œEnter ID”", key="role_user_id") +with col1: + st.subheader("Update User Role") - st.write("") # spacing + user_id = st.number_input("User ID:", step=1, min_value=1) - # Role buttons (Figma style) - role_selected = st.radio( + new_role = st.selectbox( "Select Role", - ["Admin", "User", "Analyst"], - label_visibility="collapsed" + ["admin", "user", "worker"] ) - # Convert Figma labels β†’ backend values - role_map = { - "Admin": "admin", - "User": "user", - "Analyst": "worker" # your DB role - } - - chosen_role = role_map[role_selected] - - st.write("") # spacing - - if st.button("Update Roles", use_container_width=True): - if not user_id_role: - st.error("Please enter a User ID.") - else: - try: - payload = {"role": chosen_role} - response = requests.put( - f"{API_BASE}/users/{user_id_role}/role", - json=payload - ) - if response.status_code == 200: - st.success("User role updated successfully!") - else: - st.error(f"Failed to update role: {response.text}") - - except requests.exceptions.RequestException as e: - st.error(f"Error connecting to API: {e}") - - -with col_right: - - st.subheader("") - - user_id_deactivate = st.text_input("β€œEnter ID”", key="deactivate_user_id") - - st.write("") - - if st.button("Deactivate User", use_container_width=True): - if not user_id_deactivate: - st.error("Please enter a User ID.") - else: - try: - response = requests.put( - f"{API_BASE}/users/{user_id_deactivate}/deactivate" - ) - if response.status_code == 200: - st.success("User deactivated successfully!") - else: - st.error(f"Failed to deactivate user: {response.text}") - - except requests.exceptions.RequestException as e: - st.error(f"Error connecting to API: {e}") + if st.button("Update Role", use_container_width=True): + logger.info(f"Updating role for user {user_id} β†’ {new_role}") + + try: + response = requests.put( + f"{API_BASE}/users/{int(user_id)}/role", + json={"role": new_role} + ) + st.write(response.json()) + except: + st.write("Error: Could not update role.") + + + +# DEACTIVATE USER + +with col2: + st.subheader("Deactivate User") + + deactivate_user_id = st.number_input("User ID to Deactivate:", step=1, min_value=1) + + if st.button("Deactivate", use_container_width=True): + logger.info(f"Deactivating user {deactivate_user_id}") + + try: + response = requests.put( + f"{API_BASE}/users/{int(deactivate_user_id)}/deactivate" + ) + st.write(response.json()) + except: + st.write("Error: Could not deactivate user.") diff --git a/app/src/pages/30_Item_Cleanup.py b/app/src/pages/30_Item_Cleanup.py index 4b264aa2ee..e741df0fb7 100644 --- a/app/src/pages/30_Item_Cleanup.py +++ b/app/src/pages/30_Item_Cleanup.py @@ -12,9 +12,9 @@ API_BASE = "http://api:4000/a" -# =============================== + # USER STORY 6 β€” DELETE DUPLICATES -# =============================== + st.write("## Remove Duplicate Items") if st.button("Delete Duplicate Items"): @@ -24,9 +24,9 @@ except: st.error("Error deleting duplicate items.") -# =============================== + # USER STORY 6+1 β€” DELETE SPECIFIC ITEM -# =============================== + st.write("## Delete Specific Item") item_id = st.number_input("Enter Item ID to Delete", min_value=1, step=1) From 031a84fb5576a9602e8bf1bba435b79e8caa6e7e Mon Sep 17 00:00:00 2001 From: yurikakan Date: Sat, 6 Dec 2025 17:12:26 -0500 Subject: [PATCH 22/27] Tested & Patched Admin Routes --- api/backend/admin/admin_routes.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/api/backend/admin/admin_routes.py b/api/backend/admin/admin_routes.py index b364f1f30d..be7a83f336 100644 --- a/api/backend/admin/admin_routes.py +++ b/api/backend/admin/admin_routes.py @@ -1,5 +1,5 @@ from flask import Blueprint, request, jsonify, make_response -from backend.db_connection.db import db +from backend.db_connection import db admin = Blueprint('admin', __name__) @@ -71,6 +71,21 @@ def deactivate_user(user_id): the_response.status_code = 200 return the_response +# USER STORY 3.5 β€” Activate user +@admin.route('/users//activate', methods=['PUT']) +def activate_user(user_id): + cursor = db.get_db().cursor() + cursor.execute(""" + UPDATE Users + SET IsActive = 1 + WHERE UserID = %s; + """, (user_id,)) + + db.get_db().commit() + + the_response = make_response(jsonify({"message": "User activated"})) + the_response.status_code = 200 + return the_response # USER STORY 4 β€” Create announcement @admin.route('/announcements', methods=['POST']) From 4bfd1ff5a4766fb192ed34c7edb0b132bda75fda Mon Sep 17 00:00:00 2001 From: Yurika Kan <148014074+Yurika-Kan@users.noreply.github.com> Date: Sat, 6 Dec 2025 17:22:19 -0500 Subject: [PATCH 23/27] Update api/backend/admin/admin_routes.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/backend/admin/admin_routes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/backend/admin/admin_routes.py b/api/backend/admin/admin_routes.py index be7a83f336..d534fefab2 100644 --- a/api/backend/admin/admin_routes.py +++ b/api/backend/admin/admin_routes.py @@ -70,7 +70,6 @@ def deactivate_user(user_id): the_response = make_response(jsonify({"message": "User deactivated"})) the_response.status_code = 200 return the_response - # USER STORY 3.5 β€” Activate user @admin.route('/users//activate', methods=['PUT']) def activate_user(user_id): From aa67eccc9c4d70ed15a19f09ef9914838be8373f Mon Sep 17 00:00:00 2001 From: yurikakan Date: Sat, 6 Dec 2025 17:23:47 -0500 Subject: [PATCH 24/27] Cleaned --- api/backend/admin/admin_routes.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/api/backend/admin/admin_routes.py b/api/backend/admin/admin_routes.py index be7a83f336..8f782560c2 100644 --- a/api/backend/admin/admin_routes.py +++ b/api/backend/admin/admin_routes.py @@ -71,22 +71,6 @@ def deactivate_user(user_id): the_response.status_code = 200 return the_response -# USER STORY 3.5 β€” Activate user -@admin.route('/users//activate', methods=['PUT']) -def activate_user(user_id): - cursor = db.get_db().cursor() - cursor.execute(""" - UPDATE Users - SET IsActive = 1 - WHERE UserID = %s; - """, (user_id,)) - - db.get_db().commit() - - the_response = make_response(jsonify({"message": "User activated"})) - the_response.status_code = 200 - return the_response - # USER STORY 4 β€” Create announcement @admin.route('/announcements', methods=['POST']) def create_announcement(): From 9148c5b1b8edffb702f665b9d885a4549f8dca68 Mon Sep 17 00:00:00 2001 From: micahcheng Date: Sat, 6 Dec 2025 17:38:00 -0500 Subject: [PATCH 25/27] corrected endpoints for ui --- docker-compose.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ed638732f4..e2d7dc7412 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,9 @@ services: - WATCHPACK_POLLING=true ports: - 8501:8501 + depends_on: + - api + api: build: ./api @@ -21,13 +24,16 @@ services: - WATCHPACK_POLLING=true ports: - 4000:4000 + depends_on: + - db db: env_file: - ./api/.env - image: mysql:9 + image: mysql:8.4 container_name: mysql_db hostname: db + # NO command line needed! volumes: - "./database-files:/docker-entrypoint-initdb.d/:ro" - "mysql_data:/var/lib/mysql" From 4132c025bc9c654744420cdfdd1cb01c50f216d5 Mon Sep 17 00:00:00 2001 From: micahcheng Date: Sat, 6 Dec 2025 18:04:31 -0500 Subject: [PATCH 26/27] connected all routes --- app/src/Home.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/Home.py b/app/src/Home.py index 391b6b96d1..10dcce2538 100644 --- a/app/src/Home.py +++ b/app/src/Home.py @@ -98,7 +98,7 @@ st.session_state['role'] = 'Admin: Aisha' st.session_state['first_name'] = 'Aisha' logger.info("Logging in as Admin Persona") - st.switch_page('pages/00_AdminDash.py') + st.switch_page('pages/05_Admin_Home.py') st.markdown('
', unsafe_allow_html=True) From be58fae4943f3293ae7af0ccb2168601e7e25c1a Mon Sep 17 00:00:00 2001 From: micahcheng Date: Sat, 6 Dec 2025 19:11:30 -0500 Subject: [PATCH 27/27] added styling --- app/src/pages/05_Admin_Home.py | 104 ++++++++++++++++++++----- app/src/pages/10_Reports_Management.py | 72 +++++++++++++---- app/src/pages/20_Admin_User_Tools.py | 61 +++++++++++---- app/src/pages/30_Item_Cleanup.py | 60 +++++++++++--- 4 files changed, 238 insertions(+), 59 deletions(-) diff --git a/app/src/pages/05_Admin_Home.py b/app/src/pages/05_Admin_Home.py index 586a0e1c87..d5d5c789b1 100644 --- a/app/src/pages/05_Admin_Home.py +++ b/app/src/pages/05_Admin_Home.py @@ -1,34 +1,100 @@ import logging -logger = logging.getLogger(__name__) - import streamlit as st from streamlit_extras.app_logo import add_logo from modules.nav import SideBarLinks -st.set_page_config(layout = 'wide') -# Load sidebar navigation (includes admin links if authenticated) -SideBarLinks() -# Add logo to sidebar +logger = logging.getLogger(__name__) + +# Page config +st.set_page_config(layout="wide") + +# Sidebar with logo +SideBarLinks() add_logo("assets/FitHublogo.png") # Log admin dashboard access logger.info(f"Admin Dashboard loaded by {st.session_state.get('first_name', 'Unknown')}") -# Title -st.title("Admin Dashboard") -st.markdown("### ") +# CSS Styling + +st.markdown(""" + +""", unsafe_allow_html=True) + + +# Admin Dashboard UI + + +st.markdown('
πŸ§‘πŸ»β€πŸ’Ό Admin Dashboard
', unsafe_allow_html=True) + +st.markdown('
Manage the platform and user operations
', unsafe_allow_html=True) +st.write("") # spacing + + +col1, col2, col3 = st.columns([1, 1, 1]) -if st.button('Manage Reports', - type='primary', - use_container_width=True): - st.switch_page('pages/10_Reports_Management.py') -if st.button("User Roles", use_container_width=True): - logger.info("Navigating to User Roles page") - st.switch_page("pages/20_Admin_User_Tools.py") +with col1: + if st.button("Manage Reports"): + st.switch_page("pages/10_Reports_Management.py") +with col2: + if st.button("User Roles"): + logger.info("Navigating to User Roles page") + st.switch_page("pages/20_Admin_User_Tools.py") -if st.button("Item Cleanup Tools", use_container_width=True): - logger.info("Navigating to Item Cleanup Tools") - st.switch_page("pages/30_Item_Cleanup.py") +with col3: + if st.button("Item Cleanup Tools"): + logger.info("Navigating to Item Cleanup Tools") + st.switch_page("pages/30_Item_Cleanup.py") diff --git a/app/src/pages/10_Reports_Management.py b/app/src/pages/10_Reports_Management.py index 7250e1c73f..fa263a41a1 100644 --- a/app/src/pages/10_Reports_Management.py +++ b/app/src/pages/10_Reports_Management.py @@ -5,44 +5,82 @@ import requests from streamlit_extras.app_logo import add_logo from modules.nav import SideBarLinks - +st.set_page_config(layout="wide") SideBarLinks() +add_logo("assets/FitHublogo.png") + -st.title("Reports Management") API_BASE = "http://api:4000/a" -# =============================== -# USER STORY 1 β€” VIEW PENDING REPORTS -# =============================== -st.write("## View Pending Reports") +st.markdown(""" + +""", unsafe_allow_html=True) + +st.markdown('
πŸ“‘ Reports Management
', unsafe_allow_html=True) + +# View Pending Reports +st.markdown('
View Pending Reports
', unsafe_allow_html=True) try: response = requests.get(f"{API_BASE}/reports/pending") if response.status_code == 200: reports = response.json() - st.write(f"Found **{len(reports)} pending reports**") - # Display each report inside an expander for report in reports: with st.expander(f"Report #{report['ReportID']} β€” Severity {report['Severity']}"): st.write(f"**Note:** {report['Note']}") st.write(f"**Reported Item:** {report['ReportedItem']}") - else: - st.error("Failed to fetch reports from API.") - + st.error("Failed to fetch reports.") except requests.exceptions.RequestException: - st.error("Could not connect to API server.") - st.info("Make sure the API is running.") + st.error("Could not connect") + -# =============================== -# USER STORY 1 β€” RESOLVE REPORT -# =============================== -st.write("## Resolve a Report") + +# Resolve a Report +st.markdown('
Resolve a Report
', unsafe_allow_html=True) col1, col2 = st.columns(2) + with col1: report_id = st.number_input("Enter Report ID", min_value=1, step=1) diff --git a/app/src/pages/20_Admin_User_Tools.py b/app/src/pages/20_Admin_User_Tools.py index 22306b7249..2953852909 100644 --- a/app/src/pages/20_Admin_User_Tools.py +++ b/app/src/pages/20_Admin_User_Tools.py @@ -6,21 +6,58 @@ import requests st.set_page_config(layout="wide") - SideBarLinks() API_BASE = "http://api:4000/a" -st.title("Admin: User Management") +st.markdown(""" + +""", unsafe_allow_html=True) + +st.markdown('
Admin: User Management
', unsafe_allow_html=True) -# create 2 columns col1, col2 = st.columns(2) - # UPDATE USER ROLE - with col1: - st.subheader("Update User Role") + st.markdown('
Update User Role
', unsafe_allow_html=True) user_id = st.number_input("User ID:", step=1, min_value=1) @@ -31,7 +68,6 @@ if st.button("Update Role", use_container_width=True): logger.info(f"Updating role for user {user_id} β†’ {new_role}") - try: response = requests.put( f"{API_BASE}/users/{int(user_id)}/role", @@ -39,24 +75,21 @@ ) st.write(response.json()) except: - st.write("Error: Could not update role.") - + st.error("Could not update role.") # DEACTIVATE USER - with col2: - st.subheader("Deactivate User") + st.markdown('
Deactivate User
', unsafe_allow_html=True) deactivate_user_id = st.number_input("User ID to Deactivate:", step=1, min_value=1) - if st.button("Deactivate", use_container_width=True): + if st.button("Deactivate User", use_container_width=True): logger.info(f"Deactivating user {deactivate_user_id}") - try: response = requests.put( f"{API_BASE}/users/{int(deactivate_user_id)}/deactivate" ) st.write(response.json()) except: - st.write("Error: Could not deactivate user.") + st.error("Could not deactivate user.") diff --git a/app/src/pages/30_Item_Cleanup.py b/app/src/pages/30_Item_Cleanup.py index e741df0fb7..dab177097d 100644 --- a/app/src/pages/30_Item_Cleanup.py +++ b/app/src/pages/30_Item_Cleanup.py @@ -6,18 +6,61 @@ from streamlit_extras.app_logo import add_logo from modules.nav import SideBarLinks +st.set_page_config(layout="wide") SideBarLinks() - -st.title("Item Cleanup Tools") +add_logo("assets/FitHublogo.png") API_BASE = "http://api:4000/a" +st.markdown(""" + +""", unsafe_allow_html=True) -if st.button("Delete Duplicate Items"): + +st.markdown('
🧹 Item Cleanup Tools
', unsafe_allow_html=True) + + +# Remove Duplicate Items +st.markdown('
Remove Duplicate Items
', unsafe_allow_html=True) + +if st.button("Delete Duplicate Items", use_container_width=True): try: resp = requests.delete(f"{API_BASE}/items/duplicates") st.write(resp.json()) @@ -25,13 +68,12 @@ st.error("Error deleting duplicate items.") -# USER STORY 6+1 β€” DELETE SPECIFIC ITEM - -st.write("## Delete Specific Item") +# Delete Specific Item +st.markdown('
Delete Specific Item
', unsafe_allow_html=True) item_id = st.number_input("Enter Item ID to Delete", min_value=1, step=1) -if st.button("Delete Item"): +if st.button("Delete Item", use_container_width=True): try: resp = requests.delete(f"{API_BASE}/items/{item_id}") st.write(resp.json())