Adisri99 commited on
Commit
5607177
·
verified ·
1 Parent(s): 6cf553b

Upload 19 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PORT=7860
4
+
5
+ WORKDIR /workspace
6
+
7
+ RUN apt-get update && apt-get install -y --no-install-recommends build-essential curl && rm -rf /var/lib/apt/lists/*
8
+
9
+ COPY backend/requirements.txt /workspace/requirements.txt
10
+ RUN pip install --no-cache-dir -r /workspace/requirements.txt
11
+
12
+ COPY backend /workspace/backend
13
+ COPY scripts /workspace/scripts
14
+
15
+ RUN mkdir -p /data/uploads /data/artifacts
16
+
17
+ EXPOSE 7860
18
+
19
+ CMD ["sh", "-c", "python scripts/migrate_db.py && uvicorn backend.app.main:app --host 0.0.0.0 --port 7860"]
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: ClusterBuster-API
3
+ emoji: 📊
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: false
9
+ ---
10
+
11
+ # ClusterBuster API
12
+ FastAPI backend for clustering.
backend/__init__.py ADDED
File without changes
backend/app/__init__.py ADDED
File without changes
backend/app/api/__init__.py ADDED
File without changes
backend/app/api/datasets.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, UploadFile, File, Depends
2
+ from sqlalchemy.orm import Session
3
+ import pandas as pd, json
4
+ from pathlib import Path
5
+
6
+ from backend.app.db import get_db
7
+ from backend.app.repositories.dataset_repo import create_dataset
8
+ from backend.app.utils.ids import make_dataset_id
9
+ from backend.app.services.profiling_service import profile_dataframe
10
+
11
+ router = APIRouter()
12
+
13
+ UPLOAD_DIR = Path("/data/uploads")
14
+ UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
15
+
16
+ @router.post("/datasets/upload")
17
+ async def upload_dataset(file: UploadFile = File(...), db: Session = Depends(get_db)):
18
+ dataset_id = make_dataset_id()
19
+ path = UPLOAD_DIR / f"{dataset_id}.csv"
20
+ content = await file.read()
21
+ path.write_bytes(content)
22
+
23
+ df = pd.read_csv(path)
24
+ profile = profile_dataframe(df)
25
+
26
+ create_dataset(
27
+ db,
28
+ id=dataset_id,
29
+ name=file.filename,
30
+ file_path=str(path),
31
+ row_count=len(df),
32
+ column_count=len(df.columns),
33
+ schema_json=json.dumps({"columns": list(df.columns)}),
34
+ profile_json=json.dumps(profile),
35
+ )
36
+
37
+ return {"dataset_id": dataset_id}
backend/app/api/health.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+
3
+ router = APIRouter()
4
+
5
+ @router.get("/health")
6
+ def health():
7
+ return {"ok": True}
backend/app/db.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from sqlalchemy import create_engine
3
+ from sqlalchemy.orm import declarative_base, sessionmaker
4
+
5
+ DB_PATH = os.getenv("DB_PATH", "/data/clusterforge.db")
6
+ DATABASE_URL = f"sqlite:///{DB_PATH}"
7
+
8
+ engine = create_engine(
9
+ DATABASE_URL,
10
+ connect_args={"check_same_thread": False},
11
+ )
12
+
13
+ SessionLocal = sessionmaker(bind=engine)
14
+ Base = declarative_base()
15
+
16
+ def init_db():
17
+ import backend.app.models.dataset
18
+ Base.metadata.create_all(bind=engine)
19
+
20
+ def get_db():
21
+ db = SessionLocal()
22
+ try:
23
+ yield db
24
+ finally:
25
+ db.close()
backend/app/main.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+
4
+ from backend.app.db import init_db
5
+ from backend.app.api.health import router as health_router
6
+ from backend.app.api.datasets import router as datasets_router
7
+
8
+ app = FastAPI()
9
+
10
+ @app.on_event("startup")
11
+ def startup():
12
+ init_db()
13
+
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=["*"],
17
+ allow_methods=["*"],
18
+ allow_headers=["*"],
19
+ )
20
+
21
+ app.include_router(health_router, prefix="/api")
22
+ app.include_router(datasets_router, prefix="/api")
23
+
24
+ @app.get("/")
25
+ def root():
26
+ return {"ok": True}
backend/app/models/__init__.py ADDED
File without changes
backend/app/models/dataset.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, String, Integer, Text
2
+ from backend.app.db import Base
3
+
4
+ class Dataset(Base):
5
+ __tablename__ = "datasets"
6
+ id = Column(String, primary_key=True)
7
+ name = Column(String)
8
+ file_path = Column(String)
9
+ row_count = Column(Integer)
10
+ column_count = Column(Integer)
11
+ schema_json = Column(Text)
12
+ profile_json = Column(Text)
backend/app/repositories/__init__.py ADDED
File without changes
backend/app/repositories/dataset_repo.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy.orm import Session
2
+ from backend.app.models.dataset import Dataset
3
+
4
+ def create_dataset(db: Session, **kwargs):
5
+ obj = Dataset(**kwargs)
6
+ db.add(obj)
7
+ db.commit()
8
+ db.refresh(obj)
9
+ return obj
backend/app/services/__init__.py ADDED
File without changes
backend/app/services/profiling_service.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ def profile_dataframe(df):
2
+ return {"columns": [{"name": c} for c in df.columns]}
backend/app/utils/__init__.py ADDED
File without changes
backend/app/utils/ids.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ import secrets
2
+ def make_dataset_id():
3
+ return "ds_" + secrets.token_hex(4)
backend/requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ sqlalchemy
4
+ pandas
5
+ numpy
6
+ scikit-learn
7
+ python-multipart
8
+ openpyxl
scripts/migrate_db.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from backend.app.db import init_db
2
+ init_db()
3
+ print("DB ready")