create project and create model and dashboard

This commit is contained in:
2025-12-11 10:16:43 +05:30
parent 2371202ac3
commit 2e62320167
55 changed files with 1072 additions and 0 deletions

47
app/__init__.py Normal file
View File

@@ -0,0 +1,47 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from app.config import Config
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
# Register Blueprints
from app.routes.dashboard import dashboard_bp
from app.routes.file_import import file_import_bp
# from app.routes.user import user_bp
app.register_blueprint(dashboard_bp)
app.register_blueprint(file_import_bp)
# app.register_blueprint(user_bp)
from app.routes.subcontractor_routes import subcontractor_bp
app.register_blueprint(subcontractor_bp)
return app
# from flask import Flask
# from app.config import Config
# def create_app():
# app = Flask(__name__)
# app.config.from_object(Config)
# # Register Blueprints
# from app.routes.dashboard import dashboard_bp
# from app.routes.file_import import file_import_bp
# from app.routes.user import user_bp
# app.register_blueprint(dashboard_bp)
# app.register_blueprint(file_import_bp)
# app.register_blueprint(user_bp)
# return app

Binary file not shown.

Binary file not shown.

Binary file not shown.

24
app/config.py Normal file
View File

@@ -0,0 +1,24 @@
import os
class Config:
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:root@localhost/comparisondb"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = "secret123"
UPLOAD_FOLDER = "app/static/uploads/"
ALLOWED_EXTENSIONS = {"xlsx", "xls", "csv"}
# class Config:
# SECRET_KEY = os.getenv("SECRET_KEY", "dev_key_12345")
# UPLOAD_FOLDER = "app/static/uploads/"
# ALLOWED_EXTENSIONS = {"xlsx", "xls", "csv"}
# DB_HOST = "localhost"
# DB_USER = "root"
# DB_PASSWORD = "root"
# DB_NAME = "comparisondb"

0
app/logs/app.log Normal file
View File

0
app/models/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

View File

View File

@@ -0,0 +1,21 @@
from app import db
# from app.services.db_service import db
from datetime import datetime
class Subcontractor(db.Model):
__tablename__ = "subcontractors"
id = db.Column(db.Integer, primary_key=True)
subcontractor_name = db.Column(db.String(255), nullable=False)
address = db.Column(db.String(500))
gst_no = db.Column(db.String(50))
pan_no = db.Column(db.String(50))
mobile_no = db.Column(db.String(20))
email_id = db.Column(db.String(150))
contact_person = db.Column(db.String(150))
status = db.Column(db.String(20), default="Active")
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f"<Subcontractor {self.subcontractor_name}>"

View File

@@ -0,0 +1,73 @@
from app import db
from datetime import datetime
class TrenchExcavation(db.Model):
__tablename__ = "trench_excavation"
id = db.Column(db.Integer, primary_key=True)
# Foreign Key to Subcontractor table
subcontractor_id = db.Column(db.Integer, db.ForeignKey("subcontractors.id"), nullable=False)
# Relationship for easy access (subcontractor.subcontractor_name)
subcontractor = db.relationship("Subcontractor", backref="trench_records")
# Basic Fields
Location = db.Column(db.String(255))
MH_NO = db.Column(db.String(100))
CC_length = db.Column(db.Float)
Invert_Level = db.Column(db.Float)
MH_Top_Level = db.Column(db.Float)
Ground_Level = db.Column(db.Float)
ID_of_MH_m = db.Column(db.Float)
Actual_Trench_Length = db.Column(db.Float)
Pipe_Dia_mm = db.Column(db.Float)
Width_0_to_2_5 = db.Column(db.Float)
Width_2_5_to_3_0 = db.Column(db.Float)
Width_3_0_to_4_5 = db.Column(db.Float)
Width_4_5_to_6_0 = db.Column(db.Float)
Upto_IL_Depth = db.Column(db.Float)
Cutting_Depth = db.Column(db.Float)
Avg_Depth = db.Column(db.Float)
# Excavation categories
Soft_Murum_0_to_1_5 = db.Column(db.Float)
Soft_Murum_1_5_to_3_0 = db.Column(db.Float)
Soft_Murum_3_0_to_4_5 = db.Column(db.Float)
Hard_Murum_0_to_1_5 = db.Column(db.Float)
Hard_Murum_1_5_to_3_0 = db.Column(db.Float)
Soft_Rock_0_to_1_5 = db.Column(db.Float)
Soft_Rock_1_5_to_3_0 = db.Column(db.Float)
Hard_Rock_0_to_1_5 = db.Column(db.Float)
Hard_Rock_1_5_to_3_0 = db.Column(db.Float)
Hard_Rock_3_0_to_4_5 = db.Column(db.Float)
Hard_Rock_4_5_to_6_0 = db.Column(db.Float)
Hard_Rock_6_0_to_7_5 = db.Column(db.Float)
# Totals
Soft_Murum_0_to_1_5_total = db.Column(db.Float)
Soft_Murum_1_5_to_3_0_total = db.Column(db.Float)
Soft_Murum_3_0_to_4_5_total = db.Column(db.Float)
Hard_Murum_0_to_1_5_total = db.Column(db.Float)
Hard_Murum_1_5_and_above_total = db.Column(db.Float)
Soft_Rock_0_to_1_5_total = db.Column(db.Float)
Soft_Rock_1_5_and_above_total = db.Column(db.Float)
Hard_Rock_0_to_1_5_total = db.Column(db.Float)
Hard_Rock_1_5_and_above_total = db.Column(db.Float)
Hard_Rock_3_0_to_4_5_total = db.Column(db.Float)
Hard_Rock_4_5_to_6_0_total = db.Column(db.Float)
Hard_Rock_6_0_to_7_5_total = db.Column(db.Float)
Remarks = db.Column(db.String(500))
Total = db.Column(db.Float)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f"<TrenchExcavation {self.Location}>"

