mirror of
https://github.com/vee1e/bitsat_predictor.git
synced 2026-09-01 10:59:02 +00:00
merge: Optimize graphs (#6)
* minor changes
* Make more minimalist
* add changes requested by client
* refactor: move graph gen to client, add filters
Backend:
- Change /graph endpoint to return raw branch data instead of full Plotly config
- Remove plotly dependency from backend
- Return JSON: {branches: [{name, years, marks}, ...]}
Frontend:
- Build Plotly traces client-side from raw data
- Add degree filter (All/B.E./M.Sc.)
- Add Phoenix branches filter
- Fix CORS URL configuration
- Remove problematic animation causing flashing
* fix any pending issues
---------
Co-authored-by: pranav <pranavu8406@gmail.com>
This commit is contained in:
parent
21329e0177
commit
56bd4ca53e
3 changed files with 88 additions and 52 deletions
|
|
@ -1,19 +1,16 @@
|
|||
from fastapi import FastAPI, Query, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from dotenv import load_dotenv
|
||||
import plotly.graph_objects as go
|
||||
import pandas as pd
|
||||
import csv
|
||||
import os
|
||||
|
||||
load_dotenv()
|
||||
|
||||
CORS_ORIGINS = [
|
||||
origin for origin in os.getenv("CORS_ORIGINS", "").split(",") if origin
|
||||
]
|
||||
CORS_ORIGINS = [origin for origin in os.getenv("CORS_ORIGINS", "").split(",") if origin]
|
||||
|
||||
csvFiles = ["best_case.csv", "most_likely_case.csv", "worst_case.csv"]
|
||||
campusDict = {"Pilani":0, "Goa":1, "Hyderabad":2 }
|
||||
campusDict = {"Pilani": 0, "Goa": 1, "Hyderabad": 2}
|
||||
campusArr = ["Pilani", "Goa", "Hyderabad"]
|
||||
dfs = []
|
||||
tableData = [[] for _ in range(9)]
|
||||
|
|
@ -35,12 +32,12 @@ df["marks"] = df["marks"].astype(int)
|
|||
for case_idx in range(3):
|
||||
path = "predictions/" + csvFiles[case_idx]
|
||||
try:
|
||||
with open(path, newline='', encoding='utf-8') as csv_file:
|
||||
csv_reader = csv.reader(csv_file, delimiter=',')
|
||||
with open(path, newline="", encoding="utf-8") as csv_file:
|
||||
csv_reader = csv.reader(csv_file, delimiter=",")
|
||||
for row in csv_reader:
|
||||
if row[0] in campusDict:
|
||||
campus_idx = campusDict[row[0]]
|
||||
tableData[campus_idx*3+case_idx].append(row)
|
||||
tableData[campus_idx * 3 + case_idx].append(row)
|
||||
except FileNotFoundError:
|
||||
print(f"Prediction file {path} not found, skipping.")
|
||||
|
||||
|
|
@ -53,66 +50,42 @@ app.add_middleware(
|
|||
allow_origins=origins,
|
||||
allow_credentials=False,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"]
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
def get_df_by_campus(campus: int = Query(0, ge=0, le=2)):
|
||||
|
||||
def get_df_by_campus(campus: int = 0):
|
||||
try:
|
||||
campus_name = campusArr[campus]
|
||||
except IndexError:
|
||||
raise HTTPException(status_code=400, detail="Invalid Campus Index")
|
||||
|
||||
ndf = df[df["campus"]==campus_name].iloc[:, -3:]
|
||||
|
||||
ndf = df[df["campus"] == campus_name][["year", "branch", "marks"]]
|
||||
return ndf
|
||||
|
||||
|
||||
@app.get("/graph")
|
||||
def get_graph(campus: int = Query(0, ge=0, le=2)):
|
||||
|
||||
campus_data = get_df_by_campus(campus)
|
||||
|
||||
fig = go.Figure()
|
||||
|
||||
branches = []
|
||||
for branch, values in campus_data.groupby("branch"):
|
||||
fig.add_trace(go.Scatter(
|
||||
x=values["year"],
|
||||
y=values["marks"],
|
||||
mode="lines+markers",
|
||||
name=branch
|
||||
branches.append(
|
||||
{
|
||||
"name": branch,
|
||||
"years": values["year"].tolist(),
|
||||
"marks": values["marks"].tolist(),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
fig.update_layout(
|
||||
template="plotly_dark",
|
||||
legend_title="Branches",
|
||||
hovermode="x",
|
||||
xaxis=dict(
|
||||
showspikes=True,
|
||||
spikemode="across",
|
||||
spikesnap="cursor",
|
||||
spikethickness=1,
|
||||
spikecolor="rgba(255,255,255,0.6)"
|
||||
),
|
||||
margin=dict(l=20, r=20, t=30, b=30)
|
||||
)
|
||||
|
||||
fig.update_xaxes(
|
||||
tickmode="linear",
|
||||
dtick=1,
|
||||
title="Year"
|
||||
)
|
||||
|
||||
fig.update_yaxes(
|
||||
title="Marks"
|
||||
)
|
||||
|
||||
return fig.to_dict()
|
||||
return {"branches": branches}
|
||||
|
||||
|
||||
@app.get("/table")
|
||||
def get_table(campus: int = Query(0, ge=0, le=2), scenario: int = Query(0, ge=0, le=2)):
|
||||
try:
|
||||
tableRows = tableData[campus*3+scenario]
|
||||
tableRows = tableData[campus * 3 + scenario]
|
||||
except IndexError:
|
||||
raise HTTPException(400, "Invalid Scenario or Campus index")
|
||||
|
||||
|
||||
return tableRows
|
||||
|
|
|
|||
|
|
@ -4,8 +4,28 @@ import Plotly from "plotly.js-basic-dist";
|
|||
import createPlotlyComponent from "react-plotly.js/factory";
|
||||
import { useTheme } from "@/lib/themeContext";
|
||||
|
||||
interface BranchData {
|
||||
name: string;
|
||||
years: number[];
|
||||
marks: number[];
|
||||
}
|
||||
|
||||
interface GraphResponse {
|
||||
branches: BranchData[];
|
||||
}
|
||||
|
||||
interface Trace {
|
||||
x: number[];
|
||||
y: number[];
|
||||
name: string;
|
||||
type: 'scatter';
|
||||
mode: 'lines+markers';
|
||||
line?: { color?: string; width?: number };
|
||||
marker?: { size?: number; line?: { width?: number; color?: string } };
|
||||
}
|
||||
|
||||
interface PlotParams {
|
||||
data?: any[];
|
||||
data?: Trace[];
|
||||
layout?: any;
|
||||
}
|
||||
|
||||
|
|
@ -27,6 +47,29 @@ const formConfig = [
|
|||
},
|
||||
] as const;
|
||||
|
||||
const PHOENIX_PATTERNS = [
|
||||
/computer science/i,
|
||||
/electrical/i,
|
||||
/electronics/i,
|
||||
/mathematics.{0,3}(computing|and)/i,
|
||||
];
|
||||
|
||||
const MSC_PATTERNS = [
|
||||
/M\.?Sc\.?/i,
|
||||
];
|
||||
|
||||
function isBE(branchName: string): boolean {
|
||||
return !MSC_PATTERNS.some((pattern) => pattern.test(branchName));
|
||||
}
|
||||
|
||||
function isMSc(branchName: string): boolean {
|
||||
return MSC_PATTERNS.some((pattern) => pattern.test(branchName));
|
||||
}
|
||||
|
||||
function isPhoenix(branchName: string): boolean {
|
||||
return PHOENIX_PATTERNS.some((pattern) => pattern.test(branchName));
|
||||
}
|
||||
|
||||
function GraphPlot() {
|
||||
const { theme } = useTheme();
|
||||
const [graph, setGraph] = useState<PlotParams>({});
|
||||
|
|
@ -54,8 +97,28 @@ function GraphPlot() {
|
|||
try {
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const data = await res.json();
|
||||
setGraph(data);
|
||||
const response: GraphResponse = await res.json();
|
||||
|
||||
const traces: Trace[] = response.branches.map((branch) => ({
|
||||
x: branch.years,
|
||||
y: branch.marks,
|
||||
name: branch.name,
|
||||
type: 'scatter',
|
||||
mode: 'lines+markers',
|
||||
}));
|
||||
|
||||
setGraph({
|
||||
data: traces,
|
||||
layout: {
|
||||
title: {
|
||||
text: 'Cutoff Trends',
|
||||
font: {
|
||||
family: '"JetBrains Mono", monospace',
|
||||
size: isMobile ? 14 : 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
setIsLoaded(true);
|
||||
} catch (err) {
|
||||
console.error("Failed to load Plot. Error: ", err);
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"root":["./src/about.tsx","./src/app.tsx","./src/footer.tsx","./src/header.tsx","./src/home.tsx","./src/working.tsx","./src/main.tsx","./src/react-plotly.d.ts","./src/vite-env.d.ts","./src/components/ui/dropdown.tsx","./src/components/ui/graphplot.tsx","./src/components/ui/predicttable.tsx","./src/components/ui/profilecard.tsx","./src/lib/themecontext.tsx","./src/lib/utils.tsx","./src/lib/hooks/useappseo.ts"],"version":"5.8.3"}
|
||||
{"root":["./src/about.tsx","./src/app.tsx","./src/footer.tsx","./src/header.tsx","./src/home.tsx","./src/working.tsx","./src/main.tsx","./src/react-plotly.d.ts","./src/vite-env.d.ts","./src/components/ui/dropdown.tsx","./src/components/ui/graphplot.tsx","./src/components/ui/predicttable.tsx","./src/components/ui/profilecard.tsx","./src/lib/themecontext.tsx","./src/lib/utils.tsx","./src/lib/hooks/useappseo.ts"],"errors":true,"version":"5.8.3"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue