Skip to content

Commit 4532eac

Browse files
committed
merge~2
1 parent f4061f6 commit 4532eac

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1613
-0
lines changed

Flask Server part ~1/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Python Project Hub
2+
3+
A web platform where users can sign up, log in, view all your Python projects, read their descriptions and code, and run them in real time.
4+
5+
## Features
6+
- User authentication (signup & login)
7+
- List Python projects with description and code
8+
- Run Python code in real time from the browser
9+
10+
## Tech Stack
11+
- Frontend: React
12+
- Backend: FastAPI (Python)
13+
- Database: SQLite
14+
15+
## Getting Started
16+
17+
### Backend
18+
1. Navigate to `backend` and install dependencies:
19+
```bash
20+
pip install -r requirements.txt
21+
```
22+
2. Run the backend server:
23+
```bash
24+
uvicorn main:app --reload
25+
```
26+
27+
### Frontend
28+
1. Navigate to `frontend` and install dependencies:
29+
```bash
30+
npm install
31+
```
32+
2. Run the frontend:
33+
```bash
34+
npm start
35+
```
36+
37+
---
38+
39+
### Security Note
40+
Do NOT deploy this as-is for public use. Running arbitrary code is dangerous. Use containers, resource limits, and input validation for real deployments.
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import tkinter as tk
2+
from tkinter import colorchooser
3+
from abc import ABC, abstractmethod
4+
5+
# Shape interface
6+
class Shape(ABC):
7+
@abstractmethod
8+
def draw(self, canvas, **kwargs):
9+
pass
10+
11+
class Circle(Shape):
12+
def draw(self, canvas, center, radius, fill, outline, width):
13+
x0 = center[0] - radius
14+
y0 = center[1] - radius
15+
x1 = center[0] + radius
16+
y1 = center[1] + radius
17+
canvas.create_oval(x0, y0, x1, y1, fill=fill, outline=outline, width=width)
18+
19+
class Square(Shape):
20+
def draw(self, canvas, center, side, fill, outline, width):
21+
half = side // 2
22+
x0 = center[0] - half
23+
y0 = center[1] - half
24+
x1 = center[0] + half
25+
y1 = center[1] + half
26+
canvas.create_rectangle(x0, y0, x1, y1, fill=fill, outline=outline, width=width)
27+
28+
class Rectangle(Shape):
29+
def draw(self, canvas, center, width_r, height_r, fill, outline, width):
30+
x0 = center[0] - width_r // 2
31+
y0 = center[1] - height_r // 2
32+
x1 = center[0] + width_r // 2
33+
y1 = center[1] + height_r // 2
34+
canvas.create_rectangle(x0, y0, x1, y1, fill=fill, outline=outline, width=width)
35+
36+
class ShapeFactory:
37+
@staticmethod
38+
def create_shape(shape_type):
39+
shape_type = shape_type.lower()
40+
if shape_type == 'circle':
41+
return Circle()
42+
elif shape_type == 'square':
43+
return Square()
44+
elif shape_type == 'rectangle':
45+
return Rectangle()
46+
else:
47+
raise ValueError(f"Unknown shape type: {shape_type}")
48+
49+
class AdvancedShapeApp:
50+
def __init__(self, root):
51+
self.root = root
52+
self.root.title("Advanced Shape Factory GUI")
53+
self.root.geometry("600x500")
54+
self.root.resizable(False, False)
55+
self.bg_color = "#f2f2f2"
56+
self.selected_color = "#3498db"
57+
self.outline_color = "#222"
58+
self.outline_width = 3
59+
60+
# Controls Frame
61+
self.controls = tk.Frame(root, bg="#e8eaf6", bd=2, relief=tk.GROOVE)
62+
self.controls.place(x=10, y=10, width=220, height=480)
63+
64+
tk.Label(self.controls, text="Shape Type", bg="#e8eaf6", font=("Segoe UI", 11, "bold")).pack(pady=7)
65+
self.shape_var = tk.StringVar(value="circle")
66+
shapes = ["circle", "square", "rectangle"]
67+
for s in shapes:
68+
tk.Radiobutton(self.controls, text=s.title(), variable=self.shape_var, value=s, bg="#e8eaf6", font=("Segoe UI", 10)).pack(anchor=tk.W)
69+
70+
# Size controls (dynamic)
71+
self.size_frame = tk.Frame(self.controls, bg="#e8eaf6")
72+
self.size_frame.pack(pady=8)
73+
self.size_labels = {}
74+
self.size_entries = {}
75+
self._create_size_controls()
76+
self.shape_var.trace_add('write', lambda *args: self._update_size_controls())
77+
78+
# Color picker
79+
tk.Label(self.controls, text="Fill Color", bg="#e8eaf6", font=("Segoe UI", 11, "bold")).pack(pady=(12, 3))
80+
self.color_btn = tk.Button(self.controls, text="Choose Color", command=self.choose_color, bg=self.selected_color, fg="white", font=("Segoe UI", 10, "bold"))
81+
self.color_btn.pack(pady=2)
82+
83+
# Outline width
84+
tk.Label(self.controls, text="Outline Width", bg="#e8eaf6", font=("Segoe UI", 11, "bold")).pack(pady=(10, 3))
85+
self.outline_entry = tk.Entry(self.controls, width=5, font=("Segoe UI", 10))
86+
self.outline_entry.insert(0, str(self.outline_width))
87+
self.outline_entry.pack(pady=2)
88+
89+
# Draw & Clear
90+
self.draw_btn = tk.Button(self.controls, text="Draw Shape", command=self.draw_shape, bg="#43a047", fg="white", font=("Segoe UI", 11, "bold"))
91+
self.draw_btn.pack(pady=(16, 5), fill=tk.X)
92+
self.clear_btn = tk.Button(self.controls, text="Clear Canvas", command=self.clear_canvas, bg="#e53935", fg="white", font=("Segoe UI", 11, "bold"))
93+
self.clear_btn.pack(pady=2, fill=tk.X)
94+
95+
# Canvas
96+
self.canvas = tk.Canvas(root, width=340, height=460, bg=self.bg_color, highlightthickness=2, highlightbackground="#888")
97+
self.canvas.place(x=250, y=15)
98+
99+
# Status
100+
self.status_label = tk.Label(root, text="", fg="red", font=("Segoe UI", 10))
101+
self.status_label.place(x=250, y=480)
102+
103+
def _create_size_controls(self):
104+
# Remove old
105+
for widget in self.size_frame.winfo_children():
106+
widget.destroy()
107+
self.size_labels.clear()
108+
self.size_entries.clear()
109+
# Create for default (circle)
110+
self._update_size_controls()
111+
112+
def _update_size_controls(self):
113+
for widget in self.size_frame.winfo_children():
114+
widget.destroy()
115+
shape = self.shape_var.get()
116+
if shape == "circle":
117+
self.size_labels['radius'] = tk.Label(self.size_frame, text="Radius:", bg="#e8eaf6", font=("Segoe UI", 10))
118+
self.size_labels['radius'].grid(row=0, column=0, sticky=tk.W)
119+
self.size_entries['radius'] = tk.Entry(self.size_frame, width=8, font=("Segoe UI", 10))
120+
self.size_entries['radius'].insert(0, "60")
121+
self.size_entries['radius'].grid(row=0, column=1)
122+
elif shape == "square":
123+
self.size_labels['side'] = tk.Label(self.size_frame, text="Side:", bg="#e8eaf6", font=("Segoe UI", 10))
124+
self.size_labels['side'].grid(row=0, column=0, sticky=tk.W)
125+
self.size_entries['side'] = tk.Entry(self.size_frame, width=8, font=("Segoe UI", 10))
126+
self.size_entries['side'].insert(0, "100")
127+
self.size_entries['side'].grid(row=0, column=1)
128+
elif shape == "rectangle":
129+
self.size_labels['width'] = tk.Label(self.size_frame, text="Width:", bg="#e8eaf6", font=("Segoe UI", 10))
130+
self.size_labels['width'].grid(row=0, column=0, sticky=tk.W)
131+
self.size_entries['width'] = tk.Entry(self.size_frame, width=8, font=("Segoe UI", 10))
132+
self.size_entries['width'].insert(0, "150")
133+
self.size_entries['width'].grid(row=0, column=1)
134+
self.size_labels['height'] = tk.Label(self.size_frame, text="Height:", bg="#e8eaf6", font=("Segoe UI", 10))
135+
self.size_labels['height'].grid(row=1, column=0, sticky=tk.W)
136+
self.size_entries['height'] = tk.Entry(self.size_frame, width=8, font=("Segoe UI", 10))
137+
self.size_entries['height'].insert(0, "80")
138+
self.size_entries['height'].grid(row=1, column=1)
139+
140+
def choose_color(self):
141+
color = colorchooser.askcolor(title="Choose Fill Color", initialcolor=self.selected_color)
142+
if color[1]:
143+
self.selected_color = color[1]
144+
self.color_btn.config(bg=self.selected_color)
145+
146+
def draw_shape(self):
147+
shape_type = self.shape_var.get()
148+
try:
149+
outline_width = int(self.outline_entry.get())
150+
except Exception:
151+
self.status_label.config(text="Outline width must be a number.", fg="red")
152+
return
153+
# Get size
154+
try:
155+
if shape_type == "circle":
156+
radius = int(self.size_entries['radius'].get())
157+
shape = ShapeFactory.create_shape(shape_type)
158+
shape.draw(self.canvas, center=(170, 230), radius=radius, fill=self.selected_color, outline=self.outline_color, width=outline_width)
159+
elif shape_type == "square":
160+
side = int(self.size_entries['side'].get())
161+
shape = ShapeFactory.create_shape(shape_type)
162+
shape.draw(self.canvas, center=(170, 230), side=side, fill=self.selected_color, outline=self.outline_color, width=outline_width)
163+
elif shape_type == "rectangle":
164+
width_r = int(self.size_entries['width'].get())
165+
height_r = int(self.size_entries['height'].get())
166+
shape = ShapeFactory.create_shape(shape_type)
167+
shape.draw(self.canvas, center=(170, 230), width_r=width_r, height_r=height_r, fill=self.selected_color, outline=self.outline_color, width=outline_width)
168+
self.status_label.config(text=f"Drew a {shape_type}.", fg="green")
169+
except Exception as e:
170+
self.status_label.config(text=f"Error: {e}", fg="red")
171+
172+
def clear_canvas(self):
173+
self.canvas.delete("all")
174+
self.status_label.config(text="Canvas cleared.", fg="blue")
175+
176+
if __name__ == "__main__":
177+
root = tk.Tk()
178+
app = AdvancedShapeApp(root)
179+
root.mainloop()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from fastapi import Depends, HTTPException, status
2+
from fastapi.security import OAuth2PasswordBearer
3+
from jose import JWTError, jwt
4+
from passlib.context import CryptContext
5+
from sqlalchemy.orm import Session
6+
from .database import SessionLocal
7+
from .models import User
8+
9+
SECRET_KEY = "YOUR_SECRET_KEY"
10+
ALGORITHM = "HS256"
11+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
12+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
13+
14+
def get_password_hash(password):
15+
return pwd_context.hash(password)
16+
17+
def verify_password(plain_password, hashed_password):
18+
return pwd_context.verify(plain_password, hashed_password)
19+
20+
def get_user(db, username: str):
21+
return db.query(User).filter(User.username == username).first()
22+
23+
def authenticate_user(db, username: str, password: str):
24+
user = get_user(db, username)
25+
if not user or not verify_password(password, user.hashed_password):
26+
return False
27+
return user
28+
29+
def create_access_token(data: dict):
30+
from datetime import datetime, timedelta
31+
to_encode = data.copy()
32+
expire = datetime.utcnow() + timedelta(hours=2)
33+
to_encode.update({"exp": expire})
34+
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
35+
36+
def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(SessionLocal)):
37+
credentials_exception = HTTPException(
38+
status_code=status.HTTP_401_UNAUTHORIZED,
39+
detail="Could not validate credentials",
40+
headers={"WWW-Authenticate": "Bearer"},
41+
)
42+
try:
43+
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
44+
username: str = payload.get("sub")
45+
if username is None:
46+
raise credentials_exception
47+
except JWTError:
48+
raise credentials_exception
49+
user = get_user(db, username=username)
50+
if user is None:
51+
raise credentials_exception
52+
return user
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from sqlalchemy import create_engine
2+
from sqlalchemy.orm import sessionmaker, declarative_base
3+
4+
DATABASE_URL = "sqlite:///./test.db"
5+
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
6+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
7+
Base = declarative_base()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from fastapi import FastAPI, Depends, HTTPException, status
2+
from fastapi.middleware.cors import CORSMiddleware
3+
from sqlalchemy.orm import Session
4+
from . import models, database, auth
5+
from .models import User, Project
6+
import os
7+
import subprocess
8+
9+
app = FastAPI()
10+
models.Base.metadata.create_all(bind=database.engine)
11+
12+
app.add_middleware(
13+
CORSMiddleware,
14+
allow_origins=["*"],
15+
allow_credentials=True,
16+
allow_methods=["*"],
17+
allow_headers=["*"],
18+
)
19+
20+
def get_db():
21+
db = database.SessionLocal()
22+
try:
23+
yield db
24+
finally:
25+
db.close()
26+
27+
@app.post("/signup")
28+
def signup(username: str, password: str, db: Session = Depends(get_db)):
29+
if db.query(User).filter(User.username == username).first():
30+
raise HTTPException(status_code=400, detail="Username already registered")
31+
user = User(username=username, hashed_password=auth.get_password_hash(password))
32+
db.add(user)
33+
db.commit()
34+
db.refresh(user)
35+
return {"msg": "User created"}
36+
37+
@app.post("/token")
38+
def login(username: str, password: str, db: Session = Depends(get_db)):
39+
user = auth.authenticate_user(db, username, password)
40+
if not user:
41+
raise HTTPException(status_code=400, detail="Incorrect username or password")
42+
token = auth.create_access_token({"sub": user.username})
43+
return {"access_token": token, "token_type": "bearer"}
44+
45+
@app.get("/projects")
46+
def list_projects(db: Session = Depends(get_db), user: User = Depends(auth.get_current_user)):
47+
return db.query(Project).all()
48+
49+
@app.get("/project/{project_id}")
50+
def get_project(project_id: int, db: Session = Depends(get_db), user: User = Depends(auth.get_current_user)):
51+
project = db.query(Project).filter(Project.id == project_id).first()
52+
if not project:
53+
raise HTTPException(status_code=404, detail="Project not found")
54+
with open(f"backend/projects/{project.filename}", "r") as f:
55+
code = f.read()
56+
return {"name": project.name, "description": project.description, "code": code}
57+
58+
@app.post("/run/{project_id}")
59+
def run_project(project_id: int, db: Session = Depends(get_db), user: User = Depends(auth.get_current_user)):
60+
project = db.query(Project).filter(Project.id == project_id).first()
61+
if not project:
62+
raise HTTPException(status_code=404, detail="Project not found")
63+
try:
64+
result = subprocess.run(["python", f"backend/projects/{project.filename}"], capture_output=True, text=True, timeout=10)
65+
return {"stdout": result.stdout, "stderr": result.stderr}
66+
except Exception as e:
67+
return {"stdout": "", "stderr": str(e)}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from sqlalchemy import Column, Integer, String
2+
from .database import Base
3+
4+
class User(Base):
5+
__tablename__ = "users"
6+
id = Column(Integer, primary_key=True, index=True)
7+
username = Column(String, unique=True, index=True)
8+
hashed_password = Column(String)
9+
10+
class Project(Base):
11+
__tablename__ = "projects"
12+
id = Column(Integer, primary_key=True, index=True)
13+
name = Column(String, unique=True, index=True)
14+
description = Column(String)
15+
filename = Column(String)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fastapi
2+
uvicorn
3+
sqlalchemy
4+
passlib[bcrypt]
5+
python-jose
6+
pydantic
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "python-project-hub-frontend",
3+
"version": "1.0.0",
4+
"private": true,
5+
"dependencies": {
6+
"axios": "^1.6.0",
7+
"react": "^18.2.0",
8+
"react-dom": "^18.2.0",
9+
"react-router-dom": "^6.14.2"
10+
},
11+
"scripts": {
12+
"start": "react-scripts start",
13+
"build": "react-scripts build",
14+
"test": "react-scripts test",
15+
"eject": "react-scripts eject"
16+
}
17+
}

0 commit comments

Comments
 (0)