21
app/models/user_model.py Normal file
View File

@@ -0,0 +1,21 @@
# from app.services.db_service import db
# from werkzeug.security import generate_password_hash, check_password_hash
# class User(db.Model):
# id = db.Column(db.Integer, primary_key=True)
# name = db.Column(db.String(120))
# email = db.Column(db.String(120), unique=True)
# password_hash = db.Column(db.String(255))
# def set_password(self, password):
# self.password_hash = generate_password_hash(password)
# def check_password(self, password):
# return check_password_hash(self.password_hash, password)
class User:
def __init__(self, id, name, email):
self.id = id
self.name = name
self.email = email

0
app/routes/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

31
app/routes/auth.py Normal file
View File

@@ -0,0 +1,31 @@
from flask import Blueprint, request, render_template, redirect, flash, session
from app.services.user_service import UserService
auth_bp = Blueprint("auth", __name__, url_prefix="/auth")
# LOGIN PAGE
@auth_bp.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
user = UserService.validate_login(
request.form["email"],
request.form["password"]
)
if user:
session["user_id"] = user.id
return redirect("/dashboard")
flash("Invalid credentials", "danger")
return render_template("login.html")
# REGISTER API ONLY
@auth_bp.route("/register", methods=["POST"])
def register():
data = request.json
UserService.register_user(data["name"], data["email"], data["password"])
return {"message": "User registered successfully"}, 201
# LOGOUT
@auth_bp.route("/logout")
def logout():
session.clear()
return redirect("/auth/login")

8
app/routes/dashboard.py Normal file
View File

@@ -0,0 +1,8 @@
from flask import Blueprint, render_template
dashboard_bp = Blueprint("dashboard", __name__)
@dashboard_bp.route("/")
@dashboard_bp.route("/dashboard")
def dashboard():
return render_template("dashboard.html", title="Dashboard")

21
app/routes/file_import.py Normal file
View File

@@ -0,0 +1,21 @@
from flask import Blueprint, render_template, request, flash
from app.services.file_service import FileService
from app.models.subcontractor_model import Subcontractor
file_import_bp = Blueprint("file_import", __name__, url_prefix="/file")
@file_import_bp.route("/import", methods=["GET", "POST"])
def import_file():
subcontractors = Subcontractor.query.all()
if request.method == "POST":
file = request.files.get("file")
subcontractor_id = request.form.get("subcontractor_id")
file_type = request.form.get("file_type")
service = FileService()
success, msg = service.handle_file_upload(file, subcontractor_id, file_type)
flash(msg, "success" if success else "danger")
return render_template("file_import.html", title="File Import", subcontractors=subcontractors)

