triflix commited on
Commit
437681a
·
verified ·
1 Parent(s): a872f58

Upload 20 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use official Python image
2
+ FROM python:3.11-slim
3
+
4
+ # Set environment variables
5
+ ENV PYTHONDONTWRITEBYTECODE=1
6
+ ENV PYTHONUNBUFFERED=1
7
+
8
+ # Set work directory
9
+ WORKDIR /app
10
+
11
+ # Install system dependencies
12
+ RUN apt-get update && apt-get install -y build-essential libffi-dev && rm -rf /var/lib/apt/lists/*
13
+
14
+ # Install Python dependencies
15
+ COPY requirements.txt .
16
+ RUN pip install --upgrade pip && pip install -r requirements.txt
17
+
18
+ # Copy project files
19
+ COPY . .
20
+
21
+ # Expose the port Hugging Face expects
22
+ EXPOSE 7860
23
+
24
+ # Start the FastAPI server on port 7860
25
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860", "--reload"]
__pycache__/auth.cpython-312.pyc ADDED
Binary file (1.7 kB). View file
 
__pycache__/database.cpython-312.pyc ADDED
Binary file (557 Bytes). View file
 
__pycache__/main.cpython-312.pyc ADDED
Binary file (15.6 kB). View file
 
__pycache__/models.cpython-312.pyc ADDED
Binary file (1.78 kB). View file
 
app.db ADDED
Binary file (24.6 kB). View file
 
auth.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from passlib.context import CryptContext
2
+ from sqlalchemy.orm import Session
3
+ from models import User
4
+
5
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
6
+
7
+ def verify_password(plain_password, hashed_password):
8
+ return pwd_context.verify(plain_password, hashed_password)
9
+
10
+ def get_password_hash(password):
11
+ return pwd_context.hash(password)
12
+
13
+ def authenticate_user(db: Session, username: str, password: str):
14
+ user = db.query(User).filter(User.username == username).first()
15
+ if not user:
16
+ return None
17
+ if not verify_password(password, user.password):
18
+ return None
19
+ return user
20
+
21
+ def create_user(db: Session, username: str, password: str, is_admin: bool = False):
22
+ hashed_password = get_password_hash(password)
23
+ db_user = User(username=username, password=hashed_password, is_admin=is_admin)
24
+ db.add(db_user)
25
+ db.commit()
26
+ db.refresh(db_user)
27
+ return db_user
database.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import create_engine
2
+ from sqlalchemy.ext.declarative import declarative_base
3
+ from sqlalchemy.orm import sessionmaker
4
+
5
+ SQLALCHEMY_DATABASE_URL = "sqlite:///./app.db"
6
+ engine = create_engine(
7
+ SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
8
+ )
9
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
10
+ Base = declarative_base()
main.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request, Form, Depends, status
2
+ from fastapi.responses import RedirectResponse, HTMLResponse
3
+ from fastapi.staticfiles import StaticFiles
4
+ from fastapi.templating import Jinja2Templates
5
+ from starlette.middleware.sessions import SessionMiddleware
6
+ from sqlalchemy.orm import Session
7
+ from database import engine, Base, SessionLocal
8
+ from models import User, UserDetails
9
+ from auth import authenticate_user, create_user, get_password_hash
10
+ import uvicorn
11
+ from datetime import datetime
12
+
13
+ app = FastAPI()
14
+ app.add_middleware(SessionMiddleware, secret_key="your-secret-key")
15
+ app.mount("/static", StaticFiles(directory="static"), name="static")
16
+ templates = Jinja2Templates(directory="templates")
17
+ Base.metadata.create_all(bind=engine)
18
+
19
+ def get_db():
20
+ db = SessionLocal()
21
+ try:
22
+ yield db
23
+ finally:
24
+ db.close()
25
+
26
+ @app.get("/", response_class=HTMLResponse)
27
+ def index(request: Request):
28
+ if request.session.get("user_id"):
29
+ return RedirectResponse("/user/form", status_code=302)
30
+ return templates.TemplateResponse("login.html", {"request": request, "success": request.session.pop("success", None), "error": request.session.pop("error", None)})
31
+
32
+ @app.get("/login", response_class=HTMLResponse)
33
+ def login_get(request: Request):
34
+ return templates.TemplateResponse("login.html", {"request": request, "success": request.session.pop("success", None), "error": request.session.pop("error", None)})
35
+
36
+ @app.post("/login", response_class=HTMLResponse)
37
+ def login_post(request: Request, username: str = Form(...), password: str = Form(...), db: Session = Depends(get_db)):
38
+ user = authenticate_user(db, username, password)
39
+ if user:
40
+ request.session["user_id"] = user.id
41
+ request.session["is_admin"] = user.is_admin
42
+ request.session["success"] = "Login successful!"
43
+ if user.is_admin:
44
+ return RedirectResponse("/admin/dashboard", status_code=302)
45
+ else:
46
+ return RedirectResponse("/user/form", status_code=302)
47
+ request.session["error"] = "Invalid credentials"
48
+ return RedirectResponse("/login", status_code=302)
49
+
50
+ @app.get("/signup", response_class=HTMLResponse)
51
+ def signup_get(request: Request):
52
+ return templates.TemplateResponse("signup.html", {"request": request, "success": request.session.pop("success", None), "error": request.session.pop("error", None)})
53
+
54
+ @app.post("/signup", response_class=HTMLResponse)
55
+ def signup_post(request: Request, username: str = Form(...), password: str = Form(...), db: Session = Depends(get_db)):
56
+ if db.query(User).filter(User.username == username).first():
57
+ request.session["error"] = "Username already exists"
58
+ return RedirectResponse("/signup", status_code=302)
59
+ create_user(db, username, password)
60
+ request.session["success"] = "Signup successful! Please login."
61
+ return RedirectResponse("/login", status_code=302)
62
+
63
+ @app.get("/logout")
64
+ def logout(request: Request):
65
+ request.session.clear()
66
+ return RedirectResponse("/login", status_code=302)
67
+
68
+ @app.get("/user/form", response_class=HTMLResponse)
69
+ def user_form_get(request: Request, db: Session = Depends(get_db)):
70
+ user_id = request.session.get("user_id")
71
+ if not user_id:
72
+ return RedirectResponse("/login", status_code=302)
73
+ user = db.query(User).filter(User.id == user_id).first()
74
+ details = user.details if user and user.details else None
75
+ return templates.TemplateResponse("user_form.html", {"request": request, "details": details or {}, "success": request.session.pop("success", None), "error": request.session.pop("error", None)})
76
+
77
+ @app.post("/user/form", response_class=HTMLResponse)
78
+ def user_form_post(request: Request,
79
+ first_name: str = Form(...),
80
+ last_name: str = Form(...),
81
+ email: str = Form(...),
82
+ mobile: str = Form(...),
83
+ dob: str = Form(...),
84
+ gender: str = Form(...),
85
+ current_semester: str = Form(...),
86
+ tenth_percentage: float = Form(...),
87
+ twelfth_percentage: float = Form(...),
88
+ graduation_percentage: float = Form(...),
89
+ specialization: str = Form(...),
90
+ experience_status: str = Form(...),
91
+ db: Session = Depends(get_db)):
92
+ user_id = request.session.get("user_id")
93
+ if not user_id:
94
+ return RedirectResponse("/login", status_code=302)
95
+ user = db.query(User).filter(User.id == user_id).first()
96
+ if not user:
97
+ return RedirectResponse("/login", status_code=302)
98
+ details = user.details
99
+ if not details:
100
+ details = UserDetails(user_id=user.id)
101
+ db.add(details)
102
+ msg = "Details created!"
103
+ else:
104
+ msg = "Details updated!"
105
+ details.first_name = first_name
106
+ details.last_name = last_name
107
+ details.email = email
108
+ details.mobile = mobile
109
+ details.dob = datetime.strptime(dob, "%Y-%m-%d").date()
110
+ details.gender = gender
111
+ details.current_semester = current_semester
112
+ details.tenth_percentage = tenth_percentage
113
+ details.twelfth_percentage = twelfth_percentage
114
+ details.graduation_percentage = graduation_percentage
115
+ details.specialization = specialization
116
+ details.experience_status = experience_status
117
+ db.commit()
118
+ request.session["success"] = msg
119
+ return RedirectResponse("/user/form", status_code=302)
120
+
121
+ @app.get("/admin/dashboard", response_class=HTMLResponse)
122
+ def admin_dashboard(request: Request, search: str = "", db: Session = Depends(get_db)):
123
+ if not request.session.get("is_admin"):
124
+ return RedirectResponse("/login", status_code=302)
125
+ query = db.query(User).filter(User.is_admin == False)
126
+ if search:
127
+ query = query.join(UserDetails).filter(
128
+ (User.username.contains(search)) |
129
+ (UserDetails.first_name.contains(search)) |
130
+ (UserDetails.last_name.contains(search)) |
131
+ (UserDetails.email.contains(search))
132
+ )
133
+ users = query.all()
134
+ return templates.TemplateResponse("admin_dashboard.html", {"request": request, "users": users, "search": search, "success": request.session.pop("success", None), "error": request.session.pop("error", None)})
135
+
136
+ @app.get("/admin/user/{user_id}", response_class=HTMLResponse)
137
+ def admin_view_user(request: Request, user_id: int, db: Session = Depends(get_db)):
138
+ if not request.session.get("is_admin"):
139
+ return RedirectResponse("/login", status_code=302)
140
+ user = db.query(User).filter(User.id == user_id).first()
141
+ if not user:
142
+ return RedirectResponse("/admin/dashboard", status_code=302)
143
+ details = user.details
144
+ return templates.TemplateResponse("admin_view.html", {"request": request, "user": user, "details": details})
145
+
146
+ @app.get("/admin/user/{user_id}/edit", response_class=HTMLResponse)
147
+ def admin_edit_user_get(request: Request, user_id: int, db: Session = Depends(get_db)):
148
+ if not request.session.get("is_admin"):
149
+ return RedirectResponse("/login", status_code=302)
150
+ user = db.query(User).filter(User.id == user_id).first()
151
+ if not user:
152
+ return RedirectResponse("/admin/dashboard", status_code=302)
153
+ details = user.details or None
154
+ return templates.TemplateResponse("admin_edit.html", {"request": request, "user": user, "details": details or {}, "success": request.session.pop("success", None), "error": request.session.pop("error", None)})
155
+
156
+ @app.post("/admin/user/{user_id}/edit", response_class=HTMLResponse)
157
+ def admin_edit_user_post(request: Request, user_id: int,
158
+ first_name: str = Form(...),
159
+ last_name: str = Form(...),
160
+ email: str = Form(...),
161
+ mobile: str = Form(...),
162
+ dob: str = Form(...),
163
+ gender: str = Form(...),
164
+ current_semester: str = Form(...),
165
+ tenth_percentage: float = Form(...),
166
+ twelfth_percentage: float = Form(...),
167
+ graduation_percentage: float = Form(...),
168
+ specialization: str = Form(...),
169
+ experience_status: str = Form(...),
170
+ db: Session = Depends(get_db)):
171
+ if not request.session.get("is_admin"):
172
+ return RedirectResponse("/login", status_code=302)
173
+ user = db.query(User).filter(User.id == user_id).first()
174
+ if not user:
175
+ return RedirectResponse("/admin/dashboard", status_code=302)
176
+ details = user.details
177
+ if not details:
178
+ details = UserDetails(user_id=user.id)
179
+ db.add(details)
180
+ msg = "User details created!"
181
+ else:
182
+ msg = "User details updated!"
183
+ details.first_name = first_name
184
+ details.last_name = last_name
185
+ details.email = email
186
+ details.mobile = mobile
187
+ details.dob = datetime.strptime(dob, "%Y-%m-%d").date()
188
+ details.gender = gender
189
+ details.current_semester = current_semester
190
+ details.tenth_percentage = tenth_percentage
191
+ details.twelfth_percentage = twelfth_percentage
192
+ details.graduation_percentage = graduation_percentage
193
+ details.specialization = specialization
194
+ details.experience_status = experience_status
195
+ db.commit()
196
+ request.session["success"] = msg
197
+ return RedirectResponse(f"/admin/user/{user_id}/edit", status_code=302)
198
+
199
+ @app.get("/admin/user/{user_id}/delete")
200
+ def admin_delete_user(request: Request, user_id: int, db: Session = Depends(get_db)):
201
+ if not request.session.get("is_admin"):
202
+ return RedirectResponse("/login", status_code=302)
203
+ user = db.query(User).filter(User.id == user_id).first()
204
+ if user:
205
+ db.delete(user)
206
+ db.commit()
207
+ request.session["success"] = "User deleted!"
208
+ return RedirectResponse("/admin/dashboard", status_code=302)
209
+
210
+ @app.get("/create-admin")
211
+ def create_admin(request: Request, db: Session = Depends(get_db)):
212
+ if db.query(User).filter(User.username == "admin@149gmail.com").first():
213
+ request.session["error"] = "Admin already exists."
214
+ return RedirectResponse("/login", status_code=302)
215
+ create_user(db, "admin@149gmail.com", "Admin@149", is_admin=True)
216
+ request.session["success"] = "Admin created. You can now login as admin."
217
+ return RedirectResponse("/login", status_code=302)
218
+
219
+ if __name__ == "__main__":
220
+ uvicorn.run("main:app", host="127.0.0.1", port=7860, reload=True)
models.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, Integer, String, Boolean, Date, Float, ForeignKey
2
+ from sqlalchemy.orm import relationship
3
+ from database import Base
4
+
5
+ class User(Base):
6
+ __tablename__ = "users"
7
+ id = Column(Integer, primary_key=True, index=True)
8
+ username = Column(String, unique=True, index=True, nullable=False)
9
+ password = Column(String, nullable=False)
10
+ is_admin = Column(Boolean, default=False)
11
+ details = relationship("UserDetails", back_populates="user", uselist=False)
12
+
13
+ class UserDetails(Base):
14
+ __tablename__ = "user_details"
15
+ id = Column(Integer, primary_key=True, index=True)
16
+ user_id = Column(Integer, ForeignKey("users.id"))
17
+ first_name = Column(String)
18
+ last_name = Column(String)
19
+ email = Column(String)
20
+ mobile = Column(String)
21
+ dob = Column(Date)
22
+ gender = Column(String)
23
+ current_semester = Column(String)
24
+ tenth_percentage = Column(Float)
25
+ twelfth_percentage = Column(Float)
26
+ graduation_percentage = Column(Float)
27
+ specialization = Column(String)
28
+ experience_status = Column(String)
29
+ user = relationship("User", back_populates="details")
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ jinja2
4
+ sqlalchemy
5
+ passlib[bcrypt]
6
+ python-multipart
7
+ itsdangerous
static/.keep ADDED
@@ -0,0 +1 @@
 
 
1
+
static/custom.css ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ min-height: 100vh;
3
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
4
+ position: relative;
5
+ overflow-x: hidden;
6
+ }
7
+
8
+ .blob-bg {
9
+ position: fixed;
10
+ z-index: 0;
11
+ top: -100px;
12
+ left: -100px;
13
+ width: 400px;
14
+ height: 400px;
15
+ background: radial-gradient(circle at 60% 40%, #a18cd1 0%, #fbc2eb 100%);
16
+ opacity: 0.5;
17
+ filter: blur(80px);
18
+ border-radius: 50%;
19
+ animation: blobMove 15s infinite alternate ease-in-out;
20
+ }
21
+
22
+ .blob-bg2 {
23
+ position: fixed;
24
+ z-index: 0;
25
+ bottom: -120px;
26
+ right: -120px;
27
+ width: 420px;
28
+ height: 420px;
29
+ background: radial-gradient(circle at 40% 60%, #fad0c4 0%, #ffd1ff 100%);
30
+ opacity: 0.4;
31
+ filter: blur(90px);
32
+ border-radius: 50%;
33
+ animation: blobMove2 18s infinite alternate ease-in-out;
34
+ }
35
+
36
+ @keyframes blobMove {
37
+ 0% { transform: scale(1) translateY(0) translateX(0); }
38
+ 100% { transform: scale(1.2) translateY(40px) translateX(60px); }
39
+ }
40
+ @keyframes blobMove2 {
41
+ 0% { transform: scale(1) translateY(0) translateX(0); }
42
+ 100% { transform: scale(1.1) translateY(-30px) translateX(-50px); }
43
+ }
44
+
45
+ .card, .table, .form-control, .form-select, .btn {
46
+ border-radius: 1.5rem !important;
47
+ }
48
+
49
+ .card {
50
+ box-shadow: 0 4px 32px 0 rgba(80, 80, 160, 0.09);
51
+ border: none;
52
+ background: rgba(255,255,255,0.95);
53
+ backdrop-filter: blur(2px);
54
+ transition: box-shadow 0.2s;
55
+ }
56
+ .card:hover {
57
+ box-shadow: 0 8px 40px 0 rgba(80, 80, 160, 0.18);
58
+ }
59
+
60
+ .admin-card {
61
+ background: rgba(245, 245, 255, 0.97);
62
+ border: 2px solid #d1d8fc;
63
+ box-shadow: 0 4px 28px 0 rgba(81, 86, 255, 0.09);
64
+ border-radius: 1.8rem !important;
65
+ margin-bottom: 1.5rem;
66
+ overflow: hidden;
67
+ }
68
+ .admin-card .card-header {
69
+ font-weight: 600;
70
+ letter-spacing: 0.5px;
71
+ background: linear-gradient(90deg, #5f72bd 0%, #9b23ea 100%) !important;
72
+ color: #fff !important;
73
+ border-radius: 1.8rem 1.8rem 0 0 !important;
74
+ }
75
+
76
+ .admin-navbar {
77
+ background: linear-gradient(90deg, #5f72bd 0%, #9b23ea 100%) !important;
78
+ box-shadow: 0 4px 24px 0 rgba(81, 86, 255, 0.09);
79
+ border-radius: 0 0 1.5rem 1.5rem;
80
+ margin-top: 0;
81
+ margin-bottom: 1.2rem;
82
+ padding-left: 1.2rem;
83
+ padding-right: 1.2rem;
84
+ backdrop-filter: blur(2px);
85
+ position: sticky;
86
+ top: 0;
87
+ z-index: 1050;
88
+ }
89
+
90
+ .btn-primary, .btn-success, .btn-info, .btn-warning, .btn-danger, .btn-secondary {
91
+ border-radius: 2rem;
92
+ font-weight: 600;
93
+ letter-spacing: 0.5px;
94
+ box-shadow: 0 2px 8px 0 rgba(80, 80, 160, 0.08);
95
+ transition: background 0.2s, box-shadow 0.2s;
96
+ }
97
+
98
+ .btn-primary:hover, .btn-success:hover, .btn-info:hover, .btn-warning:hover, .btn-danger:hover, .btn-secondary:hover {
99
+ filter: brightness(0.95);
100
+ box-shadow: 0 4px 16px 0 rgba(80, 80, 160, 0.12);
101
+ }
102
+
103
+ .form-control:focus, .form-select:focus {
104
+ border-color: #a18cd1;
105
+ box-shadow: 0 0 0 0.2rem rgba(161,140,209,.15);
106
+ }
107
+
108
+ .alert {
109
+ border-radius: 1.5rem;
110
+ font-size: 1rem;
111
+ }
112
+
113
+ .navbar {
114
+ }
115
+
116
+ @media (max-width: 600px) {
117
+ .card, .navbar, .form-control, .form-select, .btn, .alert {
118
+ border-radius: 1.1rem !important;
119
+ }
120
+ .admin-card {
121
+ border-radius: 1.1rem !important;
122
+ }
123
+ .admin-card .card-header {
124
+ border-radius: 1.1rem 1.1rem 0 0 !important;
125
+ }
126
+ .admin-navbar {
127
+ border-radius: 0 0 1.1rem 1.1rem !important;
128
+ margin-top: 0;
129
+ margin-bottom: 0.5rem;
130
+ padding-left: 0.2rem;
131
+ padding-right: 0.2rem;
132
+ }
133
+ .col-md-8, .col-md-4 {
134
+ padding: 0 0.5rem;
135
+ }
136
+ }
templates/admin_dashboard.html ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'base.html' %}
2
+ {% block title %}Admin Dashboard{% endblock %}
3
+ {% block content %}
4
+ <h3 class="mb-4 text-center">Admin Dashboard</h3>
5
+ <form class="row mb-3 justify-content-center" method="get" action="/admin/dashboard">
6
+ <div class="col-md-6 col-12 mb-2 mb-md-0">
7
+ <input type="text" class="form-control" name="search" placeholder="Search by username, name or email" value="{{ search or '' }}">
8
+ </div>
9
+ <div class="col-md-2 col-6">
10
+ <button type="submit" class="btn btn-primary w-100">Search</button>
11
+ </div>
12
+ </form>
13
+ <div class="table-responsive">
14
+ <table class="table table-bordered table-striped align-middle text-center" style="border-radius:1.5rem;overflow:hidden;">
15
+ <thead class="table-light">
16
+ <tr>
17
+ <th>Username</th>
18
+ <th>Name</th>
19
+ <th>Email</th>
20
+ <th>Mobile</th>
21
+ <th>Actions</th>
22
+ </tr>
23
+ </thead>
24
+ <tbody>
25
+ {% for user in users %}
26
+ <tr>
27
+ <td>{{ user.username }}</td>
28
+ <td>{{ user.details.first_name }} {{ user.details.last_name }}</td>
29
+ <td>{{ user.details.email }}</td>
30
+ <td>{{ user.details.mobile }}</td>
31
+ <td>
32
+ <a href="/admin/user/{{ user.id }}" class="btn btn-sm btn-info mx-1 mb-1">View</a>
33
+ <a href="/admin/user/{{ user.id }}/edit" class="btn btn-sm btn-warning mx-1 mb-1">Edit</a>
34
+ <a href="/admin/user/{{ user.id }}/delete" class="btn btn-sm btn-danger mx-1 mb-1" onclick="return confirm('Delete user?');">Delete</a>
35
+ </td>
36
+ </tr>
37
+ {% endfor %}
38
+ </tbody>
39
+ </table>
40
+ </div>
41
+ {% endblock %}
templates/admin_edit.html ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'base.html' %}
2
+ {% block title %}Edit User{% endblock %}
3
+ {% block content %}
4
+ <div class="row justify-content-center">
5
+ <div class="col-md-8" style="padding:0;">
6
+ <form method="post" action="/admin/user/{{ user.id }}/edit" novalidate class="needs-validation" style="background:transparent;">
7
+ <div class="card mb-4 admin-card">
8
+ <div class="card-header bg-primary text-white rounded-top-4">Personal Details</div>
9
+ <div class="card-body row g-3">
10
+ <div class="col-md-6">
11
+ <label class="form-label">First Name</label>
12
+ <input type="text" class="form-control" name="first_name" value="{{ details.first_name or '' }}" required>
13
+ </div>
14
+ <div class="col-md-6">
15
+ <label class="form-label">Last Name</label>
16
+ <input type="text" class="form-control" name="last_name" value="{{ details.last_name or '' }}" required>
17
+ </div>
18
+ <div class="col-md-6">
19
+ <label class="form-label">Email</label>
20
+ <input type="email" class="form-control" name="email" value="{{ details.email or '' }}" required>
21
+ </div>
22
+ <div class="col-md-6">
23
+ <label class="form-label">Mobile No</label>
24
+ <input type="tel" class="form-control" name="mobile" value="{{ details.mobile or '' }}" required pattern="[0-9]{10}">
25
+ </div>
26
+ <div class="col-md-6">
27
+ <label class="form-label">Date of Birth</label>
28
+ <input type="date" class="form-control" name="dob" value="{{ details.dob or '' }}" required>
29
+ </div>
30
+ <div class="col-md-6">
31
+ <label class="form-label">Gender</label>
32
+ <select class="form-select" name="gender" required>
33
+ <option value="">Select</option>
34
+ <option value="Male" {% if details.gender=='Male' %}selected{% endif %}>Male</option>
35
+ <option value="Female" {% if details.gender=='Female' %}selected{% endif %}>Female</option>
36
+ <option value="Other" {% if details.gender=='Other' %}selected{% endif %}>Other</option>
37
+ </select>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <div class="card mb-4 admin-card">
42
+ <div class="card-header bg-success text-white rounded-top-4">Educational Information</div>
43
+ <div class="card-body row g-3">
44
+ <div class="col-md-6">
45
+ <label class="form-label">Current Semester</label>
46
+ <input type="text" class="form-control" name="current_semester" value="{{ details.current_semester or '' }}" required>
47
+ </div>
48
+ <div class="col-md-6">
49
+ <label class="form-label">Class 10th Percentage</label>
50
+ <input type="number" step="0.01" min="0" max="100" class="form-control" name="tenth_percentage" value="{{ details.tenth_percentage or '' }}" required>
51
+ </div>
52
+ <div class="col-md-6">
53
+ <label class="form-label">Class 12th Percentage</label>
54
+ <input type="number" step="0.01" min="0" max="100" class="form-control" name="twelfth_percentage" value="{{ details.twelfth_percentage or '' }}" required>
55
+ </div>
56
+ <div class="col-md-6">
57
+ <label class="form-label">Graduation Percentage</label>
58
+ <input type="number" step="0.01" min="0" max="100" class="form-control" name="graduation_percentage" value="{{ details.graduation_percentage or '' }}" required>
59
+ </div>
60
+ <div class="col-md-6">
61
+ <label class="form-label">Specialization</label>
62
+ <input type="text" class="form-control" name="specialization" value="{{ details.specialization or '' }}" required>
63
+ </div>
64
+ <div class="col-md-6">
65
+ <label class="form-label">Experience Status</label>
66
+ <select class="form-select" name="experience_status" required>
67
+ <option value="">Select</option>
68
+ <option value="Experienced" {% if details.experience_status=='Experienced' %}selected{% endif %}>Experienced</option>
69
+ <option value="Fresher" {% if details.experience_status=='Fresher' %}selected{% endif %}>Fresher</option>
70
+ </select>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ <button type="submit" class="btn btn-primary w-100">Update</button>
75
+ </form>
76
+ </div>
77
+ </div>
78
+ <script>
79
+ // Bootstrap validation
80
+ (function () {
81
+ 'use strict';
82
+ var forms = document.querySelectorAll('.needs-validation');
83
+ Array.prototype.slice.call(forms).forEach(function (form) {
84
+ form.addEventListener('submit', function (event) {
85
+ if (!form.checkValidity()) {
86
+ event.preventDefault();
87
+ event.stopPropagation();
88
+ }
89
+ form.classList.add('was-validated');
90
+ }, false);
91
+ });
92
+ })();
93
+ </script>
94
+ {% endblock %}
templates/admin_view.html ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'base.html' %}
2
+ {% block title %}View User{% endblock %}
3
+ {% block content %}
4
+ <div class="row justify-content-center">
5
+ <div class="col-md-8" style="padding:0;">
6
+ <div class="card mb-4 admin-card" style="border-radius:1.5rem;">
7
+ <div class="card-header bg-info text-white rounded-top-4">User Details</div>
8
+ <div class="card-body row g-3">
9
+ <div class="col-md-6"><strong>Username:</strong> {{ user.username }}</div>
10
+ <div class="col-md-6"><strong>Name:</strong> {{ details.first_name }} {{ details.last_name }}</div>
11
+ <div class="col-md-6"><strong>Email:</strong> {{ details.email }}</div>
12
+ <div class="col-md-6"><strong>Mobile:</strong> {{ details.mobile }}</div>
13
+ <div class="col-md-6"><strong>Date of Birth:</strong> {{ details.dob }}</div>
14
+ <div class="col-md-6"><strong>Gender:</strong> {{ details.gender }}</div>
15
+ <div class="col-md-6"><strong>Current Semester:</strong> {{ details.current_semester }}</div>
16
+ <div class="col-md-6"><strong>10th </strong> {{ details.tenth_percentage }} %</div>
17
+ <div class="col-md-6"><strong>12th </strong> {{ details.twelfth_percentage }} %</div>
18
+ <div class="col-md-6"><strong>Graduation </strong> {{ details.graduation_percentage }} %</div>
19
+ <div class="col-md-6"><strong>Specialization:</strong> {{ details.specialization }}</div>
20
+ <div class="col-md-6"><strong>Experience:</strong> {{ details.experience_status }}</div>
21
+ </div>
22
+ </div>
23
+ <a href="/admin/dashboard" class="btn btn-secondary">Back to Dashboard</a>
24
+ </div>
25
+ </div>
26
+ {% endblock %}
templates/base.html ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}App{% endblock %}</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <link href="/static/custom.css" rel="stylesheet">
9
+ </head>
10
+ <body>
11
+ <div class="blob-bg"></div>
12
+ <div class="blob-bg2"></div>
13
+ {% if request.session.get('is_admin') %}
14
+ <nav class="navbar navbar-expand-lg navbar-dark admin-navbar">
15
+ <div class="container-fluid">
16
+ <a class="navbar-brand" href="/admin/dashboard">Admin Panel</a>
17
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
18
+ <span class="navbar-toggler-icon"></span>
19
+ </button>
20
+ <div class="collapse navbar-collapse" id="navbarNav">
21
+ <ul class="navbar-nav ms-auto mb-2 mb-lg-0">
22
+ <li class="nav-item">
23
+ <a class="nav-link" href="/admin/dashboard">Dashboard</a>
24
+ </li>
25
+ <li class="nav-item">
26
+ <a class="nav-link" href="/logout">Logout</a>
27
+ </li>
28
+ </ul>
29
+ </div>
30
+ </div>
31
+ </nav>
32
+ {% else %}
33
+ <nav class="navbar navbar-expand-lg navbar-light bg-light">
34
+ <div class="container-fluid">
35
+ <a class="navbar-brand" href="/">Student Portal</a>
36
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
37
+ <span class="navbar-toggler-icon"></span>
38
+ </button>
39
+ <div class="collapse navbar-collapse" id="navbarNav">
40
+ <ul class="navbar-nav ms-auto mb-2 mb-lg-0">
41
+ {% if request.session.get('user_id') %}
42
+ <li class="nav-item">
43
+ <a class="nav-link" href="/user/form">My Form</a>
44
+ </li>
45
+ <li class="nav-item">
46
+ <a class="nav-link" href="/logout">Logout</a>
47
+ </li>
48
+ {% else %}
49
+ <li class="nav-item">
50
+ <a class="nav-link" href="/login">Login</a>
51
+ </li>
52
+ <li class="nav-item">
53
+ <a class="nav-link" href="/signup">Sign Up</a>
54
+ </li>
55
+ {% endif %}
56
+ </ul>
57
+ </div>
58
+ </div>
59
+ </nav>
60
+ {% endif %}
61
+ <div class="container mt-4">
62
+ {% if success %}
63
+ <div class="alert alert-success alert-dismissible fade show" role="alert">
64
+ {{ success }}
65
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
66
+ </div>
67
+ {% endif %}
68
+ {% if error %}
69
+ <div class="alert alert-danger alert-dismissible fade show" role="alert">
70
+ {{ error }}
71
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
72
+ </div>
73
+ {% endif %}
74
+ {% block content %}{% endblock %}
75
+ </div>
76
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
77
+ </body>
78
+ </html>
templates/login.html ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'base.html' %}
2
+ {% block title %}Login{% endblock %}
3
+ {% block content %}
4
+ <div class="row justify-content-center align-items-center" style="min-height:70vh;">
5
+ <div class="col-md-4 col-12">
6
+ <h3 class="mb-3 text-center">Login</h3>
7
+ <form method="post" action="/login" novalidate class="needs-validation" style="background:transparent;">
8
+ <div class="mb-3">
9
+ <label for="username" class="form-label">Username</label>
10
+ <input type="text" class="form-control" id="username" name="username" required autocomplete="username">
11
+ </div>
12
+ <div class="mb-3">
13
+ <label for="password" class="form-label">Password</label>
14
+ <input type="password" class="form-control" id="password" name="password" required minlength="4" autocomplete="current-password">
15
+ </div>
16
+ <button type="submit" class="btn btn-primary w-100">Login</button>
17
+ </form>
18
+ <div class="mt-3 text-center">
19
+ <a href="/signup">Don't have an account? Sign up</a>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ <script>
24
+ (function () {
25
+ 'use strict';
26
+ var forms = document.querySelectorAll('.needs-validation');
27
+ Array.prototype.slice.call(forms).forEach(function (form) {
28
+ form.addEventListener('submit', function (event) {
29
+ if (!form.checkValidity()) {
30
+ event.preventDefault();
31
+ event.stopPropagation();
32
+ }
33
+ form.classList.add('was-validated');
34
+ }, false);
35
+ });
36
+ })();
37
+ </script>
38
+ {% endblock %}
templates/signup.html ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'base.html' %}
2
+ {% block title %}Signup{% endblock %}
3
+ {% block content %}
4
+ <div class="row justify-content-center align-items-center" style="min-height:70vh;">
5
+ <div class="col-md-4 col-12">
6
+ <h3 class="mb-3 text-center">Sign Up</h3>
7
+ <form method="post" action="/signup" novalidate class="needs-validation" style="background:transparent;">
8
+ <div class="mb-3">
9
+ <label for="username" class="form-label">Username</label>
10
+ <input type="text" class="form-control" id="username" name="username" required autocomplete="username">
11
+ </div>
12
+ <div class="mb-3">
13
+ <label for="password" class="form-label">Password</label>
14
+ <input type="password" class="form-control" id="password" name="password" required minlength="4" autocomplete="new-password">
15
+ </div>
16
+ <div class="mb-3">
17
+ <label for="confirm_password" class="form-label">Confirm Password</label>
18
+ <input type="password" class="form-control" id="confirm_password" name="confirm_password" required minlength="4" autocomplete="new-password">
19
+ <div class="invalid-feedback" id="confirm-feedback">Passwords do not match.</div>
20
+ </div>
21
+ <button type="submit" class="btn btn-success w-100" id="signup-btn" disabled>Sign Up</button>
22
+ </form>
23
+ <div class="mt-3 text-center">
24
+ <a href="/login">Already have an account? Login</a>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ <script>
29
+ (function () {
30
+ 'use strict';
31
+ var forms = document.querySelectorAll('.needs-validation');
32
+ Array.prototype.slice.call(forms).forEach(function (form) {
33
+ var password = form.querySelector('#password');
34
+ var confirm = form.querySelector('#confirm_password');
35
+ var btn = form.querySelector('#signup-btn');
36
+ var feedback = form.querySelector('#confirm-feedback');
37
+ function checkMatch() {
38
+ if (password.value && confirm.value && password.value === confirm.value) {
39
+ confirm.classList.remove('is-invalid');
40
+ confirm.classList.add('is-valid');
41
+ feedback.style.display = 'none';
42
+ btn.disabled = false;
43
+ } else {
44
+ confirm.classList.remove('is-valid');
45
+ if (confirm.value) {
46
+ confirm.classList.add('is-invalid');
47
+ feedback.style.display = '';
48
+ } else {
49
+ confirm.classList.remove('is-invalid');
50
+ feedback.style.display = 'none';
51
+ }
52
+ btn.disabled = true;
53
+ }
54
+ }
55
+ password.addEventListener('input', checkMatch);
56
+ confirm.addEventListener('input', checkMatch);
57
+ form.addEventListener('submit', function (event) {
58
+ if (!form.checkValidity() || password.value !== confirm.value) {
59
+ event.preventDefault();
60
+ event.stopPropagation();
61
+ checkMatch();
62
+ }
63
+ form.classList.add('was-validated');
64
+ }, false);
65
+ });
66
+ })();
67
+ </script>
68
+ {% endblock %}
templates/user_form.html ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'base.html' %}
2
+ {% block title %}Your Details{% endblock %}
3
+ {% block content %}
4
+ <div class="row justify-content-center">
5
+ <div class="col-md-8" style="padding:0;">
6
+ <form method="post" action="/user/form" novalidate class="needs-validation" style="background:transparent;">
7
+ <div class="card mb-4" style="border-radius:1.5rem;">
8
+ <div class="card-header bg-primary text-white">Personal Details</div>
9
+ <div class="card-body row g-3">
10
+ <div class="col-md-6">
11
+ <label class="form-label">First Name</label>
12
+ <input type="text" class="form-control" name="first_name" value="{{ details.first_name or '' }}" required>
13
+ </div>
14
+ <div class="col-md-6">
15
+ <label class="form-label">Last Name</label>
16
+ <input type="text" class="form-control" name="last_name" value="{{ details.last_name or '' }}" required>
17
+ </div>
18
+ <div class="col-md-6">
19
+ <label class="form-label">Email</label>
20
+ <input type="email" class="form-control" name="email" value="{{ details.email or '' }}" required>
21
+ </div>
22
+ <div class="col-md-6">
23
+ <label class="form-label">Mobile No</label>
24
+ <input type="tel" class="form-control" name="mobile" value="{{ details.mobile or '' }}" required pattern="[0-9]{10}">
25
+ </div>
26
+ <div class="col-md-6">
27
+ <label class="form-label">Date of Birth</label>
28
+ <input type="date" class="form-control" name="dob" value="{{ details.dob.strftime('%Y-%m-%d') if details.dob else '' }}" required>
29
+ </div>
30
+ <div class="col-md-6">
31
+ <label class="form-label">Gender</label>
32
+ <select class="form-select" name="gender" required>
33
+ <option value="">Select</option>
34
+ <option value="Male" {% if details.gender=='Male' %}selected{% endif %}>Male</option>
35
+ <option value="Female" {% if details.gender=='Female' %}selected{% endif %}>Female</option>
36
+ <option value="Other" {% if details.gender=='Other' %}selected{% endif %}>Other</option>
37
+ </select>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <div class="card mb-4" style="border-radius:1.5rem;">
42
+ <div class="card-header bg-success text-white">Educational Information</div>
43
+ <div class="card-body row g-3">
44
+ <div class="col-md-6">
45
+ <label class="form-label">Current Semester</label>
46
+ <input type="text" class="form-control" name="current_semester" value="{{ details.current_semester or '' }}" required>
47
+ </div>
48
+ <div class="col-md-6">
49
+ <label class="form-label">Class 10th Percentage</label>
50
+ <input type="number" step="0.01" min="0" max="100" class="form-control" name="tenth_percentage" value="{{ details.tenth_percentage or '' }}" required>
51
+ </div>
52
+ <div class="col-md-6">
53
+ <label class="form-label">Class 12th Percentage</label>
54
+ <input type="number" step="0.01" min="0" max="100" class="form-control" name="twelfth_percentage" value="{{ details.twelfth_percentage or '' }}" required>
55
+ </div>
56
+ <div class="col-md-6">
57
+ <label class="form-label">Graduation Percentage</label>
58
+ <input type="number" step="0.01" min="0" max="100" class="form-control" name="graduation_percentage" value="{{ details.graduation_percentage or '' }}" required>
59
+ </div>
60
+ <div class="col-md-6">
61
+ <label class="form-label">Specialization</label>
62
+ <input type="text" class="form-control" name="specialization" value="{{ details.specialization or '' }}" required>
63
+ </div>
64
+ <div class="col-md-6">
65
+ <label class="form-label">Experience Status</label>
66
+ <select class="form-select" name="experience_status" required>
67
+ <option value="">Select</option>
68
+ <option value="Experienced" {% if details.experience_status=='Experienced' %}selected{% endif %}>Experienced</option>
69
+ <option value="Fresher" {% if details.experience_status=='Fresher' %}selected{% endif %}>Fresher</option>
70
+ </select>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ <button type="submit" class="btn btn-primary w-100">{{ 'Update' if details and details.first_name else 'Submit' }}</button>
75
+ </form>
76
+ </div>
77
+ </div>
78
+ <script>
79
+ (function () {
80
+ 'use strict';
81
+ var forms = document.querySelectorAll('.needs-validation');
82
+ Array.prototype.slice.call(forms).forEach(function (form) {
83
+ form.addEventListener('submit', function (event) {
84
+ if (!form.checkValidity()) {
85
+ event.preventDefault();
86
+ event.stopPropagation();
87
+ }
88
+ form.classList.add('was-validated');
89
+ }, false);
90
+ });
91
+ })();
92
+ </script>
93
+ {% endblock %}