diff --git a/App/controllers/__init__.py b/App/controllers/__init__.py index e75ec60..2c196bd 100644 --- a/App/controllers/__init__.py +++ b/App/controllers/__init__.py @@ -6,6 +6,7 @@ from .student import * from .group import * from .studentGroup import * +from .labType import * from .lotGroup import * from .rfp import * from .bid import * diff --git a/App/controllers/initialize.py b/App/controllers/initialize.py index a3ca446..d2f8b6c 100644 --- a/App/controllers/initialize.py +++ b/App/controllers/initialize.py @@ -3,10 +3,16 @@ from .user import create_user from App.database import db from .lot import create_lot, edit_lotRFP_details +from .labType import create_lab_type from .student import create_student from .admin import create_admin -from .group import create_group +from .group import create_group, approve_group +from .lotGroup import add_lotGroup +from .rfp import create_rfp, approve_rfp +from .bid import create_bid +from .evaluation import create_evaluation, select_evaluation from App.models import * +import io def initialize(): db.drop_all() @@ -14,12 +20,19 @@ def initialize(): create_admin("bob", "bobpass") - create_lot("GIS Lab", "Medium, capable of having 20 machines", 160000.00) - create_lot("Government Office Lab", "Small, capable of having 10 machines", 110000.00) - create_lot("University Computer Lab", "Medium, capable of having 30 machines", 250000.00) - create_lot("Data Center", "Large, capable of having 500 machines", 25000000.00) - create_lot("Medical Imaging Lab", "Medium, capable of having 15 machines", 320000.00) - create_lot("Architecture & Design Studio", "Small, capable of having 12 machines", 195000.00) + gis_type = create_lab_type("GIS Lab", "Medium, capable of having 20 machines") + government_type = create_lab_type("Government Office Lab", "Small, capable of having 10 machines") + university_type = create_lab_type("University Computer Lab", "Medium, capable of having 30 machines") + data_center_type = create_lab_type("Data Center", "Large, capable of having 500 machines") + medical_type = create_lab_type("Medical Imaging Lab", "Medium, capable of having 15 machines") + design_type = create_lab_type("Architecture & Design Studio", "Small, capable of having 12 machines") + + create_lot(gis_type.name, gis_type.description, 160000.00, labTypeId=gis_type.id) + create_lot(government_type.name, government_type.description, 110000.00, labTypeId=government_type.id) + create_lot(university_type.name, university_type.description, 250000.00, labTypeId=university_type.id) + create_lot(data_center_type.name, data_center_type.description, 25000000.00, labTypeId=data_center_type.id) + create_lot(medical_type.name, medical_type.description, 320000.00, labTypeId=medical_type.id) + create_lot(design_type.name, design_type.description, 195000.00, labTypeId=design_type.id) edit_lotRFP_details(1, deviceType="Workstation Desktop", cpu="Intel Core i9-13900K", ram="64GB DDR5", resolution="27-inch 4K IPS", os="Windows 11 Pro", drive="2TB NVMe SSD", gpu="NVIDIA RTX 4080", peripherals="4x USB-A, 2x USB-C, HDMI 2.1, DisplayPort 1.4", features="Mechanical Keyboard, Ergonomic Mouse, Drawing Tablet", io="Webcam, WiFi 6E, Bluetooth 5.2") edit_lotRFP_details(2, deviceType="Laptop", cpu="AMD Ryzen 7 7745HX", ram="32GB DDR5", resolution="15.6-inch FHD 144Hz", os="Windows 11 Pro / Ubuntu 22.04 Dual Boot", drive="1TB NVMe SSD", gpu="NVIDIA RTX 3060", peripherals="3x USB-A, 2x USB-C, HDMI 2.0, SD Card Reader", features="Wireless Keyboard, Wireless Mouse, USB Hub", io="Fingerprint Reader, WiFi 6, Bluetooth 5.0") @@ -29,39 +42,163 @@ def initialize(): edit_lotRFP_details(6, deviceType="High-End Desktop", cpu="Intel Core i9-13900K", ram="64GB DDR5", resolution="Dual 32-inch 4K OLED", os="Windows 11 Pro", drive="2TB NVMe SSD + 2TB HDD", gpu="NVIDIA RTX 4090", peripherals="4x USB-A, 4x USB-C, 2x Thunderbolt_4, SD Card Reader", features="Wireless Ergonomic Keyboard, Precision Mouse, Drawing Tablet (Wacom)", io="Pantone-Calibrated Display, WiFi_6E, Bluetooth_5.2, Hardware Color Calibrator") - create_student("jack", "20240123", "jackpass") - create_student("cooper", "20231245", "cooperpass") - create_student("john", "20229876", "johnpass") - create_student("ray", "20246789", "raypass") + create_student("Aiden Joseph", "20240001", "aidenpass") + create_student("Kareem Ali", "20240002", "kareempass") + create_student("Joshua Peters", "20240003", "joshuapass") + + create_student("Daniel Roberts", "20240004", "danielpass") + create_student("Samantha Clarke", "20240005", "samanthapass") + create_student("Aaliyah Mohammed", "20240006", "aaliyahpass") + + create_student("Leah Charles", "20240007", "leahpass") + create_student("Naomi Thomas", "20240008", "naomipass") + create_student("Ryan Williams", "20240009", "ryanpass") - create_student("tony", "20235678", "tonypass") - create_student("steve", "20242345", "stevepass") - create_student("clint", "20238901", "clintpass") - create_student("bruce", "20238902", "brucepass") + create_student("Darius Edwards", "20240010", "dariuspass") + create_student("Marcus Browne", "20240011", "marcuspass") + create_student("Andre Lewis", "20240012", "andrepass") - create_student("ultron", "20264789", "ultronpass") - create_student("thanos", "20242435", "thanospass") - create_student("loki", "20328901", "lokipass") - create_student("red skull", "20235342", "redskullpass") + create_student("Keisha Grant", "20240013", "keishapass") + create_student("Tricia Ramnarine", "20240014", "triciapass") + create_student("Anil Singh", "20240015", "anilpass") - group = create_group("Avengers") - tony = db.session.scalars(db.select(Student).where(Student.username == "20235678")).first() - steve = db.session.scalars(db.select(Student).where(Student.username == "20242345")).first() - clint = db.session.scalars(db.select(Student).where(Student.username == "20238901")).first() - bruce = db.session.scalars(db.select(Student).where(Student.username == "20238902")).first() - members = [tony.id, steve.id, clint.id, bruce.id] + create_student("Ravi Persad", "20240016", "ravipass") + create_student("Janelle Baptiste", "20240017", "janellepass") + create_student("Shawn Mitchell", "20240018", "shawnpass") - for member in members: - add_studentGroup(member, group.id) + create_student("Kevin Hernandez", "20240019", "kevinpass") + create_student("Ashley Gomez", "20240020", "ashleypass") + create_student("Joey Patterson", "20240021", "joeypass") + create_student("Michelle James", "20240022", "michellepass") + create_student("Tyrell George", "20240023", "tyrellpass") + create_student("Nadia Richards", "20240024", "nadiapass") + create_student("Jerome Phillips", "20240025", "jeromepass") + create_student("Vanessa Edwards", "20240026", "vanessapass") + create_student("Christopher Lewis", "20240027", "christopherpass") + create_student("Melissa Clarke", "20240028", "melissapass") + create_student("Devon Peters", "20240029", "devonpass") + create_student("Sherisse Joseph", "20240030", "sherissepass") + create_student("Darren Mitchell", "20240031", "darrenpass") - ultron = db.session.scalars(db.select(Student).where(Student.username == "20264789")).first() - thanos = db.session.scalars(db.select(Student).where(Student.username == "20242435")).first() - loki = db.session.scalars(db.select(Student).where(Student.username == "20328901")).first() - red_skull = db.session.scalars(db.select(Student).where(Student.username == "20235342")).first() - group1 = create_group("Villains") - members1 = [ultron.id, thanos.id, loki.id, red_skull.id] + # Group 1 + group1 = create_group("NexoraTech") + s1 = db.session.scalars(db.select(Student).where(Student.username == "20240001")).first() + s2 = db.session.scalars(db.select(Student).where(Student.username == "20240002")).first() + s3 = db.session.scalars(db.select(Student).where(Student.username == "20240003")).first() + members1 = [s1.id, s2.id, s3.id] for member in members1: add_studentGroup(member, group1.id) - \ No newline at end of file + # Group 2 + group2 = create_group("QuantumSoft") + s4 = db.session.scalars(db.select(Student).where(Student.username == "20240004")).first() + s5 = db.session.scalars(db.select(Student).where(Student.username == "20240005")).first() + s6 = db.session.scalars(db.select(Student).where(Student.username == "20240006")).first() + + members2 = [s4.id, s5.id, s6.id] + for member in members2: + add_studentGroup(member, group2.id) + + # Group 3 + group3 = create_group("ByteForge") + s7 = db.session.scalars(db.select(Student).where(Student.username == "20240007")).first() + s8 = db.session.scalars(db.select(Student).where(Student.username == "20240008")).first() + s9 = db.session.scalars(db.select(Student).where(Student.username == "20240009")).first() + + members3 = [s7.id, s8.id, s9.id] + for member in members3: + add_studentGroup(member, group3.id) + + # Group 4 + group4 = create_group("CyberNova") + s10 = db.session.scalars(db.select(Student).where(Student.username == "20240010")).first() + s11 = db.session.scalars(db.select(Student).where(Student.username == "20240011")).first() + s12 = db.session.scalars(db.select(Student).where(Student.username == "20240012")).first() + + members4 = [s10.id, s11.id, s12.id] + for member in members4: + add_studentGroup(member, group4.id) + + # Group 5 + group5 = create_group("InnovateTech") + s13 = db.session.scalars(db.select(Student).where(Student.username == "20240013")).first() + s14 = db.session.scalars(db.select(Student).where(Student.username == "20240014")).first() + s15 = db.session.scalars(db.select(Student).where(Student.username == "20240015")).first() + + members5 = [s13.id, s14.id, s15.id] + for member in members5: + add_studentGroup(member, group5.id) + + # Group 6 + group6 = create_group("CloudSystems") + s16 = db.session.scalars(db.select(Student).where(Student.username == "20240016")).first() + s17 = db.session.scalars(db.select(Student).where(Student.username == "20240017")).first() + s18 = db.session.scalars(db.select(Student).where(Student.username == "20240018")).first() + + members6 = [s16.id, s17.id, s18.id] + for member in members6: + add_studentGroup(member, group6.id) + + # Approve groups and assign lots + approve_group(group1.id) + add_lotGroup(1, group1.id) + add_lotGroup(2, group1.id) + + approve_group(group2.id) + add_lotGroup(3, group2.id) + add_lotGroup(4, group2.id) + + approve_group(group3.id) + add_lotGroup(5, group3.id) + add_lotGroup(6, group3.id) + + approve_group(group4.id) + add_lotGroup(1, group4.id) + + approve_group(group5.id) + add_lotGroup(2, group5.id) + + # Create sample RFPs + rfp1_1 = create_rfp(group1.id, 1) + rfp1_2 = create_rfp(group1.id, 2) + rfp2_3 = create_rfp(group2.id, 3) + rfp2_4 = create_rfp(group2.id, 4) + + # Approve some RFPs + approve_rfp(group1.id, 1) + approve_rfp(group1.id, 2) + approve_rfp(group2.id, 3) + + # Generate sample PDF content + def generate_sample_pdf(vendor_name, lot_name, price): + pdf_header = b'%PDF-1.4\n' + pdf_content = ( + f'Sample Bid Document\nVendor: {vendor_name}\nLot: {lot_name}\n' + f'Quoted Price: ${price:,.2f}\n\n' + f'This is a sample bid document for testing purposes.\n' + f'Terms and conditions apply.\n' + ).encode('latin1') + pdf_footer = b'%%EOF\n' + return pdf_header + pdf_content + pdf_footer + + # Create sample bids + bid1 = create_bid(1, group2.id, group1.id, generate_sample_pdf("QuantumSoft", "GIS Lab", 145000), "quantumsoft_gis_bid.pdf", 145000.00) + bid2 = create_bid(1, group3.id, group1.id, generate_sample_pdf("ByteForge", "GIS Lab", 155000), "byteforge_gis_bid.pdf", 155000.00) + bid3 = create_bid(2, group2.id, group1.id, generate_sample_pdf("QuantumSoft", "Gov Office Lab", 105000), "quantumsoft_gov_bid.pdf", 105000.00) + bid4 = create_bid(3, group1.id, group2.id, generate_sample_pdf("NexoraTech", "University Lab", 240000), "nexoratech_uni_bid.pdf", 240000.00) + bid5 = create_bid(3, group4.id, group2.id, generate_sample_pdf("CyberNova", "University Lab", 260000), "cybernova_uni_bid.pdf", 260000.00) + bid6 = create_bid(4, group1.id, group2.id, generate_sample_pdf("NexoraTech", "Data Center", 24950000), "nexoratech_dc_bid.pdf", 24950000.00) + + # Create sample evaluations for draft status + eval1 = create_evaluation(group1.id, group2.id, bid1.id, 1, 4, 3, 4, 3) + eval2 = create_evaluation(group1.id, group3.id, bid2.id, 1, 5, 4, 5, 4) + eval3 = create_evaluation(group1.id, group2.id, bid3.id, 2, 4, 3, 3, 4) + eval4 = create_evaluation(group2.id, group1.id, bid4.id, 3, 5, 4, 4, 5) + eval5 = create_evaluation(group2.id, group4.id, bid5.id, 3, 3, 3, 3, 2) + eval6 = create_evaluation(group2.id, group1.id, bid6.id, 4, 5, 5, 5, 5) + + # Select some evaluations + select_evaluation(eval2.id) + select_evaluation(eval4.id) + select_evaluation(eval6.id) \ No newline at end of file diff --git a/App/controllers/labType.py b/App/controllers/labType.py new file mode 100644 index 0000000..497694d --- /dev/null +++ b/App/controllers/labType.py @@ -0,0 +1,40 @@ +from App.models import LabType +from App.database import db + + +def create_lab_type(name, description): + lab_type = LabType(name, description) + db.session.add(lab_type) + db.session.commit() + return lab_type + + +def get_lab_type(id): + return db.session.get(LabType, id) + + +def get_all_lab_types(): + return db.session.scalars( + db.select(LabType).order_by(LabType.name) + ).all() + + +def edit_lab_type(id, name=None, description=None): + lab_type = get_lab_type(id) + if lab_type: + if name is not None: + lab_type.name = name + if description is not None: + lab_type.description = description + db.session.commit() + return lab_type + return None + + +def remove_lab_type(id): + lab_type = get_lab_type(id) + if lab_type: + db.session.delete(lab_type) + db.session.commit() + return True + return False diff --git a/App/controllers/lot.py b/App/controllers/lot.py index c4e2d30..a0cdd03 100644 --- a/App/controllers/lot.py +++ b/App/controllers/lot.py @@ -3,8 +3,10 @@ from sqlalchemy.orm.attributes import flag_modified from sqlalchemy import or_, and_ -def create_lot(labType, labSize, budget): +def create_lot(labType, labSize, budget, labTypeId=None): newlot = Lot(labType, labSize, budget) + if labTypeId is not None: + newlot.labTypeId = labTypeId db.session.add(newlot) db.session.flush() newlot.set_generated_name() @@ -25,11 +27,13 @@ def get_all_lots_json(): lots = [lot.get_json() for lot in lots] return lots -def edit_lot(id, labType=None, labSize=None, budget=None): +def edit_lot(id, labType=None, labTypeId=None, labSize=None, budget=None): lot = get_lot(id) if lot: if labType: lot.labType = labType + if labTypeId is not None: + lot.labTypeId = labTypeId if labSize: lot.labSize = labSize diff --git a/App/models/__init__.py b/App/models/__init__.py index 12a5df4..adad89a 100644 --- a/App/models/__init__.py +++ b/App/models/__init__.py @@ -5,6 +5,7 @@ from .group import * from .lotGroup import * from .studentGroup import * +from .labType import * from .RFP import * from .bid import * from .evaluation import * \ No newline at end of file diff --git a/App/models/labType.py b/App/models/labType.py new file mode 100644 index 0000000..31844bc --- /dev/null +++ b/App/models/labType.py @@ -0,0 +1,19 @@ +from App.database import db + +class LabType(db.Model): + __tablename__ = 'lab_type' + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(60), nullable=False, unique=True) + description = db.Column(db.String(1000), nullable=False) + + def __init__(self, name, description): + self.name = name + self.description = description + + def get_json(self): + return { + 'id': self.id, + 'name': self.name, + 'description': self.description + } diff --git a/App/models/lot.py b/App/models/lot.py index 910c682..874d562 100644 --- a/App/models/lot.py +++ b/App/models/lot.py @@ -4,6 +4,8 @@ class Lot(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(10)) labType = db.Column(db.String(30), nullable=False) + labTypeId = db.Column(db.Integer, db.ForeignKey('lab_type.id'), nullable=True) + labTypeObj = db.relationship('LabType', foreign_keys=[labTypeId], lazy='joined') labSize = db.Column(db.String(1000), nullable=False) budget = db.Column(db.Float, nullable=False) deviceType = db.Column(db.String(1000), default="") diff --git a/App/static/style.css b/App/static/style.css index 5f15e0f..a485724 100644 --- a/App/static/style.css +++ b/App/static/style.css @@ -1,3 +1,333 @@ html { padding: 0; -} \ No newline at end of file + margin: 0; + background: #020617; + color: #e2e8f0; +} + +body { + margin: 0; + padding: 0; + background: #020617; + color: #e2e8f0; +} + +:root { + color-scheme: dark; +} + +body, +input, +button, +select, +textarea { + color: inherit; + font-family: "Manrope", sans-serif; +} + +.bg-white { + background-color: #0f172a !important; +} + +.bg-slate-50 { + background-color: #111827 !important; +} + +.bg-slate-100 { + background-color: #111827 !important; +} + +.bg-slate-200 { + background-color: #161e2e !important; +} + +.bg-slate-300 { + background-color: #1f2937 !important; +} + +.bg-slate-800 { + background-color: #111827 !important; +} + +.bg-slate-900 { + background-color: #020617 !important; +} + +.bg-slate-950 { + background-color: #020617 !important; +} + +.border-slate-200, +.border-slate-300, +.border-slate-400, +.border-slate-500, +.border-slate-600, +.border-slate-800 { + border-color: #334155 !important; +} + +.text-slate-900 { + color: #f8fafc !important; +} + +.text-slate-700, +.text-slate-600, +.text-slate-500 { + color: #94a3b8 !important; +} + +.bg-brand-50 { + background-color: #172554 !important; +} + +.text-brand-600 { + color: #93c5fd !important; +} + +.bg-slate-800 { + background-color: #111827 !important; +} + +.bg-slate-950 { + background-color: #020617 !important; +} + +.bg-white\/50 { + background-color: rgba(15, 23, 42, 0.5) !important; +} + +.bg-slate-100\/50 { + background-color: rgba(15, 23, 42, 0.5) !important; +} + +.bg-slate-50\/80 { + background-color: rgba(15, 23, 42, 0.8) !important; +} + +.text-slate-800, +.text-slate-900, +.text-black { + color: #f8fafc !important; +} + +.text-slate-400, +.text-gray-500, +.text-gray-700, +.text-gray-900, +.placeholder\:text-slate-400 { + color: #94a3b8 !important; +} + +.bg-gray-300, +.bg-gray-500, +.bg-gray-700 { + background-color: #1f2937 !important; +} + +.bg-stone-100, +.bg-white, +.bg-slate-50, +.bg-slate-100, +.bg-slate-200, +.bg-slate-300, +.bg-slate-800, +.bg-slate-900, +.bg-slate-950 { + background-color: #0f172a !important; +} + +.bg-white\/50, +.bg-slate-100\/50, +.bg-slate-50\/80, +.bg-slate-150 { + background-color: rgba(15, 23, 42, 0.55) !important; +} + +.hover\:bg-slate-50:hover, +.hover\:bg-slate-100:hover, +.hover\:bg-white:hover, +.hover\:bg-white\/50:hover, +.hover\:bg-slate-100\/50:hover, +.hover\:bg-slate-50\/80:hover { + background-color: #111827 !important; +} + +.focus\:bg-white:focus, +.focus\:bg-slate-50:focus, +.focus\:bg-slate-100:focus, +.focus\:bg-slate-100\/50:focus, +.focus\:bg-slate-50\/80:focus { + background-color: #111827 !important; +} + +.hover\:text-slate-900:hover, +.hover\:text-slate-800:hover, +.hover\:text-slate-700:hover, +.hover\:text-slate-600:hover, +.hover\:text-slate-500:hover, +.hover\:text-black:hover, +.hover\:text-gray-700:hover, +.hover\:text-gray-900:hover { + color: #f8fafc !important; +} + +.hover\:bg-red-100:hover { + background-color: #7f1d1d !important; +} + +.hover\:bg-amber-100:hover { + background-color: #78350f !important; +} + +.hover\:bg-gray-300:hover, +.hover\:bg-gray-400:hover { + background-color: #334155 !important; +} + +.shadow-panel { + box-shadow: 0 25px 70px rgba(0, 0, 0, 0.45) !important; +} + +.bg-emerald-100 { + background-color: #064e3b !important; +} + +.text-emerald-700, +.text-emerald-600, +.text-amber-700, +.text-red-700, +.text-brand-600, +.text-slate-900 { + color: #a7f3d0 !important; +} + +.bg-amber-100 { + background-color: #78350f !important; +} + +.bg-red-100 { + background-color: #7f1d1d !important; +} + +.bg-brand-50 { + background-color: #172554 !important; +} + +.border-green-600, +.border-red-600, +.border-amber-200, +.border-emerald-200, +.border-slate-100, +.border-slate-200, +.border-slate-300, +.border-slate-400, +.border-slate-500, +.border-slate-600, +.border-slate-800 { + border-color: #334155 !important; +} + +.bg-slate-150, +.bg-slate-100\/50, +.bg-slate-50\/80 { + background-color: rgba(15, 23, 42, 0.55) !important; +} + +.text-slate-700, +.text-slate-600, +.text-slate-500 { + color: #94a3b8 !important; +} + +.placeholder\:text-slate-400::placeholder { + color: #94a3b8 !important; +} + +.border-slate-100, +.border-slate-200, +.border-slate-300, +.border-slate-400, +.border-slate-500, +.border-slate-600, +.border-slate-800 { + border-color: #334155 !important; +} + +.bg-emerald-50 { + background-color: #064e3b !important; +} + +.border-emerald-200 { + border-color: #064e3b !important; +} + +.text-emerald-800 { + color: #86efac !important; +} + +.bg-red-50 { + background-color: #831843 !important; +} + +.border-red-200 { + border-color: #831843 !important; +} + +.text-red-800 { + color: #fecdd3 !important; +} + +.bg-amber-50 { + background-color: #78350f !important; +} + +.border-amber-200 { + border-color: #78350f !important; +} + +.text-amber-800 { + color: #facc15 !important; +} + +a { + transition-duration: 150ms; +} + +#manage-groups-page { + background-color: #020617 !important; +} + +#manage-groups-page .rounded-xl.border.border-slate-800.bg-slate-950\/95 { + background-color: #1f2937 !important; + border-color: #334155 !important; +} + +#manage-groups-page .rounded-xl.border.border-slate-200.bg-slate-50 { + background-color: #1f2937 !important; + border-color: #334155 !important; +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +/* Firefox scrollbar */ +* { + scrollbar-width: thin; + scrollbar-color: #cbd5e1 transparent; +} diff --git a/App/templates/admin/assign_lots_modal.html b/App/templates/admin/assign_lots_modal.html index b839393..6fc8c83 100644 --- a/App/templates/admin/assign_lots_modal.html +++ b/App/templates/admin/assign_lots_modal.html @@ -1,22 +1,44 @@ -