View File

@@ -0,0 +1,64 @@
from flask import Blueprint, render_template, request, redirect, flash
from app import db
from app.models.subcontractor_model import Subcontractor
subcontractor_bp = Blueprint("subcontractor", __name__, url_prefix="/subcontractor")
# ---------------- ADD -----------------
@subcontractor_bp.route("/add")
def add_subcontractor():
return render_template("subcontractor/add.html")
@subcontractor_bp.route("/save", methods=["POST"])
def save_subcontractor():
subcontractor = Subcontractor(
subcontractor_name=request.form.get("subcontractor_name"),
contact_person=request.form.get("contact_person"),
mobile_no=request.form.get("mobile_no"),
email_id=request.form.get("email_id"),
gst_no=request.form.get("gst_no")
)
db.session.add(subcontractor)
db.session.commit()
flash("Subcontractor added successfully!", "success")
return redirect("/subcontractor/list")
# ---------------- LIST -----------------
@subcontractor_bp.route("/list")
def subcontractor_list():
subcontractors = Subcontractor.query.all()
return render_template("subcontractor/list.html", subcontractors=subcontractors)
# ---------------- EDIT -----------------
@subcontractor_bp.route("/edit/<int:id>")
def edit_subcontractor(id):
subcontractor = Subcontractor.query.get_or_404(id)
return render_template("subcontractor/edit.html", subcontractor=subcontractor)
# ---------------- UPDATE -----------------
@subcontractor_bp.route("/update/<int:id>", methods=["POST"])
def update_subcontractor(id):
subcontractor = Subcontractor.query.get_or_404(id)
subcontractor.subcontractor_name = request.form.get("subcontractor_name")
subcontractor.contact_person = request.form.get("contact_person")
subcontractor.mobile_no = request.form.get("mobile_no")
subcontractor.email_id = request.form.get("email_id")
subcontractor.gst_no = request.form.get("gst_no")
db.session.commit()
flash("Subcontractor updated successfully!", "success")
return redirect("/subcontractor/list")
# ---------------- DELETE -----------------
@subcontractor_bp.route("/delete/<int:id>")
def delete_subcontractor(id):
subcontractor = Subcontractor.query.get_or_404(id)
db.session.delete(subcontractor)
db.session.commit()
flash("Subcontractor deleted successfully!", "success")
return redirect("/subcontractor/list")

9
app/routes/user.py Normal file
View File

@@ -0,0 +1,9 @@
from flask import Blueprint, render_template
from app.services.user_service import UserService
user_bp = Blueprint("user", __name__, url_prefix="/user")
@user_bp.route("/list")
def list_users():
users = UserService().get_all_users()
return render_template("users.html", users=users, title="Users")

0
app/services/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,19 @@
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy()
migrate = Migrate()
# import mysql.connector
# from app.config import Config
# class DBService:
# def connect(self):
# return mysql.connector.connect(
# host=Config.DB_HOST,
# user=Config.DB_USER,
# password=Config.DB_PASSWORD,
# database=Config.DB_NAME
# )

View File

