diff --git a/contributors/varshit-pratap-singh-bhadauria.json b/contributors/varshit-pratap-singh-bhadauria.json new file mode 100644 index 000000000..f48a98d03 --- /dev/null +++ b/contributors/varshit-pratap-singh-bhadauria.json @@ -0,0 +1,10 @@ +{ + "name": "Varshit Pratap Singh Bhadauria", + "github": "temporalzone", + "program": "B.Tech CSE", + "campus": "Amity Noida", + "skills": ["python", "java", "javascript", "react", "django", "flask", "rest-apis", "sqlite", "mysql", "git", "dsa", "jwt-auth"], + "interests": ["agents", "AI", "api-integration", "backend", "automation"], + "track": "A: Agent Builders", + "my_twin": "My digital twin would correlate sleep cycles, screen time, and focus sessions to detect productivity patterns I can't see manually. I want it to predict low-performance windows before they happen — not just track, but intervene with data-backed recommendations. This is exactly the kind of agent I want to build." +} diff --git a/package-lock.json b/package-lock.json index c1282f975..5851bf8c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,14 @@ "zod": "^3.24.0" }, "bin": { - "lpi-sandbox": "dist/index.js" + "lpi-sandbox": "dist/src/index.js" }, "devDependencies": { "@types/node": "^22.0.0", "typescript": "^5.7.0" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@hono/node-server": { diff --git a/submissions/varshit-pratap-singh-bhadauria/One pagers/AI_Agent_Landscape.docx b/submissions/varshit-pratap-singh-bhadauria/One pagers/AI_Agent_Landscape.docx new file mode 100644 index 000000000..22b02d6da Binary files /dev/null and b/submissions/varshit-pratap-singh-bhadauria/One pagers/AI_Agent_Landscape.docx differ diff --git a/submissions/varshit-pratap-singh-bhadauria/One pagers/Ontologies_in_AI_and_Knowledge_Systems (1).docx b/submissions/varshit-pratap-singh-bhadauria/One pagers/Ontologies_in_AI_and_Knowledge_Systems (1).docx new file mode 100644 index 000000000..c70d4e40e Binary files /dev/null and b/submissions/varshit-pratap-singh-bhadauria/One pagers/Ontologies_in_AI_and_Knowledge_Systems (1).docx differ diff --git a/submissions/varshit-pratap-singh-bhadauria/One pagers/Taxonomy_in_Technology_and_AI.docx b/submissions/varshit-pratap-singh-bhadauria/One pagers/Taxonomy_in_Technology_and_AI.docx new file mode 100644 index 000000000..1dfe90526 Binary files /dev/null and b/submissions/varshit-pratap-singh-bhadauria/One pagers/Taxonomy_in_Technology_and_AI.docx differ diff --git a/submissions/varshit-pratap-singh-bhadauria/level 2/HOW_I_DID_IT.md b/submissions/varshit-pratap-singh-bhadauria/level 2/HOW_I_DID_IT.md new file mode 100644 index 000000000..2a953563f --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level 2/HOW_I_DID_IT.md @@ -0,0 +1,53 @@ +# Level 2 Submission — Varshit Pratap Singh Bhadauria + +## What I Did + +### Step 1: Ran LPI Sandbox +Command run kiya: `npm run test-client` + +Output: +=== LPI Sandbox Test Client === +[PASS] smile_overview({}) +[PASS] smile_phase_detail({"phase":"reality-emulation"}) +[PASS] list_topics({}) +[PASS] query_knowledge({"query":"explainable AI"}) +[PASS] get_case_studies({}) +[PASS] get_case_studies({"query":"smart buildings"}) +[PASS] get_insights({"scenario":"personal health digital twin","tier":"free"}) +[PASS] get_methodology_step({"phase":"concurrent-engineering"}) +=== Results === +Passed: 8/8 +Failed: 0/8 +All tools working. Your LPI Sandbox is ready. + +### Step 2: Installed Ollama — Model: qwen2.5:1.5b + +Command: `ollama run qwen2.5:1.5b "What is the SMILE methodology in digital twins?"` + +LLM Output: +The SMILE methodology stands for Simulation, Model, Input, Output, +Execution — used in digital twin development to help organizations +gain insight into their systems. It involves simulation, modeling, +input identification, output generation, and execution of real-world +scenarios. + +### Step 3: What Surprised Me About SMILE + +1. The local LLM incorrectly expanded SMILE as "Simulation, Model, +Input, Output, Execution" — proving that general-purpose models +hallucinate domain-specific knowledge about digital twins. + +2. The actual SMILE methodology (Sustainable Methodology for Impact +Lifecycle Enablement) focuses on 6 structured phases — which the +LPI tools explained far more accurately than the LLM. + +3. This proved why grounding AI agents with domain-specific tools +like LPI is critical — LLMs alone cannot be trusted for specialized +digital twin knowledge without retrieval-augmented context. + +## Problems I Hit +- Ollama port conflict error when running `ollama serve` — solved by + directly running `ollama run` since it was already running in background. + +## Model Choice +Used qwen2.5:1.5b — lightweight model, runs locally without GPU, no API key needed. \ No newline at end of file diff --git a/submissions/varshit-pratap-singh-bhadauria/level 2/level2.md b/submissions/varshit-pratap-singh-bhadauria/level 2/level2.md new file mode 100644 index 000000000..24069704f --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level 2/level2.md @@ -0,0 +1,36 @@ +# Level 2 Submission — Varshit Pratap Singh Bhadauria + +## Test Client Output +=== LPI Sandbox Test Client === +[PASS] smile_overview({}) +[PASS] smile_phase_detail({"phase":"reality-emulation"}) +[PASS] list_topics({}) +[PASS] query_knowledge({"query":"explainable AI"}) +[PASS] get_case_studies({}) +[PASS] get_case_studies({"query":"smart buildings"}) +[PASS] get_insights({"scenario":"personal health digital twin","tier":"free"}) +[PASS] get_methodology_step({"phase":"concurrent-engineering"}) +Passed: 8/8 | Failed: 0/8 +All tools working. Your LPI Sandbox is ready. + +## LLM Output +Model: qwen2.5:1.5b +Command: ollama run qwen2.5:1.5b "What is the SMILE methodology in digital twins?" + +The SMILE methodology stands for Simulation, Model, Input, Output, +Execution and it is an approach used in digital twin development to +help organizations gain insight into their systems or processes. + +## 3 Things That Surprised Me About SMILE +1. The local LLM completely hallucinated SMILE's definition — calling +it "Simulation, Model, Input, Output, Execution" when it actually +stands for Sustainable Methodology for Impact Lifecycle Enablement, +proving LLMs cannot be trusted for domain-specific knowledge. + +2. SMILE has 6 structured phases for digital twin implementation — +far more comprehensive than I expected, covering everything from +Reality Emulation to full lifecycle management. + +3. Grounding AI agents with domain-specific tools like LPI is +critical — without retrieval-augmented context, even capable LLMs +produce confident but completely wrong answers about digital twins. \ No newline at end of file diff --git a/submissions/varshit-pratap-singh-bhadauria/level5/answers.md b/submissions/varshit-pratap-singh-bhadauria/level5/answers.md new file mode 100644 index 000000000..fcbe12e5d --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level5/answers.md @@ -0,0 +1,80 @@ +# Level 5: Graph Thinking Answers + +## Q1. Model It (20 pts) +Here is the graph schema capturing the relationships between the factory entities based on the production, capacity, and worker data. + +**6 Node Labels:** +1. Project +2. Product +3. Station +4. Worker +5. Week +6. Etapp + +**8 Relationship Types:** +1. (Project)-[:PRODUCES {quantity, unit_factor}]->(Product) +2. (Project)-[:SCHEDULED_AT {planned_hours, actual_hours}]->(Station) +3. (Week)-[:HAS_CAPACITY {total_capacity, total_planned, deficit}]->(Station) +4. (Project)-[:BELONGS_TO]->(Etapp) +5. (Project)-[:ACTIVE_IN]->(Week) +6. (Worker)-[:PRIMARY_STATION]->(Station) +7. (Worker)-[:CAN_COVER]->(Station) +8. (Product)-[:REQUIRES]->(Station) + +### Visual Diagram: +```mermaid +graph TD + Project((Project)) + Product((Product)) + Station((Station)) + Worker((Worker)) + Week((Week)) + Etapp((Etapp)) + + Project -- "PRODUCES {quantity, unit_factor}" --> Product + Project -- "SCHEDULED_AT {planned_hours, actual_hours}" --> Station + Project -- "BELONGS_TO" --> Etapp + Project -- "ACTIVE_IN" --> Week + Worker -- "PRIMARY_STATION" --> Station + Worker -- "CAN_COVER" --> Station + Week -- "HAS_CAPACITY {total_capacity, total_planned, deficit}" --> Station + Product -- "REQUIRES" --> Station + +Q2. Why Not Just SQL? (20 pts) +SQL Version: +SELECT w.name, p.project_name +FROM workers w +JOIN worker_coverage wc ON w.worker_id = wc.worker_id +JOIN stations s ON wc.station_code = s.station_code +JOIN production pr ON s.station_code = pr.station_code +JOIN projects p ON pr.project_id = p.project_id +WHERE s.station_code = '016' AND w.name != 'Per Gustafsson'; +Cypher Version: +MATCH (w:Worker)-[:CAN_COVER]->(s:Station {station_code: '016'})<-[:SCHEDULED_AT]-(p:Project) +WHERE w.name <> 'Per Gustafsson' +RETURN w.name AS Backup_Worker, p.project_name AS Affected_Project +Why the graph version is better: The graph version makes it immediately obvious how workers, stations, and projects are connected without requiring expensive mapping tables or complex JOIN logic. In Cypher, you visually draw the path (Worker -> Station <- Project). + +Q3. Spot the Bottleneck (20 pts) +Identifying the overload: According to the capacity data, Weeks 1 and 2 have the largest capacity deficits (-132 and -125 hours). The production data shows the 011 FS IQB and 012 Förmontering IQB stations have highly concurrent schedules across almost all projects during these weeks, causing the massive bottleneck. +Cypher Query: +MATCH (p:Project)-[r:SCHEDULED_AT]->(s:Station) +WHERE r.actual_hours > (r.planned_hours * 1.10) +RETURN s.station_name, collect(p.project_name) AS Over_Budget_Projects +Modeling this alert: We can add a status: 'Bottleneck' property directly to the [:SCHEDULED_AT] relationship when actual hours exceed planned capacity, or create a dynamic (:Bottleneck) node linked to the specific (Station) and (Week). +For example, in Week 1, the factory had a capacity deficit of -132 hours. A major bottleneck was Station 014 (Svets o montage), where multiple projects went over their scheduled time. Project P03 planned for 42.0 hours but actually took 48.0 hours, and Project P08 planned for 40.0 hours but actually took 44.0 hours. + +Q4. Vector + Graph Hybrid (20 pts) +What to embed: Embed the unstructured text of the "project descriptions" and "product requirements". +Hybrid Query: +CALL db.index.vector.queryNodes('project_embeddings', 5, $search_embedding) YIELD node AS p, score +MATCH (p)-[r:SCHEDULED_AT]->(s:Station) +WHERE ((r.actual_hours - r.planned_hours) / r.planned_hours) < 0.05 +RETURN p.project_name, score, s.station_name +Why this is more useful: Keyword filtering is rigid. Vector search understands the semantic meaning to find similar past scopes (e.g., matching "hospital" to the existing "Sjukhus Linköping" project), while the graph enforces hard factual constraints (like ensuring historical variance is < 5%). + +Q5. Your L6 Plan (20 pts) +Node Labels: Project, Product, Station, Worker, Week +Relationship Types: (Project)-[:PRODUCES]->(Product) (Project)-[:SCHEDULED_AT]->(Station) (Worker)-[:WORKS_AT]->(Station) (Worker)-[:CAN_COVER]->(Station) (Week)-[:HAS_CAPACITY]->(Station) +Dashboard Panels: Project Overview: A table showing all projects with planned vs actual hours. Station Load: An interactive Plotly bar chart visualizing hours per station. Worker Coverage: A matrix showing which workers can cover which stations. +Cypher Queries: Project Overview: MATCH (p:Project)-[r:SCHEDULED_AT]->(s:Station) RETURN p.project_name, sum(r.planned_hours), sum(r.actual_hours) Station Load: MATCH (s:Station)<-[r:SCHEDULED_AT]-(p:Project) RETURN s.station_name, r.week, r.planned_hours, r.actual_hours Worker Coverage: MATCH (w:Worker)-[:CAN_COVER]->(s:Station) RETURN w.name, collect(s.station_name) diff --git a/submissions/varshit-pratap-singh-bhadauria/level5/schema.png b/submissions/varshit-pratap-singh-bhadauria/level5/schema.png new file mode 100644 index 000000000..a3afe831b Binary files /dev/null and b/submissions/varshit-pratap-singh-bhadauria/level5/schema.png differ diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/.gitignore b/submissions/varshit-pratap-singh-bhadauria/level6/.gitignore new file mode 100644 index 000000000..4c49bd78f --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/.gitignore @@ -0,0 +1 @@ +.env diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/DASHBOARD_URL.txt b/submissions/varshit-pratap-singh-bhadauria/level6/DASHBOARD_URL.txt new file mode 100644 index 000000000..3a71d280b --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/DASHBOARD_URL.txt @@ -0,0 +1 @@ +https://lpi-developer-kit-l6.streamlit.app/ diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/README.md b/submissions/varshit-pratap-singh-bhadauria/level6/README.md new file mode 100644 index 000000000..cc337476a --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/README.md @@ -0,0 +1,15 @@ +# Level 6: Factory Knowledge Graph Dashboard + +This project is a Streamlit dashboard powered by a Neo4j knowledge graph. It replaces a 46-sheet Excel workbook for a steel fabrication company. + +## Files Included: +- `seed_graph.py`: Standalone, idempotent script used to parse CSV data and populate the Neo4j Aura cloud database. +- `app.py`: The Streamlit application containing the dashboard UI and Cypher queries. +- `DASHBOARD_URL.txt`: Contains the public link to the deployed Streamlit Cloud dashboard. + +## Dashboard Features: +- Project Overview +- Station Load Visualization +- Capacity Tracker +- Worker Coverage Matrix +- Automated Self-Test Page diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/app.py b/submissions/varshit-pratap-singh-bhadauria/level6/app.py new file mode 100644 index 000000000..62a29baca --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/app.py @@ -0,0 +1,155 @@ +import streamlit as st +import pandas as pd +import plotly.express as px +from neo4j import GraphDatabase + +# Using your local Neo4j Desktop credentials +URI = st.secrets["NEO4J_URI"] +USERNAME = st.secrets["NEO4J_USERNAME"] +PASSWORD = st.secrets["NEO4J_PASSWORD"] # <--- PUT YOUR NEO4J DESKTOP PASSWORD HERE + +# Connect to Neo4j +@st.cache_resource +def get_db_driver(): + return GraphDatabase.driver(URI, auth=(USERNAME, PASSWORD)) + +driver = get_db_driver() + +def run_query(query): + with driver.session() as session: + result = session.run(query) + # Handle empty results gracefully + if not result.peek(): + return pd.DataFrame() + return pd.DataFrame([r.values() for r in result], columns=result.keys()) + +# --- Sidebar Navigation --- +st.sidebar.title("Factory Dashboard") +page = st.sidebar.radio("Go to", ["Project Overview", "Station Load", "Capacity Tracker", "Worker Coverage", "Self-Test"]) + +# --- Page 1: Project Overview --- +if page == "Project Overview": + st.title("Project Overview") + query = """ + MATCH (p:Project)-[sched:SCHEDULED_AT]->(s:Station) + OPTIONAL MATCH (p)-[:PRODUCES]->(prod:Product) + RETURN p.name AS Project, + sum(sched.planned_hours) AS Total_Planned, + sum(sched.actual_hours) AS Total_Actual, + collect(DISTINCT prod.type) AS Products + """ + df = run_query(query) + if not df.empty: + df['Variance %'] = ((df['Total_Actual'] - df['Total_Planned']) / df['Total_Planned'] * 100).round(2) + st.dataframe(df) + else: + st.write("No data found.") + +# --- Page 2: Station Load --- +elif page == "Station Load": + st.title("Station Load") + query = """ + MATCH (p:Project)-[sched:SCHEDULED_AT]->(s:Station) + RETURN s.name AS Station, sched.week AS Week, + sum(sched.planned_hours) AS Planned, + sum(sched.actual_hours) AS Actual + """ + df = run_query(query) + if not df.empty: + # Highlight where actual > planned + df['Overloaded'] = df['Actual'] > df['Planned'] + + # Interactive Plotly Chart + fig = px.bar(df, x="Station", y=["Planned", "Actual"], barmode="group", + color="Overloaded", color_discrete_map={True: 'red', False: 'green'}, + title="Planned vs Actual Hours per Station") + st.plotly_chart(fig) + +# --- Page 3: Capacity Tracker --- +elif page == "Capacity Tracker": + st.title("Capacity Tracker") + query = """ + MATCH (wk:Week)-[hc:HAS_CAPACITY]->(c:Capacity) + RETURN wk.id AS Week, + (hc.own + hc.hired + hc.overtime) AS Total_Capacity, + hc.deficit AS Deficit + ORDER BY Week + """ + df = run_query(query) + if not df.empty: + # Display deficit weeks in red using Streamlit styling + def color_deficit(val): + color = 'red' if val < 0 else 'green' + return f'color: {color}' + st.dataframe(df.style.map(color_deficit, subset=['Deficit'])) + +# --- Page 4: Worker Coverage --- +elif page == "Worker Coverage": + st.title("Worker Coverage") + query = """ + MATCH (w:Worker)-[:CAN_COVER]->(s:Station) + WITH s, count(w) as Worker_Count, collect(w.name) as Workers + RETURN s.name AS Station, Worker_Count, Workers + ORDER BY Worker_Count ASC + """ + df = run_query(query) + if not df.empty: + # Highlight Single Point of Failure (Worker_Count == 1) + def highlight_spof(val): + color = 'red' if val == 1 else '' + return f'background-color: {color}' + st.markdown("**Stations in RED have only 1 certified worker (Single Point of Failure)!**") + st.dataframe(df.style.map(highlight_spof, subset=['Worker_Count'])) + +### --- Page 5: Self-Test (Mandatory) --- +elif page == "Self-Test": + st.title("Self-Test") + st.markdown("Running automated checks...") + + # 1. Check Projects (Min 8) + df_proj = run_query("MATCH (n:Project) RETURN count(n) as count") + proj_count = int(df_proj['count'].sum()) if not df_proj.empty else 0 + if proj_count >= 8: + st.success(f"✅ Projects: {proj_count} (Min 8)") + else: + st.error(f"❌ Projects: {proj_count} (Min 8)") + + # 2. Check Products (Min 7) + df_prod = run_query("MATCH (n:Product) RETURN count(n) as count") + prod_count = int(df_prod['count'].sum()) if not df_prod.empty else 0 + if prod_count >= 7: + st.success(f"✅ Products: {prod_count} (Min 7)") + else: + st.error(f"❌ Products: {prod_count} (Min 7)") + + # 3. Check Stations (Min 9) + df_stat = run_query("MATCH (n:Station) RETURN count(n) as count") + stat_count = int(df_stat['count'].sum()) if not df_stat.empty else 0 + if stat_count >= 9: + st.success(f"✅ Stations: {stat_count} (Min 9)") + else: + st.error(f"❌ Stations: {stat_count} (Min 9)") + + # 4. Check Workers (Min 13) + df_work = run_query("MATCH (n:Worker) RETURN count(n) as count") + work_count = int(df_work['count'].sum()) if not df_work.empty else 0 + if work_count >= 13: + st.success(f"✅ Workers: {work_count} (Min 13)") + else: + st.error(f"❌ Workers: {work_count} (Min 13)") + + # 5. Check Weeks (Min 8) + df_week = run_query("MATCH (n:Week) RETURN count(n) as count") + week_count = int(df_week['count'].sum()) if not df_week.empty else 0 + if week_count >= 8: + st.success(f"✅ Weeks: {week_count} (Min 8)") + else: + st.error(f"❌ Weeks: {week_count} (Min 8)") + + # 6. Check Etapps (Min 2) + df_etapp = run_query("MATCH (n:Etapp) RETURN count(n) as count") + etapp_count = int(df_etapp['count'].sum()) if not df_etapp.empty else 0 + if etapp_count >= 2: + st.success(f"✅ Etapps: {etapp_count} (Min 2)") + else: + st.error(f"❌ Etapps: {etapp_count} (Min 2)") diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/factory_capacity.csv b/submissions/varshit-pratap-singh-bhadauria/level6/factory_capacity.csv new file mode 100644 index 000000000..795ff52f0 --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/factory_capacity.csv @@ -0,0 +1,9 @@ +week,own_staff_count,hired_staff_count,own_hours,hired_hours,overtime_hours,total_capacity,total_planned,deficit +w1,10,2,400,80,0,480,612,-132 +w2,10,2,400,80,40,520,645,-125 +w3,10,2,400,80,0,480,398,82 +w4,10,2,400,80,20,500,550,-50 +w5,10,2,400,80,30,510,480,30 +w6,9,2,360,80,0,440,520,-80 +w7,10,2,400,80,40,520,600,-80 +w8,10,2,400,80,20,500,470,30 \ No newline at end of file diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/factory_production.csv b/submissions/varshit-pratap-singh-bhadauria/level6/factory_production.csv new file mode 100644 index 000000000..ca6ce43e1 --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/factory_production.csv @@ -0,0 +1,69 @@ +project_id,project_number,project_name,product_type,unit,quantity,unit_factor,station_code,station_name,etapp,bop,week,planned_hours,actual_hours,completed_units +P01,4501,Stålverket Borås,IQB,meter,600,1.77,011,FS IQB,ET1,BOP1,w1,48.0,45.2,28 +P01,4501,Stålverket Borås,IQB,meter,600,1.77,012,Förmontering IQB,ET1,BOP1,w1,32.0,35.5,25 +P01,4501,Stålverket Borås,IQB,meter,600,1.77,013,Montering IQB,ET1,BOP1,w1,28.0,26.0,22 +P01,4501,Stålverket Borås,IQB,meter,600,1.77,014,Svets o montage IQB,ET1,BOP1,w1,35.0,38.2,20 +P01,4501,Stålverket Borås,SB,styck,40,4.0,018,SB B/F-hall,ET1,BOP1,w1,16.0,14.5,4 +P01,4501,Stålverket Borås,SP,styck,180,2.0,019,SP B/F-hall,ET1,BOP1,w1,12.0,13.0,7 +P01,4501,Stålverket Borås,IQB,meter,600,1.77,011,FS IQB,ET1,BOP1,w2,48.0,50.0,32 +P01,4501,Stålverket Borås,IQB,meter,600,1.77,012,Förmontering IQB,ET1,BOP1,w2,32.0,30.0,28 +P01,4501,Stålverket Borås,IQP,styck,90,2.80,015,Montering IQP,ET1,BOP2,w2,25.0,28.0,9 +P01,4501,Stålverket Borås,SR,styck,8,45.0,021,SR B/F-hall,ET1,BOP2,w2,40.0,42.0,1 +P02,4502,Kontorshus Mölndal,IQB,meter,350,1.50,011,FS IQB,ET1,BOP1,w1,30.0,28.0,20 +P02,4502,Kontorshus Mölndal,IQB,meter,350,1.50,012,Förmontering IQB,ET1,BOP1,w1,22.0,24.5,18 +P02,4502,Kontorshus Mölndal,IQB,meter,350,1.50,013,Montering IQB,ET1,BOP1,w1,18.0,17.0,16 +P02,4502,Kontorshus Mölndal,IQP,styck,70,2.70,015,Montering IQP,ET1,BOP1,w1,19.0,21.0,7 +P02,4502,Kontorshus Mölndal,SD,styck,30,3.00,018,SB B/F-hall,ET1,BOP1,w1,9.0,8.5,3 +P02,4502,Kontorshus Mölndal,IQB,meter,350,1.50,011,FS IQB,ET1,BOP1,w2,30.0,32.0,24 +P02,4502,Kontorshus Mölndal,IQB,meter,350,1.50,014,Svets o montage IQB,ET1,BOP1,w2,25.0,23.0,20 +P02,4502,Kontorshus Mölndal,SP,styck,120,1.75,019,SP B/F-hall,ET1,BOP2,w2,14.0,15.5,8 +P03,4503,Lagerhall Jönköping,IQB,meter,900,1.89,011,FS IQB,ET1,BOP1,w1,72.0,70.0,40 +P03,4503,Lagerhall Jönköping,IQB,meter,900,1.89,012,Förmontering IQB,ET1,BOP1,w1,48.0,52.0,35 +P03,4503,Lagerhall Jönköping,IQB,meter,900,1.89,013,Montering IQB,ET1,BOP1,w1,38.0,36.5,30 +P03,4503,Lagerhall Jönköping,IQB,meter,900,1.89,014,Svets o montage IQB,ET1,BOP1,w1,42.0,48.0,28 +P03,4503,Lagerhall Jönköping,SB,styck,60,6.00,018,SB B/F-hall,ET1,BOP1,w1,36.0,38.0,6 +P03,4503,Lagerhall Jönköping,IQB,meter,900,1.89,011,FS IQB,ET1,BOP1,w2,72.0,75.0,45 +P03,4503,Lagerhall Jönköping,IQP,styck,110,2.90,015,Montering IQP,ET1,BOP2,w2,32.0,30.0,11 +P03,4503,Lagerhall Jönköping,IQB,meter,900,1.89,016,Gjutning,ET1,BOP2,w2,28.0,35.0,8 +P03,4503,Lagerhall Jönköping,IQB,meter,900,1.89,017,Målning,ET1,BOP2,w3,24.0,22.0,20 +P04,4504,Parkering Helsingborg,IQB,meter,450,1.65,011,FS IQB,ET1,BOP1,w1,38.0,36.0,24 +P04,4504,Parkering Helsingborg,IQB,meter,450,1.65,012,Förmontering IQB,ET1,BOP1,w1,25.0,27.0,20 +P04,4504,Parkering Helsingborg,IQB,meter,450,1.65,013,Montering IQB,ET1,BOP1,w1,20.0,19.0,18 +P04,4504,Parkering Helsingborg,IQP,styck,55,2.85,015,Montering IQP,ET1,BOP1,w1,16.0,18.0,6 +P04,4504,Parkering Helsingborg,SB,styck,25,7.50,018,SB B/F-hall,ET1,BOP1,w1,19.0,22.0,3 +P04,4504,Parkering Helsingborg,IQB,meter,450,1.65,011,FS IQB,ET1,BOP1,w2,38.0,40.0,28 +P04,4504,Parkering Helsingborg,SP,styck,100,2.00,019,SP B/F-hall,ET1,BOP2,w2,12.0,11.0,6 +P04,4504,Parkering Helsingborg,SR,styck,12,120.0,021,SR B/F-hall,ET1,BOP2,w2,60.0,65.0,1 +P05,4505,Sjukhus Linköping ET2,IQB,meter,1200,1.85,011,FS IQB,ET2,BOP3,w1,95.0,90.0,50 +P05,4505,Sjukhus Linköping ET2,IQB,meter,1200,1.85,012,Förmontering IQB,ET2,BOP3,w1,65.0,68.0,42 +P05,4505,Sjukhus Linköping ET2,IQB,meter,1200,1.85,013,Montering IQB,ET2,BOP3,w1,50.0,48.0,38 +P05,4505,Sjukhus Linköping ET2,IQB,meter,1200,1.85,014,Svets o montage IQB,ET2,BOP3,w1,58.0,62.0,35 +P05,4505,Sjukhus Linköping ET2,IQP,styck,150,2.88,015,Montering IQP,ET2,BOP3,w1,30.0,33.0,10 +P05,4505,Sjukhus Linköping ET2,SB,styck,50,5.00,018,SB B/F-hall,ET2,BOP3,w1,25.0,28.0,5 +P05,4505,Sjukhus Linköping ET2,SD,styck,45,2.75,018,SB B/F-hall,ET2,BOP3,w1,12.0,11.5,4 +P05,4505,Sjukhus Linköping ET2,IQB,meter,1200,1.85,011,FS IQB,ET2,BOP3,w2,95.0,98.0,55 +P05,4505,Sjukhus Linköping ET2,IQB,meter,1200,1.85,016,Gjutning,ET2,BOP3,w2,35.0,40.0,12 +P05,4505,Sjukhus Linköping ET2,IQB,meter,1200,1.85,017,Målning,ET2,BOP3,w2,28.0,26.0,25 +P05,4505,Sjukhus Linköping ET2,SR,styck,20,274.0,021,SR B/F-hall,ET2,BOP3,w3,120.0,115.0,2 +P06,4506,Skola Uppsala,IQB,meter,500,1.60,011,FS IQB,ET1,BOP1,w2,40.0,38.0,26 +P06,4506,Skola Uppsala,IQB,meter,500,1.60,012,Förmontering IQB,ET1,BOP1,w2,28.0,30.0,22 +P06,4506,Skola Uppsala,IQB,meter,500,1.60,013,Montering IQB,ET1,BOP1,w2,22.0,20.0,18 +P06,4506,Skola Uppsala,IQP,styck,80,2.75,015,Montering IQP,ET1,BOP1,w2,22.0,24.0,8 +P06,4506,Skola Uppsala,SB,styck,35,4.50,018,SB B/F-hall,ET1,BOP1,w2,16.0,18.0,4 +P06,4506,Skola Uppsala,SP,styck,140,1.50,019,SP B/F-hall,ET1,BOP2,w3,14.0,12.0,10 +P07,4507,Idrottshall Västerås,HSQ,meter,400,2.05,011,FS IQB,ET1,BOP1,w1,45.0,42.0,22 +P07,4507,Idrottshall Västerås,HSQ,meter,400,2.05,012,Förmontering IQB,ET1,BOP1,w1,30.0,33.0,18 +P07,4507,Idrottshall Västerås,HSQ,meter,400,2.05,014,Svets o montage IQB,ET1,BOP1,w1,35.0,32.0,16 +P07,4507,Idrottshall Västerås,SB,styck,45,3.50,018,SB B/F-hall,ET1,BOP1,w1,16.0,18.0,5 +P07,4507,Idrottshall Västerås,HSQ,meter,400,2.05,011,FS IQB,ET1,BOP1,w2,45.0,48.0,26 +P07,4507,Idrottshall Västerås,HSQ,meter,400,2.05,016,Gjutning,ET1,BOP2,w2,20.0,22.0,5 +P07,4507,Idrottshall Västerås,HSQ,meter,400,2.05,017,Målning,ET1,BOP2,w3,18.0,16.0,15 +P08,4508,Bro E6 Halmstad,IQB,meter,800,1.80,011,FS IQB,ET1,BOP1,w1,65.0,62.0,36 +P08,4508,Bro E6 Halmstad,IQB,meter,800,1.80,012,Förmontering IQB,ET1,BOP1,w1,42.0,45.0,30 +P08,4508,Bro E6 Halmstad,IQB,meter,800,1.80,013,Montering IQB,ET1,BOP1,w1,35.0,38.0,25 +P08,4508,Bro E6 Halmstad,IQB,meter,800,1.80,014,Svets o montage IQB,ET1,BOP1,w1,40.0,44.0,22 +P08,4508,Bro E6 Halmstad,SP,styck,200,2.50,019,SP B/F-hall,ET1,BOP1,w1,20.0,18.0,8 +P08,4508,Bro E6 Halmstad,IQB,meter,800,1.80,011,FS IQB,ET1,BOP1,w2,65.0,68.0,42 +P08,4508,Bro E6 Halmstad,IQP,styck,95,2.93,015,Montering IQP,ET1,BOP2,w2,28.0,30.0,10 +P08,4508,Bro E6 Halmstad,IQB,meter,800,1.80,016,Gjutning,ET1,BOP2,w3,22.0,25.0,8 +P08,4508,Bro E6 Halmstad,SR,styck,15,180.0,021,SR B/F-hall,ET1,BOP2,w3,90.0,85.0,2 \ No newline at end of file diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/factory_workers.csv b/submissions/varshit-pratap-singh-bhadauria/level6/factory_workers.csv new file mode 100644 index 000000000..3110285cc --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/factory_workers.csv @@ -0,0 +1,15 @@ +worker_id,name,role,primary_station,can_cover_stations,certifications,hours_per_week,type +W01,Erik Lindberg,Operator,011,"011,012","MIG/MAG,TIG,ISO 9606",40,permanent +W02,Anna Berg,Operator,011,"011,014","MIG/MAG,TIG",40,permanent +W03,Lars Jensen,Operator,012,"012,013","Surface treatment,CE marking",40,permanent +W04,Maria Stone,Operator,013,"013","Blasting,Surface protection",40,permanent +W05,Johan Peters,Operator,014,"014,015","Hydraulics,Mechanics,Crane",40,permanent +W06,Karen Nilsen,Inspector,015,"015","SIS,SS-EN 1090,NDT",40,permanent +W07,Per Hansen,Operator,016,"016,017","Casting,Formwork",40,permanent +W08,Sofia Arden,Operator,017,"017","Surface treatment,Spray painting",40,permanent +W09,Magnus Stone,Operator,018,"018,019","Sheet metal,Assembly",40,permanent +W10,Elin Frank,Operator,019,"019,018","Assembly,Welding",32,permanent +W11,Victor Elm,Foreman,all,"011,012,013,014,015,016,017,018,019,021","Leadership,CE,ISO 9001",45,permanent +W12,Lena Dale,Quality Manager,015,"015","ISO 9001,SS-EN 1090,Audit",40,permanent +W13,Ahmed Hassan,Operator,011,"011","MIG/MAG",40,hired +W14,Petra Steen,Operator,012,"012,013","Surface treatment",40,hired \ No newline at end of file diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/requirements.txt b/submissions/varshit-pratap-singh-bhadauria/level6/requirements.txt new file mode 100644 index 000000000..f8b5658f7 --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/requirements.txt @@ -0,0 +1,4 @@ +streamlit +neo4j +pandas +plotly \ No newline at end of file diff --git a/submissions/varshit-pratap-singh-bhadauria/level6/seed_graph.py b/submissions/varshit-pratap-singh-bhadauria/level6/seed_graph.py new file mode 100644 index 000000000..b3a0ed071 --- /dev/null +++ b/submissions/varshit-pratap-singh-bhadauria/level6/seed_graph.py @@ -0,0 +1,96 @@ +from neo4j import GraphDatabase +import pandas as pd +import os +from dotenv import load_dotenv + +# Load the hidden credentials from the .env file +load_dotenv() + +URI = os.getenv("NEO4J_URI") +USERNAME = os.getenv("NEO4J_USERNAME") +PASSWORD = os.getenv("NEO4J_PASSWORD") + +# Connect to the Neo4j database +driver = GraphDatabase.driver(URI, auth=(USERNAME, PASSWORD)) + +def create_constraints(tx): + # The scoring guide requires setting uniqueness constraints + tx.run("CREATE CONSTRAINT IF NOT EXISTS FOR (p:Project) REQUIRE p.id IS UNIQUE") + tx.run("CREATE CONSTRAINT IF NOT EXISTS FOR (s:Station) REQUIRE s.code IS UNIQUE") + tx.run("CREATE CONSTRAINT IF NOT EXISTS FOR (w:Worker) REQUIRE w.id IS UNIQUE") + tx.run("CREATE CONSTRAINT IF NOT EXISTS FOR (wk:Week) REQUIRE wk.id IS UNIQUE") + +def load_data(tx): + # 2. Load Production Data (Projects, Products, Stations, Etapp, BOP, Weeks) + prod_df = pd.read_csv('factory_production.csv').fillna('') + query_prod = """ + UNWIND $rows AS row + MERGE (p:Project {id: row.project_id}) + ON CREATE SET p.name = row.project_name + + MERGE (prod:Product {type: row.product_type}) + MERGE (p)-[pr:PRODUCES]->(prod) + ON CREATE SET pr.quantity = toFloat(row.quantity), pr.unit_factor = toFloat(row.unit_factor) + + MERGE (s:Station {code: row.station_code}) + ON CREATE SET s.name = row.station_name + + MERGE (wk:Week {id: row.week}) + + MERGE (e:Etapp {name: row.etapp}) + MERGE (p)-[:HAS_PHASE]->(e) + + // We only attach BOP if the row has one + WITH p, s, wk, row + WHERE row.bop <> '' + MERGE (b:BOP {name: row.bop}) + MERGE (s)-[:PART_OF_BOP]->(b) + + MERGE (p)-[sched:SCHEDULED_AT {week: row.week}]->(s) + ON CREATE SET sched.planned_hours = toFloat(row.planned_hours), + sched.actual_hours = toFloat(row.actual_hours) + """ + tx.run(query_prod, rows=prod_df.to_dict('records')) + + # 3. Load Workers Data + workers_df = pd.read_csv('factory_workers.csv').fillna('') + query_workers = """ + UNWIND $rows AS row + MERGE (w:Worker {id: row.worker_id}) + ON CREATE SET w.name = row.name, w.role = row.role + + WITH w, row + MERGE (ps:Station {code: row.primary_station}) + MERGE (w)-[:WORKS_AT]->(ps) + + WITH w, row + // Split the comma-separated list to create multiple CAN_COVER relationships + UNWIND split(row.can_cover_stations, ',') AS cover_code + MERGE (cs:Station {code: trim(cover_code)}) + MERGE (w)-[:CAN_COVER]->(cs) + """ + tx.run(query_workers, rows=workers_df.to_dict('records')) + + # 4. Load Capacity Data + cap_df = pd.read_csv('factory_capacity.csv').fillna('') + query_capacity = """ + UNWIND $rows AS row + MERGE (wk:Week {id: row.week}) + MERGE (c:Capacity {id: row.week + '_cap'}) + MERGE (wk)-[hc:HAS_CAPACITY]->(c) + ON CREATE SET hc.own = toFloat(row.own_hours), + hc.hired = toFloat(row.hired_hours), + hc.overtime = toFloat(row.overtime_hours), + hc.deficit = toFloat(row.deficit) + """ + tx.run(query_capacity, rows=cap_df.to_dict('records')) + +# Execute everything +with driver.session() as session: + print("Creating database constraints...") + session.execute_write(create_constraints) + print("Loading all CSV data into Neo4j Graph...") + session.execute_write(load_data) + print("✅ Graph seeding complete! You just earned 20 points.") + +driver.close() \ No newline at end of file