@@ -0,0 +1,103 @@
# app/services/file_service.py
import os
import pandas as pd
from werkzeug.utils import secure_filename
from app.config import Config
from app import db
from app.models.trench_excavation_model import TrenchExcavation
from app.utils.file_utils import ensure_upload_folder
class FileService:
def allowed_file(self, filename):
return "." in filename and filename.rsplit(".", 1)[1].lower() in Config.ALLOWED_EXTENSIONS
def handle_file_upload(self, file, subcontractor_id, file_type):
if not subcontractor_id:
return False, "Please select subcontractor."
if not file_type:
return False, "Please select file type."
if not file or file.filename == "":
return False, "No file selected."
if not self.allowed_file(file.filename):
return False, "Invalid file type! Allowed: CSV, XLSX, XLS"
ensure_upload_folder()
folder = os.path.join(Config.UPLOAD_FOLDER, f"sub_{subcontractor_id}")
os.makedirs(folder, exist_ok=True)
filename = secure_filename(file.filename)
filepath = os.path.join(folder, filename)
file.save(filepath)
try:
df = pd.read_csv(filepath) if filename.endswith(".csv") else pd.read_excel(filepath)
print("\n=== Uploaded File Preview ===")
print(df.head())
print("=============================\n")
if file_type == "trench_excavation":
return self.process_trench_excavation(df, subcontractor_id)
return True, "File uploaded successfully."
except Exception as e:
return False, f"Processing failed: {e}"
# CLEAN & SAVE TRENCH EXCAVATION DATA
def process_trench_excavation(self, df, subcontractor_id):
# Clean column names (strip whitespace)
df.columns = [str(c).strip() for c in df.columns]
# If the sheet has merged cells -> forward fill Location
if "Location" in df.columns:
df["Location"] = df["Location"].ffill()
# REMOVE empty rows
df = df.dropna(how="all")
# Identify missing location rows before insert
missing_loc = df[df["Location"].isna() | (df["Location"].astype(str).str.strip() == "")]
if not missing_loc.empty:
return False, f"Error: Some rows have empty Location. Rows: {missing_loc.index.tolist()}"
saved_count = 0
try:
for index, row in df.iterrows():
record_data = {}
# Insert only fields that exist in model
for col in df.columns:
if hasattr(TrenchExcavation, col):
value = row[col]
# Normalize empty values
if pd.isna(value) or str(value).strip() in ["", "-", "", "nan", "NaN"]:
value = None
record_data[col] = value
record = TrenchExcavation(
subcontractor_id=subcontractor_id,
**record_data
)
db.session.add(record)
saved_count += 1
db.session.commit()
return True, f"Trench Excavation data saved successfully. Total rows: {saved_count}"
except Exception as e:
db.session.rollback()
return False, f"Trench Excavation Save Failed: {e}"

View File

@@ -0,0 +1,47 @@
# from app.models.user_model import User
# from app.services.db_service import db
# import logging
# class UserService:
# @staticmethod
# def register_user(name, email, password):
# user = User(name=name, email=email)
# user.set_password(password)
# db.session.add(user)
# db.session.commit()
# logging.info(f"New user registered: {email}")
# return user
# @staticmethod
# def validate_login(email, password):
# user = User.query.filter_by(email=email).first()
# if user and user.check_password(password):
# logging.info(f"Login success: {email}")
# return user
# logging.warning(f"Login failed for: {email}")
# return None
# @staticmethod
# def get_all_users():
# return User.query.all()
from app.services.db_service import DBService
from app.models.user_model import User
class UserService:
def get_all_users(self):
db = DBService().connect()
cursor = db.cursor(dictionary=True)
cursor.execute("SELECT id, name, email FROM users")
rows = cursor.fetchall()
users = [User(**row) for row in rows]
cursor.close()
db.close()
return users

View File

@@ -0,0 +1,35 @@
body {
background: #f5f7fa;
font-family: Arial;
padding: 40px;
}
form {
width: 420px;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
label {
margin-top: 10px;
display: block;
font-weight: bold;
}
input,
textarea {
width: 100%;
padding: 8px;
margin-top: 4px;
}
button {
margin-top: 20px;
padding: 10px 20px;
background: #0055ff;
color: #fff;
border: 0;
cursor: pointer;
}

Binary file not shown.

Binary file not shown.

64
app/templates/base.html Normal file
View File

@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title if title else "IndustryBase" }}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body style="background:#f5f7fa;">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">LCEPL</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navMenu">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link" href="/dashboard">Dashboard</a></li>
<li class="nav-item"><a class="nav-link" href="/file/import">File Import</a></li>
<!-- <li class="nav-item"><a class="nav-link" href="/user/list">Users</a></li> -->
<!-- <li class="nav-item"><a class="nav-link" href="/subcontractor/add">Subcontractor</a></li> -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">Subcontractor</a>
<ul class="dropdown-menu dropdown-menu-dark">
<li><a class="dropdown-item" href="/subcontractor/add">Add Subcontractor</a></li>
<li><a class="dropdown-item" href="/subcontractor/list">Subcontractor List</a></li>
</ul>
</li>
<li class="nav-item"><a class="nav-link" href="/user/list">Users</a></li>
</ul>
</div>
</div>
</nav>
<div class="container mt-3">
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show">
{{ message }}
<button class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endwith %}
</div>
<div class="container mt-4">
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block content %}
<h2 class="mb-4">Dashboard</h2>
<div class="card p-4 shadow-sm">
<h5>Welcome to Comparison Project</h5>
<p>This is dashboard panel.</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,39 @@
{% extends "base.html" %}
{% block content %}
<h2 class="mb-4">File Import</h2>
<div class="card p-4 shadow-sm">
<form method="POST" enctype="multipart/form-data">
<!-- 1. SELECT SUBCONTRACTOR -->
<label class="form-label">Select Subcontractor</label>
<select name="subcontractor_id" id="subcontractor_id" class="form-select mb-3" required>
<option value="">-- Select Subcontractor --</option>
{% for sc in subcontractors %}
<option value="{{ sc.id }}">{{ sc.subcontractor_name }}</option>
{% endfor %}
</select>
<!-- 2. FILE TYPE (MODEL NAME) -->
<label class="form-label">Select File Type</label>
<select name="file_type" id="file_type" class="form-select mb-3" required>
<option value="">-- Select File Type --</option>
<option value="subcontractor_sheet">Subcontractor Sheet</option>
<option value="trench_excavation">Trench Excavation (Tr.Ex)</option>
<option value="item_rate_sheet">Item Rate Sheet</option>
<option value="billing_sheet">Billing Sheet</option>
<option value="comparison_sheet">Comparison Sheet</option>
</select>
<!-- 3. FILE UPLOAD -->
<label class="form-label">Choose File</label>
<input type="file" name="file" class="form-control mb-3" required>
<button class="btn btn-primary w-100">Upload</button>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block content %}
<h2 class="mb-4">Users List</h2>
<div class="card p-4 shadow-sm">
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

17
app/templates/login.html Normal file
View File

@@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
<h2 class="mb-4">Login</h2>
<div class="card p-4 shadow-sm">
<form method="POST">
<label>Email:</label>
<input type="email" name="email" class="form-control mb-3" required>
<label>Password:</label>
<input type="password" name="password" class="form-control mb-3" required>
<button class="btn btn-success">Login</button>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block content %}
<h2 class="mb-4">User Registration</h2>
<div class="card p-4 shadow-sm">
<form method="POST">
<label>Name:</label>
<input type="text" name="name" class="form-control mb-3" required>
<label>Email:</label>
<input type="email" name="email" class="form-control mb-3" required>
<label>Password:</label>
<input type="password" name="password" class="form-control mb-3" required>
<button class="btn btn-primary">Register</button>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block content %}
<div class="card shadow-sm p-4">
<h4 class="mb-3">Add New Subcontractor</h4>
<form action="/subcontractor/save" method="POST">
<div class="mb-3">
<label class="form-label">Subcontractor Name</label>
<input type="text" class="form-control" name="subcontractor_name" required>
</div>
<div class="mb-3">
<label class="form-label">Contact Person</label>
<input type="text" class="form-control" name="contact_person">
</div>
<div class="mb-3">
<label class="form-label">Mobile</label>
<input type="text" class="form-control" name="mobile_no">
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" class="form-control" name="email_id">
</div>
<div class="mb-3">
<label class="form-label">GST No</label>
<input type="text" class="form-control" name="gst_no">
</div>
<button class="btn btn-success">Save</button>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,41 @@
{% extends "base.html" %}
{% block content %}
<div class="card shadow-sm p-4">
<h4 class="mb-3">Edit Subcontractor</h4>
<form action="/subcontractor/update/{{ subcontractor.id }}" method="POST">
<div class="mb-3">
<label class="form-label">Subcontractor Name</label>
<input type="text" class="form-control" name="subcontractor_name"
value="{{ subcontractor.subcontractor_name }}" required>
</div>
<div class="mb-3">
<label class="form-label">Contact Person</label>
<input type="text" class="form-control" name="contact_person" value="{{ subcontractor.contact_person }}">
</div>
<div class="mb-3">
<label class="form-label">Mobile</label>
<input type="text" class="form-control" name="mobile_no" value="{{ subcontractor.mobile_no }}">
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" class="form-control" name="email_id" value="{{ subcontractor.email_id }}">
</div>
<div class="mb-3">
<label class="form-label">GST No</label>
<input type="text" class="form-control" name="gst_no" value="{{ subcontractor.gst_no }}">
</div>
<button class="btn btn-success">Update</button>
<a href="/subcontractor/list" class="btn btn-secondary">Back</a>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,38 @@
{% extends "base.html" %}
{% block content %}
<div class="card shadow-sm p-4">
<h4 class="mb-3">Subcontractor List</h4>
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>Mobile</th>
<th>Email</th>
<th>GST No</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for s in subcontractors %}
<tr>
<td>{{ s.id }}</td>
<td>{{ s.subcontractor_name }}</td>
<td>{{ s.mobile_no }}</td>
<td>{{ s.email_id }}</td>
<td>{{ s.gst_no }}</td>
<td>
<a href="/subcontractor/edit/{{ s.id }}" class="btn btn-sm btn-warning">Edit</a>
<a href="/subcontractor/delete/{{ s.id }}" class="btn btn-sm btn-danger"
onclick="return confirm('Are you sure?')">Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

28
app/templates/users.htm Normal file
View File

@@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block content %}
<h2 class="mb-4">Users List</h2>
<div class="card p-4 shadow-sm">
<table class="table table-bordered table-striped">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

Binary file not shown.

6
app/utils/file_utils.py Normal file
View File

@@ -0,0 +1,6 @@
import os
from app.config import Config
def ensure_upload_folder():
if not os.path.exists(Config.UPLOAD_FOLDER):
os.makedirs(Config.UPLOAD_FOLDER)

2
app/utils/helpers.py Normal file
View File

@@ -0,0 +1,2 @@
def is_logged_in(session):
return session.get("user_id") is not None

BIN
instance/comparisondb.db Normal file

Binary file not shown.

200
logs/app.log Normal file
View File

@@ -0,0 +1,200 @@
2025-12-09 13:11:05,606 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
2025-12-09 13:11:05,607 | INFO | Press CTRL+C to quit
2025-12-09 13:11:05,608 | INFO | * Restarting with stat
2025-12-09 13:11:06,239 | WARNING | * Debugger is active!
2025-12-09 13:11:06,240 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:11:48,880 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
2025-12-09 13:11:48,881 | INFO | Press CTRL+C to quit
2025-12-09 13:11:48,882 | INFO | * Restarting with stat
2025-12-09 13:11:49,519 | WARNING | * Debugger is active!
2025-12-09 13:11:49,521 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:12:05,727 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\services\\user_service.py', reloading
2025-12-09 13:12:05,826 | INFO | * Restarting with stat
2025-12-09 13:12:06,499 | WARNING | * Debugger is active!
2025-12-09 13:12:06,501 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:12:09,545 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\config.py', reloading
2025-12-09 13:12:09,654 | INFO | * Restarting with stat
2025-12-09 13:12:10,286 | WARNING | * Debugger is active!
2025-12-09 13:12:10,288 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:12:12,311 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\routes\\auth.py', reloading
2025-12-09 13:12:12,407 | INFO | * Restarting with stat
2025-12-09 13:12:13,071 | WARNING | * Debugger is active!
2025-12-09 13:12:13,072 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:12:16,128 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\config.py', reloading
2025-12-09 13:12:16,257 | INFO | * Restarting with stat
2025-12-09 13:12:16,898 | WARNING | * Debugger is active!
2025-12-09 13:12:16,900 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:12:20,944 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\routes\\user.py', reloading
2025-12-09 13:12:21,042 | INFO | * Restarting with stat
2025-12-09 13:12:21,719 | WARNING | * Debugger is active!
2025-12-09 13:12:21,721 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:12:23,762 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\routes\\file_import.py', reloading
2025-12-09 13:12:23,870 | INFO | * Restarting with stat
2025-12-09 13:12:24,505 | WARNING | * Debugger is active!
2025-12-09 13:12:24,507 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:12:27,561 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\services\\__init__.py', reloading
2025-12-09 13:12:27,670 | INFO | * Restarting with stat
2025-12-09 13:12:28,294 | WARNING | * Debugger is active!
2025-12-09 13:12:28,296 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:12:31,336 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\services\\db_service.py', reloading
2025-12-09 13:12:31,448 | INFO | * Restarting with stat
2025-12-09 13:12:32,097 | WARNING | * Debugger is active!
2025-12-09 13:12:32,099 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:13:05,662 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\config.py', reloading
2025-12-09 13:13:05,773 | INFO | * Restarting with stat
2025-12-09 13:13:06,466 | WARNING | * Debugger is active!
2025-12-09 13:13:06,469 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:13:10,944 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
2025-12-09 13:13:10,944 | INFO | Press CTRL+C to quit
2025-12-09 13:13:10,945 | INFO | * Restarting with stat
2025-12-09 13:13:11,623 | WARNING | * Debugger is active!
2025-12-09 13:13:11,625 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:14:11,295 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\run.py', reloading
2025-12-09 13:14:11,393 | INFO | * Restarting with stat
2025-12-09 13:14:12,004 | WARNING | * Debugger is active!
2025-12-09 13:14:12,006 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:14:32,108 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:14:32,109 | INFO | Press CTRL+C to quit
2025-12-09 13:14:32,110 | INFO | * Restarting with stat
2025-12-09 13:14:32,699 | WARNING | * Debugger is active!
2025-12-09 13:14:32,701 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:15:58,632 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\run.py', reloading
2025-12-09 13:15:58,733 | INFO | * Restarting with stat
2025-12-09 13:15:59,415 | WARNING | * Debugger is active!
2025-12-09 13:15:59,416 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:16:03,475 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\run.py', reloading
2025-12-09 13:16:03,583 | INFO | * Restarting with stat
2025-12-09 13:16:04,204 | WARNING | * Debugger is active!
2025-12-09 13:16:04,206 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:16:33,504 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\run.py', reloading
2025-12-09 13:16:33,605 | INFO | * Restarting with stat
2025-12-09 13:16:34,213 | WARNING | * Debugger is active!
2025-12-09 13:16:34,215 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:16:41,815 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
2025-12-09 13:16:41,816 | INFO | Press CTRL+C to quit
2025-12-09 13:18:12,302 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
2025-12-09 13:18:12,302 | INFO | Press CTRL+C to quit
2025-12-09 13:22:07,114 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:22:07,114 | INFO | Press CTRL+C to quit
2025-12-09 13:22:07,116 | INFO | * Restarting with stat
2025-12-09 13:22:07,935 | WARNING | * Debugger is active!
2025-12-09 13:22:07,937 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:23:21,204 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\run.py', reloading
2025-12-09 13:23:21,305 | INFO | * Restarting with stat
2025-12-09 13:24:06,973 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:24:06,973 | INFO | Press CTRL+C to quit
2025-12-09 13:24:06,974 | INFO | * Restarting with stat
2025-12-09 13:24:07,689 | WARNING | * Debugger is active!
2025-12-09 13:24:07,691 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:24:36,315 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\app.py', reloading
2025-12-09 13:24:36,418 | INFO | * Restarting with stat
2025-12-09 13:24:37,074 | WARNING | * Debugger is active!
2025-12-09 13:24:37,076 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:26:54,442 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\__init__.py', reloading
2025-12-09 13:26:54,543 | INFO | * Restarting with stat
2025-12-09 13:26:59,170 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:26:59,170 | INFO | Press CTRL+C to quit
2025-12-09 13:26:59,171 | INFO | * Restarting with stat
2025-12-09 13:26:59,827 | WARNING | * Debugger is active!
2025-12-09 13:26:59,829 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:28:47,631 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\__init__.py', reloading
2025-12-09 13:28:47,747 | INFO | * Restarting with stat
2025-12-09 13:28:48,478 | WARNING | * Debugger is active!
2025-12-09 13:28:48,480 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:28:51,150 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:28:51,151 | INFO | Press CTRL+C to quit
2025-12-09 13:28:51,153 | INFO | * Restarting with stat
2025-12-09 13:28:51,788 | WARNING | * Debugger is active!
2025-12-09 13:28:51,790 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:28:54,904 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\__init__.py', reloading
2025-12-09 13:28:55,010 | INFO | * Restarting with stat
2025-12-09 13:28:55,608 | WARNING | * Debugger is active!
2025-12-09 13:28:55,610 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:28:56,644 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\__init__.py', reloading
2025-12-09 13:28:56,752 | INFO | * Restarting with stat
2025-12-09 13:29:04,454 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:29:04,454 | INFO | Press CTRL+C to quit
2025-12-09 13:29:04,455 | INFO | * Restarting with stat
2025-12-09 13:29:05,096 | WARNING | * Debugger is active!
2025-12-09 13:29:05,098 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:30:01,657 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:30:01,657 | INFO | Press CTRL+C to quit
2025-12-09 13:30:01,658 | INFO | * Restarting with stat
2025-12-09 13:30:02,278 | WARNING | * Debugger is active!
2025-12-09 13:30:02,280 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:30:27,872 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:30:27,872 | INFO | Press CTRL+C to quit
2025-12-09 13:30:27,873 | INFO | * Restarting with stat
2025-12-09 13:30:28,474 | WARNING | * Debugger is active!
2025-12-09 13:30:28,476 | INFO | * Debugger PIN: 105-645-384
2025-12-09 13:33:22,709 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:33:22,709 | INFO | Press CTRL+C to quit
2025-12-09 13:33:22,710 | INFO | * Restarting with stat
2025-12-09 13:33:23,778 | WARNING | * Debugger is active!
2025-12-09 13:33:23,781 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:33:29,939 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\services\\db_service.py', reloading
2025-12-09 13:33:30,080 | INFO | * Restarting with stat
2025-12-09 13:33:44,462 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:33:44,462 | INFO | Press CTRL+C to quit
2025-12-09 13:33:44,464 | INFO | * Restarting with stat
2025-12-09 13:33:45,216 | WARNING | * Debugger is active!
2025-12-09 13:33:45,218 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:35:23,298 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:35:23,299 | INFO | Press CTRL+C to quit
2025-12-09 13:35:23,301 | INFO | * Restarting with stat
2025-12-09 13:35:24,098 | WARNING | * Debugger is active!
2025-12-09 13:35:24,100 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:38:25,991 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\__init__.py', reloading
2025-12-09 13:38:26,126 | INFO | * Restarting with stat
2025-12-09 13:38:27,120 | WARNING | * Debugger is active!
2025-12-09 13:38:27,122 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:38:37,386 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\config.py', reloading
2025-12-09 13:38:37,513 | INFO | * Restarting with stat
2025-12-09 13:38:38,297 | WARNING | * Debugger is active!
2025-12-09 13:38:38,300 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:38:45,485 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\run.py', reloading
2025-12-09 13:38:45,605 | INFO | * Restarting with stat
2025-12-09 13:38:46,348 | WARNING | * Debugger is active!
2025-12-09 13:38:46,350 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:38:55,109 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:38:55,109 | INFO | Press CTRL+C to quit
2025-12-09 13:38:55,110 | INFO | * Restarting with stat
2025-12-09 13:38:55,959 | WARNING | * Debugger is active!
2025-12-09 13:38:55,961 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:39:27,813 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\__init__.py', reloading
2025-12-09 13:39:27,937 | INFO | * Restarting with stat
2025-12-09 13:39:28,684 | WARNING | * Debugger is active!
2025-12-09 13:39:28,687 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:40:00,602 | INFO | * Detected change in 'C:\\Work\\lcepl_Projects\\Comparison Project\\app\\__init__.py', reloading
2025-12-09 13:40:00,728 | INFO | * Restarting with stat
2025-12-09 13:40:01,428 | WARNING | * Debugger is active!
2025-12-09 13:40:01,430 | INFO | * Debugger PIN: 697-115-033
2025-12-09 13:40:21,531 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 13:40:21,531 | INFO | Press CTRL+C to quit
2025-12-09 13:40:21,533 | INFO | * Restarting with stat
2025-12-09 13:40:22,307 | WARNING | * Debugger is active!
2025-12-09 13:40:22,309 | INFO | * Debugger PIN: 697-115-033
2025-12-09 14:03:58,363 | INFO | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5001
2025-12-09 14:03:58,363 | INFO | Press CTRL+C to quit
2025-12-09 14:03:58,364 | INFO | * Restarting with stat
2025-12-09 14:03:59,038 | WARNING | * Debugger is active!
2025-12-09 14:03:59,041 | INFO | * Debugger PIN: 697-115-033

7
requirements.txt Normal file
View File

@@ -0,0 +1,7 @@
Flask
pandas
openpyxl
xlrd
Werkzeug
python-dotenv
cryptography

9
run.py Normal file
View File

@@ -0,0 +1,9 @@
from app import create_app, db
app = create_app()
with app.app_context():
db.create_all()
if __name__ == "__main__":
app.run(debug=True, port=5001)