Convert into MVC and optimize the code

This commit is contained in:
Pooja Fulari
2026-04-15 12:05:28 +05:30
parent 92670665c3
commit c6f543c63f
538 changed files with 923 additions and 881 deletions

View File

@@ -0,0 +1,64 @@
import os
from flask import request, render_template, current_app
from datetime import datetime
def activity_log_controller():
logs = []
log_file = os.path.join(current_app.root_path, 'activity.log')
if os.path.exists(log_file):
with open(log_file, 'r') as f:
for line in f:
parts = line.strip().split(" | ")
if len(parts) == 4:
logs.append({
"timestamp": parts[0].replace("Timestamp:", "").strip(),
"user": parts[1].replace("User:", "").strip(),
"action": parts[2].replace("Action:", "").strip(),
"details": parts[3].replace("Details:", "").strip()
})
# Filters
start_date = request.args.get("start_date")
end_date = request.args.get("end_date")
username = request.args.get("username")
filtered_logs = logs
# Date filter
if start_date or end_date:
try:
if start_date:
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
else:
start_dt = datetime.min
if end_date:
end_dt = datetime.strptime(end_date, "%Y-%m-%d")
else:
end_dt = datetime.max
filtered_logs = [
log for log in filtered_logs
if start_dt <= datetime.strptime(
log["timestamp"], "%Y-%m-%d %H:%M:%S"
) <= end_dt.replace(hour=23, minute=59, second=59)
]
except Exception as e:
print("Date filter error:", e)
# Username filter
if username:
filtered_logs = [
log for log in filtered_logs
if log["user"].lower() == username.lower()
]
return render_template(
"activity_log.html",
logs=filtered_logs,
start_date=start_date,
end_date=end_date,
username=username
)

View File

@@ -0,0 +1,84 @@
from flask import render_template, request, redirect, url_for, flash
from flask_login import login_user, logout_user, current_user
from ldap3 import Server, Connection, ALL
from app import LDAPUser
def login_controller():
if current_user.is_authenticated:
return redirect(url_for("main.dashboard"))
if request.method == "POST":
username = request.form.get("username", "").strip()
password = request.form.get("password", "")
if not username or not password:
flash("Username and password are required.", "danger")
return render_template("login.html")
ldap_user_dn = f"uid={username},ou=users,dc=lcepl,dc=org"
try:
server = Server("localhost", port=389, get_info=ALL)
conn = Connection(server, user=ldap_user_dn, password=password)
if conn.bind():
user = LDAPUser(dn=ldap_user_dn, username=username, data={})
login_user(user)
flash(f"Welcome, {username}! (LDAP)", "success")
return redirect(url_for("main.dashboard"))
else:
flash("Invalid LDAP credentials", "danger")
except Exception:
if username == "admin" and password == "admin":
user = LDAPUser(dn=None, username=username, data={})
login_user(user)
flash(f"Welcome, {username}! (Local Login)", "success")
return redirect(url_for("main.dashboard"))
else:
flash("LDAP unavailable and local login failed", "danger")
return render_template("login.html")
# @main.route("/login", methods=["GET", "POST"])
# def login():
# # Redirect if already logged in
# if current_user.is_authenticated:
# return redirect(url_for("main.dashboard"))
# if request.method == "POST":
# username = request.form.get("username", "").strip()
# password = request.form.get("password", "")
# if not username or not password:
# flash("Username and password are required.", "danger")
# return render_template("login.html")
# ldap_user_dn = f"uid={username},ou=users,dc=lcepl,dc=org"
# try:
# # Connect to LDAP server
# # server = Server("openldap", port=389, get_info=ALL)
# server = Server("localhost", port=389, get_info=ALL)
# conn = Connection(server, user=ldap_user_dn, password=password)
# if conn.bind():
# # Pass the required 'data' argument
# user = LDAPUser(dn=ldap_user_dn, username=username, data={})
# login_user(user)
# flash(f"Welcome, {username}!", "success")
# return redirect(url_for("main.dashboard"))
# else:
# flash("Invalid LDAP credentials", "danger")
# except Exception as e:
# flash(f"LDAP connection error: {e}", "danger")
# # GET request or failed login
# return render_template("login.html")
def logout_controller():
logout_user()
return redirect(url_for("main.login"))

View File

@@ -0,0 +1,59 @@
from flask import request, render_template
from sqlalchemy import func, cast, Float
from flask_login import login_required
from app.models import Task
from app import db
def dashboard_controller():
selected_block = request.args.getlist('block[]', None)
rate_col = cast(Task.rate, Float)
qty_col = cast(Task.in_this_ra_bill_qty, Float)
query = db.session.query(
Task.block_name.label("block_name"),
Task.village_name.label("village_name"),
func.sum(cast(Task.boq_amount, Float)).label("total_boq_amount"),
func.sum(cast(Task.previous_billing_amount, Float)).label("prev_billed_amount"),
func.sum(cast(Task.variation_amount, Float)).label("total_variation_amount"),
func.sum(cast(Task.cumulative_billed_qty, Float)).label("cumulative_billed_qty"),
func.sum(
cast(Task.cumulative_billed_qty, Float) *
cast(Task.rate, Float)
).label("cumulative_billed_amount"),
func.sum(qty_col).label("in_this_ra_bill_qty"),
func.sum(rate_col * qty_col).label("to_be_claimed_amount")
)
if selected_block and "All" not in selected_block:
query = query.filter(Task.block_name.in_(selected_block))
query = query.group_by(Task.block_name, Task.village_name)
villages = query.all()
village_data = []
for village in villages:
village_data.append({
"block_name": village.block_name,
"village_name": village.village_name,
"total_boq_amount": village.total_boq_amount or 0,
"rate": "-",
"prev_billed_amount": village.prev_billed_amount or 0,
"total_variation_amount": village.total_variation_amount or 0,
"cumulative_billed_qty": village.cumulative_billed_qty or 0,
"cumulative_billed_amount": village.cumulative_billed_amount or 0,
"in_this_ra_bill_qty": village.in_this_ra_bill_qty or 0,
"to_be_claimed_amount": round(village.to_be_claimed_amount or 0, 2)
})
blocks = db.session.query(Task.block_name).distinct().all()
block_list = ["All"] + [block[0] for block in blocks]
return render_template(
'index.html',
villages=village_data,
blocks=block_list,
selected_block=selected_block
)

View File

@@ -0,0 +1,80 @@
from flask import request, jsonify
from flask_login import current_user
from app import db
from app.models import Task, WorkDetail
from app.service.logger import log_activity
def get_blocks_by_district_controller():
district = request.args.get('district')
if not district:
return jsonify({'blocks': []})
blocks = (
db.session.query(Task.block_name)
.filter(Task.district == district)
.distinct()
.all()
)
return jsonify({'blocks': [b[0] for b in blocks]})
def get_tasks_by_block_controller():
block = request.args.get('block')
if not block:
return jsonify({'tasks': []})
tasks = (
db.session.query(Task.task_name)
.filter(Task.block_name == block)
.distinct()
.all()
)
task_list = [
task[0].strip()
.replace(",", "")
.replace("(", "")
.replace(")", "")
.replace(".", "")
.replace("&", "")
.replace("\n", "")
for task in tasks
]
log_activity(
current_user.username,
"Fetch Tasks",
f"Fetched tasks for block {block}"
)
return jsonify({'tasks': task_list})
def get_villages_for_block(block_name):
villages = (
db.session.query(WorkDetail.name_of_village)
.filter(WorkDetail.block == block_name)
.distinct()
.order_by(WorkDetail.name_of_village)
.all()
)
return [v[0] for v in villages if v[0]]
def get_villages_by_block_controller():
block = request.args.get('block')
villages = get_villages_for_block(block)
log_activity(
current_user.username,
"Fetch Villages",
f"Fetched villages for block {block}"
)
return jsonify({'villages': villages})

View File

@@ -0,0 +1,97 @@
from flask import request, render_template
from flask_login import current_user
from app import db
from app.models import Task, WorkDetail
from app.service.logger import log_activity
def filter_tasks_controller():
district = request.args.get('district')
block = request.args.get('block')
village = request.args.get('village')
# districts
districts = [d[0] for d in db.session.query(
WorkDetail.district).distinct()]
# blocks
if district:
blocks = [b[0] for b in db.session.query(WorkDetail.block)
.filter(WorkDetail.district == district).distinct()]
else:
blocks = []
# villages
if district and block:
villages = [v[0] for v in db.session.query(
WorkDetail.name_of_village)
.filter(
WorkDetail.district == district,
WorkDetail.block == block
).distinct()]
else:
villages = []
grouped_tasks = []
if district and block and village:
query = (
db.session.query(Task)
.join(WorkDetail,
Task.village_name == WorkDetail.name_of_village)
.filter(
WorkDetail.district == district,
WorkDetail.block == block,
WorkDetail.name_of_village == village
)
)
tasks = query.order_by(Task.uploaded_at.desc()).all()
current_main_task = None
for task in tasks:
task_data = {
"id": task.id,
"task_name": task.task_name,
"unit": task.unit,
"qty": task.qty,
"rate": task.rate,
"boq_amount": task.boq_amount,
"previous_billed_qty": task.previous_billed_qty,
"previous_billing_amount": task.previous_billing_amount,
"in_this_ra_bill_qty": task.in_this_ra_bill_qty,
"in_this_ra_billing_amount": task.in_this_ra_billing_amount,
"cumulative_billed_qty": task.cumulative_billed_qty,
"cumulative_billed_amount": task.cumulative_billed_amount,
"variation_qty": task.variation_qty,
"variation_amount": task.variation_amount,
"remark": task.remark
}
if task.serial_number:
task_data["subtasks"] = []
grouped_tasks.append(task_data)
current_main_task = task_data
elif current_main_task:
current_main_task["subtasks"].append(task_data)
log_activity(
current_user.username,
"Filter Tasks",
f"district={district}, block={block}, village={village}"
)
return render_template(
'filter_tasks.html',
grouped_tasks=grouped_tasks,
districts=districts,
blocks=blocks,
villages=villages,
selected_district=district,
selected_block=block,
selected_village=village
)

View File

@@ -0,0 +1,61 @@
from flask import request, render_template
from flask_login import current_user
from app import db
from app.models import Task
from app.service.logger import log_activity
def generate_report_page_controller():
selected_district = request.args.get('district')
selected_block = request.args.get('block')
# Get districts
districts = [
d[0] for d in db.session.query(Task.district).distinct().all()
]
# Get blocks
if selected_district:
blocks = [
b[0] for b in db.session.query(Task.block_name)
.filter(Task.district == selected_district)
.distinct().all()
]
else:
blocks = []
# Get main tasks
if selected_block:
main_tasks = db.session.query(Task.task_name).filter(
Task.serial_number.isnot(None),
Task.block_name == selected_block
).distinct().all()
main_tasks = [
task[0].strip()
.replace(",", "")
.replace("(", "")
.replace(")", "")
.replace(".", "")
.replace("&", "")
.replace("\n", "")
for task in main_tasks
]
else:
main_tasks = []
log_activity(
current_user.username,
"Report Page",
f"district={selected_district}, block={selected_block}"
)
return render_template(
'task_report.html',
districts=districts,
blocks=blocks,
main_tasks=main_tasks,
selected_district=selected_district,
selected_block=selected_block
)

View File

@@ -0,0 +1,189 @@
from flask import request, jsonify, render_template
from flask_login import current_user
from app import db
from app.models import Task, WorkDetail
from app.service.logger import log_activity
def recalc_task(task):
rate = float(task.rate or 0)
qty = float(task.qty or 0)
prev_qty = float(task.previous_billed_qty or 0)
ra_qty = float(task.in_this_ra_bill_qty or 0)
task.previous_billing_amount = round(prev_qty * rate, 2)
task.in_this_ra_billing_amount = round(ra_qty * rate, 2)
task.cumulative_billed_qty = round(prev_qty + ra_qty, 2)
task.cumulative_billed_amount = round(task.cumulative_billed_qty * rate, 2)
if task.cumulative_billed_qty > qty:
task.variation_qty = round(task.cumulative_billed_qty - qty, 2)
else:
task.variation_qty = 0
task.variation_amount = round(task.variation_qty * rate, 2)
def update_tasks_controller():
try:
updates = request.get_json()
update_count = 0
formula_fields = [
"previous_billing_amount",
"in_this_ra_billing_amount",
"cumulative_billed_qty",
"cumulative_billed_amount",
"variation_qty",
"variation_amount"
]
for key, new_value in updates.items():
if '_' not in key:
continue
field_name, task_id_str = key.rsplit('_', 1)
if not task_id_str.isdigit():
continue
task = Task.query.get(int(task_id_str))
if task:
if field_name in formula_fields:
continue
current_value = getattr(task, field_name, None)
if str(current_value) != str(new_value):
setattr(task, field_name, new_value)
recalc_task(task)
update_count += 1
log_activity(
current_user.username,
"Task Update",
f"Task ID {task.id} - {field_name} changed to {new_value}"
)
if update_count > 0:
db.session.commit()
log_activity(
current_user.username,
"Database Commit",
f"{update_count} task field(s) updated"
)
return jsonify({'message': f'count: {update_count} field(s) updated.'})
return jsonify({'message': 'No fields were updated.'})
except Exception as e:
log_activity(current_user.username, "Error", str(e))
return jsonify({'error': 'Update failed'}), 500
# # Update tasks route
# @main.route('/update_tasks', methods=['POST'])
# @login_required
# def update_tasks():
# try:
# updates = request.get_json()
# update_count = 0
# for key, new_value in updates.items():
# if '_' not in key:
# continue
# field_name, task_id_str = key.rsplit('_', 1)
# if not task_id_str.isdigit():
# continue
# task_id = int(task_id_str)
# task = db.session.query(Task).filter_by(id=task_id).first()
# if task:
# current_value = getattr(task, field_name, None)
# if current_value != new_value:
# setattr(task, field_name, new_value)
# update_count += 1
# log_activity(current_user.username, "Task Update", f"Task ID {task.id} - {field_name} changed to {new_value}")
# if update_count > 0:
# db.session.commit()
# log_activity(current_user.username, "Database Commit", f"{update_count} task field(s) updated")
# return jsonify({'message': f'count: {update_count} field(s) updated.'})
# else:
# return jsonify({'message': 'No fields were updated.'})
# except Exception as e:
# log_activity(current_user.username, "Error", f"Update tasks error: {str(e)}")
# return jsonify({'error': 'An error occurred while updating tasks.'}), 500
def display_tasks_controller():
work_details = WorkDetail.query.order_by(
WorkDetail.uploaded_at.desc()
).first()
if not work_details:
log_activity(current_user.username, "Tasks View", "No work details")
return "No work details available.", 404
tasks = Task.query.filter_by(
district=work_details.district,
village_name=work_details.name_of_village,
block_name=work_details.block
).order_by(Task.uploaded_at.desc()).all()
grouped_tasks = []
current_main_task = None
for task in tasks:
task_data = {
"id": task.id,
"task_name": task.task_name,
"unit": task.unit,
"qty": task.qty,
"rate": task.rate,
"boq_amount": task.boq_amount,
"previous_billed_qty": task.previous_billed_qty,
"previous_billing_amount": task.previous_billing_amount,
"in_this_ra_bill_qty": task.in_this_ra_bill_qty,
"in_this_ra_billing_amount": task.in_this_ra_billing_amount,
"cumulative_billed_qty": task.cumulative_billed_qty,
"cumulative_billed_amount": task.cumulative_billed_amount,
"variation_qty": task.variation_qty,
"variation_amount": task.variation_amount,
"remark": task.remark,
"district": task.district
}
if task.serial_number:
task_data["subtasks"] = []
grouped_tasks.append(task_data)
current_main_task = task_data
elif current_main_task:
current_main_task["subtasks"].append(task_data)
log_activity(
current_user.username,
"Tasks View",
f"{work_details.name_of_village}, {work_details.block}"
)
return render_template(
'tasks_display.html',
work_details=work_details,
grouped_tasks=grouped_tasks
)

View File

@@ -0,0 +1,214 @@
import os
import pandas as pd
from flask import request, redirect, url_for, current_app
from flask_login import current_user
from app import db
from app.models import Task, WorkDetail
from datetime import datetime
from app.service.logger import log_activity
# keep helper inside controller
def to_2_decimal(value):
try:
if value is None or value == "":
return None
return round(float(value), 2)
except (TypeError, ValueError):
return None
def upload_controller():
if 'file' not in request.files:
return "No file part"
file = request.files['file']
if file.filename == '':
return "No selected file"
filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], file.filename)
file.save(filepath)
log_activity(current_user.username, "File Upload", f"Uploaded file: {file.filename}")
work_details_data = pd.read_excel(filepath, nrows=11, header=None, dtype=str)
work_details_dict = {
"name_of_work": work_details_data.iloc[0, 1],
"cover_agreement_no": work_details_data.iloc[1, 1],
"name_of_contractor": work_details_data.iloc[2, 1],
"name_of_tpi_agency": work_details_data.iloc[3, 1],
"name_of_division": work_details_data.iloc[4, 1],
"name_of_village": work_details_data.iloc[5, 1],
"block": work_details_data.iloc[6, 1],
"scheme_id": work_details_data.iloc[7, 1],
"date_of_billing": work_details_data.iloc[8, 1],
"measurement_book": work_details_data.iloc[9, 1],
"district": work_details_data.iloc[10, 1]
}
work_details_dict = {k: (None if pd.isna(v) else v) for k, v in work_details_dict.items()}
work_detail = WorkDetail(**work_details_dict)
db.session.add(work_detail)
data = pd.read_excel(filepath, skiprows=10)
data = data.astype(object).where(pd.notna(data), None)
expected_columns = [
"serial_number", "task_name", "unit", "qty", "rate", "boq_amount",
"previous_billed_qty", "previous_billing_amount",
"in_this_ra_bill_qty", "in_this_ra_billing_amount",
"cumulative_billed_qty", "cumulative_billed_amount",
"variation_qty", "variation_amount", "remark"
]
if data.shape[1] == len(expected_columns):
data.columns = expected_columns
else:
data.columns = expected_columns[:data.shape[1]]
current_main_task_serial = None
current_main_task_name = None
for _, row in data.iterrows():
task_name = str(row["task_name"]) if row["task_name"] else ""
serial_number = str(row["serial_number"]) if row["serial_number"] else None
if serial_number:
current_main_task_serial = serial_number
current_main_task_name = task_name
parent_id = None
else:
parent_id = current_main_task_serial
task = Task(
district=work_details_dict.get("district"),
block_name=work_details_dict["block"],
village_name=work_details_dict["name_of_village"],
serial_number=serial_number,
task_name=task_name,
unit=row["unit"],
qty=to_2_decimal(row["qty"]),
rate=to_2_decimal(row["rate"]),
boq_amount=to_2_decimal(row["boq_amount"]),
previous_billed_qty=to_2_decimal(row["previous_billed_qty"]),
previous_billing_amount=to_2_decimal(row["previous_billing_amount"]),
in_this_ra_bill_qty=to_2_decimal(row["in_this_ra_bill_qty"]),
in_this_ra_billing_amount=to_2_decimal(row["in_this_ra_billing_amount"]),
cumulative_billed_qty=to_2_decimal(row["cumulative_billed_qty"]),
cumulative_billed_amount=to_2_decimal(row["cumulative_billed_amount"]),
variation_qty=to_2_decimal(row["variation_qty"]),
variation_amount=to_2_decimal(row["variation_amount"]),
parent_id=parent_id,
parent_task_name=current_main_task_name if not serial_number else None,
remark=row["remark"]
)
db.session.add(task)
db.session.commit()
log_activity(
current_user.username,
"Database Insert",
f"Inserted work details and tasks from {file.filename}"
)
return redirect(url_for('main.display_tasks'))
# # File upload route
# @main.route('/upload', methods=['POST'])
# @login_required
# def upload():
# if 'file' not in request.files:
# return "No file part"
# file = request.files['file']
# if file.filename == '':
# return "No selected file"
# if file:
# filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], file.filename)
# file.save(filepath)
# log_activity(current_user.username, "File Upload", f"Uploaded file: {file.filename}")
# # Read work details (first 11 rows)
# work_details_data = pd.read_excel(filepath, nrows=11, header=None)
# work_details_dict = {
# "name_of_work": work_details_data.iloc[0, 1],
# "cover_agreement_no": work_details_data.iloc[1, 1],
# "name_of_contractor": work_details_data.iloc[2, 1],
# "name_of_tpi_agency": work_details_data.iloc[3, 1],
# "name_of_division": work_details_data.iloc[4, 1],
# "name_of_village": work_details_data.iloc[5, 1],
# "block": work_details_data.iloc[6, 1],
# "scheme_id": work_details_data.iloc[7, 1],
# "date_of_billing": work_details_data.iloc[8, 1],
# "measurement_book": work_details_data.iloc[9, 1],
# "district": work_details_data.iloc[10, 1] # Example: row 11 (index 10), column 2 (index 1)
# }
# work_details_dict = {key: (None if pd.isna(value) else value) for key, value in work_details_dict.items()}
# work_detail = WorkDetail(**work_details_dict)
# db.session.add(work_detail)
# # Read task data starting from row 12
# data = pd.read_excel(filepath, skiprows=10)
# data = data.astype(str).replace({"nan": None, "NaT": None, "None": None})
# expected_columns = [
# "serial_number", "task_name", "unit", "qty", "rate", "boq_amount",
# "previous_billed_qty", "previous_billing_amount",
# "in_this_ra_bill_qty", "in_this_ra_billing_amount",
# "cumulative_billed_qty", "cumulative_billed_amount",
# "variation_qty", "variation_amount", "remark"
# ]
# if data.shape[1] == len(expected_columns):
# data.columns = expected_columns
# else:
# data.columns = expected_columns[:data.shape[1]] # Truncate
# current_main_task_serial = None
# current_main_task_name = None
# for _, row in data.iterrows():
# task_name = str(row["task_name"]) if row["task_name"] else ""
# serial_number = row["serial_number"]
# if serial_number: # Main task
# current_main_task_serial = serial_number
# current_main_task_name = task_name
# parent_id = None
# else: # Subtask
# parent_id = current_main_task_serial
# task = Task(
# district=work_details_dict.get("district"),
# block_name=work_details_dict["block"],
# village_name=work_details_dict["name_of_village"],
# serial_number=serial_number,
# task_name=task_name,
# unit=row["unit"],
# qty=row["qty"],
# rate=row["rate"],
# boq_amount=row["boq_amount"],
# previous_billed_qty=row["previous_billed_qty"],
# previous_billing_amount=row["previous_billing_amount"],
# in_this_ra_bill_qty=row["in_this_ra_bill_qty"],
# in_this_ra_billing_amount=row["in_this_ra_billing_amount"],
# cumulative_billed_qty=row["cumulative_billed_qty"],
# cumulative_billed_amount=row["cumulative_billed_amount"],
# variation_qty=row["variation_qty"],
# variation_amount=row["variation_amount"],
# parent_id=parent_id,
# parent_task_name=current_main_task_name if not serial_number else None,
# remark=row["remark"]
# )
# db.session.add(task)
# db.session.commit()
# log_activity(current_user.username, "Database Insert", f"Inserted work details and tasks from {file.filename}")
# return redirect(url_for('main.display_tasks'))

View File

@@ -4284,3 +4284,28 @@ Timestamp: 2026-04-14 16:24:01 | User: admin | Action: Filter Tasks | Details: F
Timestamp: 2026-04-14 16:24:48 | User: admin | Action: Task Update | Details: Task ID 7 - previous_billed_qty changed to 2
Timestamp: 2026-04-14 16:24:48 | User: admin | Action: Database Commit | Details: 1 task field(s) updated
Timestamp: 2026-04-14 16:24:50 | User: admin | Action: Filter Tasks | Details: Filtered tasks for district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:43:16 | User: admin | Action: Page Load | Details: Upload Excel page accessed
Timestamp: 2026-04-15 11:43:18 | User: admin | Action: Page Load | Details: Upload Excel page accessed
Timestamp: 2026-04-15 11:43:20 | User: admin | Action: Report Page | Details: district=None, block=None
Timestamp: 2026-04-15 11:43:26 | User: admin | Action: Fetch Tasks | Details: Fetched tasks for block Kandhla
Timestamp: 2026-04-15 11:43:29 | User: admin | Action: Report Page | Details: district=None, block=None
Timestamp: 2026-04-15 11:43:35 | User: admin | Action: Fetch Tasks | Details: Fetched tasks for block Kandhla
Timestamp: 2026-04-15 11:44:25 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:44:48 | User: admin | Action: Task Update | Details: Task ID 6 - in_this_ra_bill_qty changed to 4
Timestamp: 2026-04-15 11:44:48 | User: admin | Action: Database Commit | Details: 1 task field(s) updated
Timestamp: 2026-04-15 11:44:51 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:45:30 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:49:52 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:50:23 | User: admin | Action: Task Update | Details: Task ID 5 - qty changed to 2
Timestamp: 2026-04-15 11:50:23 | User: admin | Action: Database Commit | Details: 1 task field(s) updated
Timestamp: 2026-04-15 11:50:25 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:50:43 | User: admin | Action: Task Update | Details: Task ID 5 - qty changed to 1
Timestamp: 2026-04-15 11:50:43 | User: admin | Action: Database Commit | Details: 1 task field(s) updated
Timestamp: 2026-04-15 11:50:44 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:51:05 | User: admin | Action: Task Update | Details: Task ID 5 - previous_billed_qty changed to 3
Timestamp: 2026-04-15 11:51:05 | User: admin | Action: Database Commit | Details: 1 task field(s) updated
Timestamp: 2026-04-15 11:51:07 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:51:20 | User: admin | Action: Task Update | Details: Task ID 5 - previous_billed_qty changed to 1
Timestamp: 2026-04-15 11:51:20 | User: admin | Action: Database Commit | Details: 1 task field(s) updated
Timestamp: 2026-04-15 11:51:21 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi
Timestamp: 2026-04-15 11:55:04 | User: admin | Action: Filter Tasks | Details: district=Shamli, block=Kandhla, village=Aldi

View File

@@ -1,68 +0,0 @@
# from flask import Flask
# from flask_sqlalchemy import SQLAlchemy
# from flask_migrate import Migrate
# from flask_login import LoginManager
# from flask_ldap3_login import LDAP3LoginManager
# import os
# db = SQLAlchemy()
# migrate = Migrate()
# login_manager = LoginManager()
# ldap_manager = LDAP3LoginManager()
# from app.models import User
# # Flask-Login user loader
# @login_manager.user_loader
# def load_user(user_id):
# return User.query.get(int(user_id))
# def create_app():
# app = Flask(__name__)
# # --------------------
# # App config
# # --------------------
# app.config['SECRET_KEY'] = 'dev-secret-key' # 🔐 change this in production
# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:admin@localhost/excel_data7'
# # 🔽 Add upload folder config
# app.config['UPLOAD_FOLDER'] = os.path.join(app.root_path, 'upload')
# # Make sure folder exists
# os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
# # --------------------
# # LDAP config
# # --------------------
# app.config['LDAP_HOST'] = 'openldap'
# app.config['LDAP_PORT'] = 389
# app.config['LDAP_BASE_DN'] = 'dc=lcepl,dc=org'
# app.config['LDAP_BIND_USER_DN'] = 'cn=admin,dc=lcepl,dc=org'
# app.config['LDAP_BIND_USER_PASSWORD'] = 'admin123'
# app.config['LDAP_USER_DN'] = 'ou=users'
# app.config['LDAP_GROUP_DN'] = 'ou=groups'
# app.config['LDAP_USER_RDN_ATTR'] = 'uid'
# app.config['LDAP_USER_LOGIN_ATTR'] = 'uid'
# # Extra to avoid BasicAuth popup
# app.config['USE_LDAP_AUTH'] = True
# app.config['LDAP_REQUIRE_CERT'] = False
# app.config['LDAP_LOGIN_VIEW'] = 'main.login' # your login route
# # --------------------
# # Init extensions
# # --------------------
# db.init_app(app)
# migrate.init_app(app, db)
# login_manager.init_app(app)
# ldap_manager.init_app(app)
# # Redirect to login if not authenticated
# login_manager.login_view = "main.login"
# # --------------------
# # Register blueprints
# # --------------------
# from app.routes.main import main
# app.register_blueprint(main)
# return app

View File

@@ -1,10 +0,0 @@
# from flask_login import UserMixin
# # --------------------
# # LDAP User class (not stored in DB)
# # --------------------
# class LDAPUser(UserMixin):
# """Represents an authenticated LDAP user (kept in session)."""
# def __init__(self, dn, username):
# self.dn = dn
# self.username = username
# self.id = username # 🔑 Use username for session id (stable)

View File

@@ -1,107 +1,29 @@
from flask import Blueprint, render_template, request, redirect, url_for, flash,jsonify
from flask_login import login_user, logout_user, login_required, current_user
from sqlalchemy import func, cast, Float
from app.models import Task,WorkDetail
import pandas as pd
from werkzeug.security import generate_password_hash
import os
from app import LDAPUser
import numpy as np
from flask import Blueprint, render_template
from flask_login import login_required, current_user
from flask import current_app
from datetime import datetime
from app import db
from app.models import User
from ldap3 import Server, Connection, ALL # ✅ make sure this is at the top
from app.service.logger import log_activity
from app.Controllers.dashboard_controller import dashboard_controller
from app.Controllers.auth_controller import (login_controller,logout_controller)
from app.Controllers.upload_controller import upload_controller
from app.Controllers.activity_controller import activity_log_controller
from app.Controllers.task_controller import (update_tasks_controller,display_tasks_controller)
from app.Controllers.filter_controller import filter_tasks_controller
from app.Controllers.report_controller import generate_report_page_controller
from app.Controllers.dropdown_controller import (get_blocks_by_district_controller,get_tasks_by_block_controller,get_villages_by_block_controller)
main = Blueprint("main", __name__)
# @main.route("/login", methods=["GET", "POST"])
# def login():
# # Redirect if already logged in
# if current_user.is_authenticated:
# return redirect(url_for("main.dashboard"))
# if request.method == "POST":
# username = request.form.get("username", "").strip()
# password = request.form.get("password", "")
# if not username or not password:
# flash("Username and password are required.", "danger")
# return render_template("login.html")
# ldap_user_dn = f"uid={username},ou=users,dc=lcepl,dc=org"
# try:
# # Connect to LDAP server
# # server = Server("openldap", port=389, get_info=ALL)
# server = Server("localhost", port=389, get_info=ALL)
# conn = Connection(server, user=ldap_user_dn, password=password)
# if conn.bind():
# # Pass the required 'data' argument
# user = LDAPUser(dn=ldap_user_dn, username=username, data={})
# login_user(user)
# flash(f"Welcome, {username}!", "success")
# return redirect(url_for("main.dashboard"))
# else:
# flash("Invalid LDAP credentials", "danger")
# except Exception as e:
# flash(f"LDAP connection error: {e}", "danger")
# # GET request or failed login
# return render_template("login.html")
@main.route("/login", methods=["GET", "POST"])
def login():
# Redirect if already logged in
if current_user.is_authenticated:
return redirect(url_for("main.dashboard"))
return login_controller()
if request.method == "POST":
username = request.form.get("username", "").strip()
password = request.form.get("password", "")
if not username or not password:
flash("Username and password are required.", "danger")
return render_template("login.html")
ldap_user_dn = f"uid={username},ou=users,dc=lcepl,dc=org"
try:
# Try LDAP authentication first
server = Server("localhost", port=389, get_info=ALL)
conn = Connection(server, user=ldap_user_dn, password=password)
if conn.bind():
user = LDAPUser(dn=ldap_user_dn, username=username, data={})
login_user(user)
flash(f"Welcome, {username}! (LDAP)", "success")
return redirect(url_for("main.dashboard"))
else:
flash("Invalid LDAP credentials", "danger")
except Exception as e:
# Fallback to LOCAL login if LDAP not available
if username == "admin" and password == "admin":
user = LDAPUser(dn=None, username=username, data={})
login_user(user)
flash(f"Welcome, {username}! (Local Login)", "success")
return redirect(url_for("main.dashboard"))
else:
flash("LDAP unavailable and local login failed", "danger")
return render_template("login.html")
@main.route('/logout')
@main.route("/logout")
@login_required
def logout():
logout_user()
# flash("You have been logged out.", "info")
return redirect(url_for("main.login"))
return logout_controller()
# Home route
@main.route('/upload_excel')
@login_required
def upload_excel():
@@ -113,722 +35,63 @@ def upload_excel():
# def upload_file():
# return render_template('dashboard.html')
# # File upload route
# @main.route('/upload', methods=['POST'])
# @login_required
# def upload():
# if 'file' not in request.files:
# return "No file part"
# file = request.files['file']
# if file.filename == '':
# return "No selected file"
# if file:
# filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], file.filename)
# file.save(filepath)
# log_activity(current_user.username, "File Upload", f"Uploaded file: {file.filename}")
# # Read work details (first 11 rows)
# work_details_data = pd.read_excel(filepath, nrows=11, header=None)
# work_details_dict = {
# "name_of_work": work_details_data.iloc[0, 1],
# "cover_agreement_no": work_details_data.iloc[1, 1],
# "name_of_contractor": work_details_data.iloc[2, 1],
# "name_of_tpi_agency": work_details_data.iloc[3, 1],
# "name_of_division": work_details_data.iloc[4, 1],
# "name_of_village": work_details_data.iloc[5, 1],
# "block": work_details_data.iloc[6, 1],
# "scheme_id": work_details_data.iloc[7, 1],
# "date_of_billing": work_details_data.iloc[8, 1],
# "measurement_book": work_details_data.iloc[9, 1],
# "district": work_details_data.iloc[10, 1] # Example: row 11 (index 10), column 2 (index 1)
# }
# work_details_dict = {key: (None if pd.isna(value) else value) for key, value in work_details_dict.items()}
# work_detail = WorkDetail(**work_details_dict)
# db.session.add(work_detail)
# # Read task data starting from row 12
# data = pd.read_excel(filepath, skiprows=10)
# data = data.astype(str).replace({"nan": None, "NaT": None, "None": None})
# expected_columns = [
# "serial_number", "task_name", "unit", "qty", "rate", "boq_amount",
# "previous_billed_qty", "previous_billing_amount",
# "in_this_ra_bill_qty", "in_this_ra_billing_amount",
# "cumulative_billed_qty", "cumulative_billed_amount",
# "variation_qty", "variation_amount", "remark"
# ]
# if data.shape[1] == len(expected_columns):
# data.columns = expected_columns
# else:
# data.columns = expected_columns[:data.shape[1]] # Truncate
# current_main_task_serial = None
# current_main_task_name = None
# for _, row in data.iterrows():
# task_name = str(row["task_name"]) if row["task_name"] else ""
# serial_number = row["serial_number"]
# if serial_number: # Main task
# current_main_task_serial = serial_number
# current_main_task_name = task_name
# parent_id = None
# else: # Subtask
# parent_id = current_main_task_serial
# task = Task(
# district=work_details_dict.get("district"),
# block_name=work_details_dict["block"],
# village_name=work_details_dict["name_of_village"],
# serial_number=serial_number,
# task_name=task_name,
# unit=row["unit"],
# qty=row["qty"],
# rate=row["rate"],
# boq_amount=row["boq_amount"],
# previous_billed_qty=row["previous_billed_qty"],
# previous_billing_amount=row["previous_billing_amount"],
# in_this_ra_bill_qty=row["in_this_ra_bill_qty"],
# in_this_ra_billing_amount=row["in_this_ra_billing_amount"],
# cumulative_billed_qty=row["cumulative_billed_qty"],
# cumulative_billed_amount=row["cumulative_billed_amount"],
# variation_qty=row["variation_qty"],
# variation_amount=row["variation_amount"],
# parent_id=parent_id,
# parent_task_name=current_main_task_name if not serial_number else None,
# remark=row["remark"]
# )
# db.session.add(task)
# db.session.commit()
# log_activity(current_user.username, "Database Insert", f"Inserted work details and tasks from {file.filename}")
# return redirect(url_for('main.display_tasks'))
def to_2_decimal(value):
try:
if value is None or value == "":
return None
return round(float(value), 2)
except (TypeError, ValueError):
return None
@main.route('/upload', methods=['POST'])
@login_required
def upload():
if 'file' not in request.files:
return "No file part"
file = request.files['file']
if file.filename == '':
return "No selected file"
return upload_controller()
if file:
filepath = os.path.join(current_app.config['UPLOAD_FOLDER'], file.filename)
file.save(filepath)
log_activity(current_user.username, "File Upload", f"Uploaded file: {file.filename}")
# Read work details (first 11 rows)
# work_details_data = pd.read_excel(filepath, nrows=11, header=None)
work_details_data = pd.read_excel(filepath, nrows=11, header=None, dtype=str)
work_details_dict = {
"name_of_work": work_details_data.iloc[0, 1],
"cover_agreement_no": work_details_data.iloc[1, 1],
"name_of_contractor": work_details_data.iloc[2, 1],
"name_of_tpi_agency": work_details_data.iloc[3, 1],
"name_of_division": work_details_data.iloc[4, 1],
"name_of_village": work_details_data.iloc[5, 1],
"block": work_details_data.iloc[6, 1],
"scheme_id": work_details_data.iloc[7, 1],
"date_of_billing": work_details_data.iloc[8, 1],
"measurement_book": work_details_data.iloc[9, 1],
"district": work_details_data.iloc[10, 1]
}
# Clean work details NaN
work_details_dict = {k: (None if pd.isna(v) else v) for k, v in work_details_dict.items()}
work_detail = WorkDetail(**work_details_dict)
db.session.add(work_detail)
# Read task data
# data = pd.read_excel(filepath, skiprows=10)
data = pd.read_excel(filepath, skiprows=10)
# ✅ Convert all NaN → None (critical for MySQL)
data = data.astype(object).where(pd.notna(data), None)
expected_columns = [
"serial_number", "task_name", "unit", "qty", "rate", "boq_amount",
"previous_billed_qty", "previous_billing_amount",
"in_this_ra_bill_qty", "in_this_ra_billing_amount",
"cumulative_billed_qty", "cumulative_billed_amount",
"variation_qty", "variation_amount", "remark"
]
if data.shape[1] == len(expected_columns):
data.columns = expected_columns
else:
data.columns = expected_columns[:data.shape[1]]
current_main_task_serial = None
current_main_task_name = None
for _, row in data.iterrows():
task_name = str(row["task_name"]) if row["task_name"] else ""
serial_number = str(row["serial_number"]) if row["serial_number"] else None
if serial_number:
current_main_task_serial = serial_number
current_main_task_name = task_name
parent_id = None
else:
parent_id = current_main_task_serial
task = Task(
district=work_details_dict.get("district"),
block_name=work_details_dict["block"],
village_name=work_details_dict["name_of_village"],
serial_number=serial_number,
task_name=task_name,
unit=row["unit"],
qty=to_2_decimal(row["qty"]),
rate=to_2_decimal(row["rate"]),
boq_amount=to_2_decimal(row["boq_amount"]),
previous_billed_qty=to_2_decimal(row["previous_billed_qty"]),
previous_billing_amount=to_2_decimal(row["previous_billing_amount"]),
in_this_ra_bill_qty=to_2_decimal(row["in_this_ra_bill_qty"]),
in_this_ra_billing_amount=to_2_decimal(row["in_this_ra_billing_amount"]),
cumulative_billed_qty=to_2_decimal(row["cumulative_billed_qty"]),
cumulative_billed_amount=to_2_decimal(row["cumulative_billed_amount"]),
variation_qty=to_2_decimal(row["variation_qty"]),
variation_amount=to_2_decimal(row["variation_amount"]),
parent_id=parent_id,
parent_task_name=current_main_task_name if not serial_number else None,
remark=row["remark"]
)
db.session.add(task)
db.session.commit()
log_activity(current_user.username, "Database Insert", f"Inserted work details and tasks from {file.filename}")
return redirect(url_for('main.display_tasks'))
# # Update tasks route
# @main.route('/update_tasks', methods=['POST'])
# @login_required
# def update_tasks():
# try:
# updates = request.get_json()
# update_count = 0
# for key, new_value in updates.items():
# if '_' not in key:
# continue
# field_name, task_id_str = key.rsplit('_', 1)
# if not task_id_str.isdigit():
# continue
# task_id = int(task_id_str)
# task = db.session.query(Task).filter_by(id=task_id).first()
# if task:
# current_value = getattr(task, field_name, None)
# if current_value != new_value:
# setattr(task, field_name, new_value)
# update_count += 1
# log_activity(current_user.username, "Task Update", f"Task ID {task.id} - {field_name} changed to {new_value}")
# if update_count > 0:
# db.session.commit()
# log_activity(current_user.username, "Database Commit", f"{update_count} task field(s) updated")
# return jsonify({'message': f'count: {update_count} field(s) updated.'})
# else:
# return jsonify({'message': 'No fields were updated.'})
# except Exception as e:
# log_activity(current_user.username, "Error", f"Update tasks error: {str(e)}")
# return jsonify({'error': 'An error occurred while updating tasks.'}), 500
def recalc_task(task):
rate = float(task.rate or 0)
qty = float(task.qty or 0)
prev_qty = float(task.previous_billed_qty or 0)
ra_qty = float(task.in_this_ra_bill_qty or 0)
# H = G * E
task.previous_billing_amount = round(prev_qty * rate, 2)
# J = I * E
task.in_this_ra_billing_amount = round(ra_qty * rate, 2)
# K = I + G
task.cumulative_billed_qty = round(prev_qty + ra_qty, 2)
# L = K * E
task.cumulative_billed_amount = round(task.cumulative_billed_qty * rate, 2)
# M = IF(K > D , K - D , 0)
if task.cumulative_billed_qty > qty:
task.variation_qty = round(task.cumulative_billed_qty - qty, 2)
else:
task.variation_qty = 0
# N = M * E
task.variation_amount = round(task.variation_qty * rate, 2)
@main.route('/update_tasks', methods=['POST'])
@login_required
def update_tasks():
try:
updates = request.get_json()
update_count = 0
return update_tasks_controller()
# fields that should NOT be manually edited
formula_fields = [
"previous_billing_amount",
"in_this_ra_billing_amount",
"cumulative_billed_qty",
"cumulative_billed_amount",
"variation_qty",
"variation_amount"
]
for key, new_value in updates.items():
if '_' not in key:
continue
field_name, task_id_str = key.rsplit('_', 1)
if not task_id_str.isdigit():
continue
task_id = int(task_id_str)
task = db.session.query(Task).filter_by(id=task_id).first()
if task:
# ❌ Skip manual update for formula fields
if field_name in formula_fields:
continue
current_value = getattr(task, field_name, None)
if str(current_value) != str(new_value):
setattr(task, field_name, new_value)
# 🔥 auto recalc after any change
recalc_task(task)
update_count += 1
log_activity(
current_user.username,
"Task Update",
f"Task ID {task.id} - {field_name} changed to {new_value}"
)
if update_count > 0:
db.session.commit()
log_activity(
current_user.username,
"Database Commit",
f"{update_count} task field(s) updated"
)
return jsonify({'message': f'count: {update_count} field(s) updated.'})
else:
return jsonify({'message': 'No fields were updated.'})
except Exception as e:
log_activity(current_user.username, "Error", f"Update tasks error: {str(e)}")
return jsonify({'error': 'An error occurred while updating tasks.'}), 500
# Display tasks route
@main.route('/tasks')
@login_required
def display_tasks():
work_details = WorkDetail.query.order_by(WorkDetail.uploaded_at.desc()).first()
if not work_details:
log_activity(current_user.username, "Tasks View", "No work details available")
return "No work details available.", 404
tasks = Task.query.filter_by(
district=work_details.district,
village_name=work_details.name_of_village,
block_name=work_details.block
).order_by(Task.uploaded_at.desc()).all()
grouped_tasks = []
current_main_task = None
for task in tasks:
task_data = {
"id": task.id,
"task_name": task.task_name,
"unit": task.unit,
"qty": task.qty,
"rate": task.rate,
"boq_amount": task.boq_amount,
"previous_billed_qty": task.previous_billed_qty,
"previous_billing_amount": task.previous_billing_amount,
"in_this_ra_bill_qty": task.in_this_ra_bill_qty,
"in_this_ra_billing_amount": task.in_this_ra_billing_amount,
"cumulative_billed_qty": task.cumulative_billed_qty,
"cumulative_billed_amount": task.cumulative_billed_amount,
"variation_qty": task.variation_qty,
"variation_amount": task.variation_amount,
"remark": task.remark,
"district": task.district
}
if task.serial_number:
task_data["subtasks"] = []
grouped_tasks.append(task_data)
current_main_task = task_data
elif current_main_task:
current_main_task["subtasks"].append(task_data)
log_activity(current_user.username, "Tasks View", f"Displayed tasks for {work_details.name_of_village}, {work_details.block}")
return render_template('tasks_display.html', work_details=work_details, grouped_tasks=grouped_tasks)
return display_tasks_controller()
@main.route('/')
@login_required
def dashboard():
selected_block = request.args.getlist('block[]', None)
return dashboard_controller()
rate_col = cast(Task.rate, Float)
qty_col = cast(Task.in_this_ra_bill_qty, Float)
query = db.session.query(
Task.block_name.label("block_name"),
Task.village_name.label("village_name"),
func.sum(cast(Task.boq_amount, Float)).label("total_boq_amount"),
func.sum(cast(Task.previous_billing_amount, Float)).label("prev_billed_amount"),
func.sum(cast(Task.variation_amount, Float)).label("total_variation_amount"),
func.sum(cast(Task.cumulative_billed_qty, Float)).label("cumulative_billed_qty"),
func.sum(cast(Task.cumulative_billed_qty, Float) * cast(Task.rate, Float)).label("cumulative_billed_amount"),
func.sum(qty_col).label("in_this_ra_bill_qty"),
func.sum(rate_col * qty_col).label("to_be_claimed_amount")
)
if selected_block and "All" not in selected_block:
query = query.filter(Task.block_name.in_(selected_block))
query = query.group_by(Task.block_name, Task.village_name)
villages = query.all()
village_data = []
for village in villages:
village_data.append({
"block_name": village.block_name,
"village_name": village.village_name,
"total_boq_amount": village.total_boq_amount or 0,
"rate": "-",
"prev_billed_amount": village.prev_billed_amount or 0,
"total_variation_amount": village.total_variation_amount or 0,
"cumulative_billed_qty": village.cumulative_billed_qty or 0,
"cumulative_billed_amount": village.cumulative_billed_amount or 0,
"in_this_ra_bill_qty": village.in_this_ra_bill_qty or 0,
"to_be_claimed_amount": round(village.to_be_claimed_amount or 0, 2)
})
blocks = db.session.query(Task.block_name).distinct().all()
block_list = ["All"] + [block[0] for block in blocks]
# log_activity(current_user.username, "Dashboard View", "Dashboard accessed")
return render_template('index.html', villages=village_data, blocks=block_list, selected_block=selected_block)
# @main.route('/generate_report_page')
# @login_required
# def generate_report_page():
# blocks = db.session.query(Task.block_name).distinct().all()
# blocks = [block.block_name for block in blocks]
# selected_block = request.args.get('block')
# if selected_block:
# main_tasks = db.session.query(Task.task_name).filter(
# Task.serial_number.isnot(None),
# Task.block_name == selected_block
# ).distinct().all()
# main_tasks = [task.task_name.strip().replace(",", "").replace("(", "").replace(")", "").replace(".", "").replace("&", "").replace("\n", "") for task in main_tasks]
# else:
# main_tasks = db.session.query(Task.task_name).filter(Task.serial_number.isnot(None)).distinct().all()
# main_tasks = [task.task_name.strip().replace(",", "").replace("(", "").replace(")", "").replace(".", "").replace("&", "").replace("\n", "") for task in main_tasks]
# log_activity(current_user.username, "Report Page", f"Report generation page accessed (block={selected_block})")
# return render_template('task_report.html', main_tasks=main_tasks, blocks=blocks)
@main.route('/get_blocks_by_district')
@login_required
def get_blocks_by_district():
district = request.args.get('district')
return get_blocks_by_district_controller()
if not district:
return jsonify({'blocks': []})
blocks = db.session.query(Task.block_name)\
.filter(Task.district == district)\
.distinct().all()
@main.route('/get_tasks_by_block')
@login_required
def get_tasks_by_block():
return get_tasks_by_block_controller()
@main.route('/get_villages_by_block')
@login_required
def get_villages_by_block():
return get_villages_by_block_controller()
return jsonify({'blocks': [b[0] for b in blocks]})
@main.route('/generate_report_page')
@login_required
def generate_report_page():
selected_district = request.args.get('district')
selected_block = request.args.get('block')
# ✅ Get all districts
districts = [d[0] for d in db.session.query(Task.district).distinct().all()]
# ✅ Get blocks based on district
if selected_district:
blocks = [b[0] for b in db.session.query(Task.block_name)
.filter(Task.district == selected_district)
.distinct().all()]
else:
blocks = []
# ✅ Get main tasks based on block
if selected_block:
main_tasks = db.session.query(Task.task_name).filter(
Task.serial_number.isnot(None),
Task.block_name == selected_block
).distinct().all()
main_tasks = [
task[0].strip()
.replace(",", "")
.replace("(", "")
.replace(")", "")
.replace(".", "")
.replace("&", "")
.replace("\n", "")
for task in main_tasks
]
else:
main_tasks = []
log_activity(
current_user.username,
"Report Page",
f"Report page accessed district={selected_district}, block={selected_block}"
)
return render_template(
'task_report.html',
districts=districts,
blocks=blocks,
main_tasks=main_tasks,
selected_district=selected_district,
selected_block=selected_block
)
@main.route('/get_tasks_by_block')
@login_required
def get_tasks_by_block():
block = request.args.get('block')
if not block:
return jsonify({'tasks': []})
tasks = db.session.query(Task.task_name)\
.filter(Task.block_name == block)\
.distinct()\
.all()
task_list = [task[0].strip()
.replace(",", "")
.replace("(", "")
.replace(")", "")
.replace(".", "")
.replace("&", "")
.replace("\n", "") for task in tasks]
log_activity(current_user.username, "Fetch Tasks", f"Fetched tasks for block {block}")
return jsonify({'tasks': task_list})
def get_villages_for_block(block_name):
villages = (
db.session.query(WorkDetail.name_of_village)
.filter(WorkDetail.block == block_name)
.distinct()
.order_by(WorkDetail.name_of_village)
.all()
)
return [v[0] for v in villages if v[0]]
@main.route('/get_villages_by_block', methods=['GET'])
@login_required
def get_villages_by_block():
block = request.args.get('block')
villages = get_villages_for_block(block)
log_activity(current_user.username, "Fetch Villages", f"Fetched villages for block {block}")
return jsonify({'villages': villages})
return generate_report_page_controller()
@main.route('/filter_tasks', methods=['GET'])
@login_required
def filter_tasks():
district = request.args.get('district')
block = request.args.get('block')
village = request.args.get('village')
return filter_tasks_controller()
# ✅ Fetch distinct districts
districts = [d[0] for d in db.session.query(WorkDetail.district).distinct()]
# ✅ Fetch blocks filtered by district
if district:
blocks = [b[0] for b in db.session.query(WorkDetail.block)
.filter(WorkDetail.district == district).distinct()]
else:
blocks = []
# ✅ Fetch villages filtered by district + block
if district and block:
villages = [v[0] for v in db.session.query(WorkDetail.name_of_village)
.filter(WorkDetail.district == district,
WorkDetail.block == block)
.distinct()]
else:
villages = []
grouped_tasks = []
# ✅ Only fetch tasks if all three (district, block, village) are selected
if district and block and village:
query = (db.session.query(Task)
.join(WorkDetail, Task.village_name == WorkDetail.name_of_village)
.filter(WorkDetail.district == district,
WorkDetail.block == block,
WorkDetail.name_of_village == village))
tasks = query.order_by(Task.uploaded_at.desc()).all()
current_main_task = None
for task in tasks:
task_data = {
"id": task.id,
"task_name": task.task_name,
"unit": task.unit,
"qty": task.qty,
"rate": task.rate,
"boq_amount": task.boq_amount,
"previous_billed_qty": task.previous_billed_qty,
"previous_billing_amount": task.previous_billing_amount,
"in_this_ra_bill_qty": task.in_this_ra_bill_qty,
"in_this_ra_billing_amount": task.in_this_ra_billing_amount,
"cumulative_billed_qty": task.cumulative_billed_qty,
"cumulative_billed_amount": task.cumulative_billed_amount,
"variation_qty": task.variation_qty,
"variation_amount": task.variation_amount,
"remark": task.remark
}
# ✅ Group main tasks (with serial_number) and subtasks
if task.serial_number:
task_data["subtasks"] = []
grouped_tasks.append(task_data)
current_main_task = task_data
elif current_main_task:
current_main_task["subtasks"].append(task_data)
log_activity(current_user.username, "Filter Tasks",
f"Filtered tasks for district={district}, block={block}, village={village}")
# ✅ Render with both filtering + grouped tasks
return render_template(
'filter_tasks.html',
grouped_tasks=grouped_tasks,
districts=districts,
blocks=blocks,
villages=villages,
selected_district=district,
selected_block=block,
selected_village=village
)
# ✅ Helper function for logging user activity
def log_activity(user, action, details=""):
try:
log_file = os.path.join(current_app.root_path, "activity.log")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(log_file, "a") as f:
f.write(f"Timestamp: {timestamp} | User: {user} | Action: {action} | Details: {details}\n")
except Exception as e:
print(f"Logging failed: {e}")
from flask import request
from datetime import datetime
@main.route('/activity_log', methods=['GET', 'POST'])
@login_required
def activity_log():
logs = []
log_file = os.path.join(current_app.root_path, 'activity.log')
if os.path.exists(log_file):
with open(log_file, 'r') as f:
for line in f:
parts = line.strip().split(" | ")
if len(parts) == 4:
logs.append({
"timestamp": parts[0].replace("Timestamp:", "").strip(),
"user": parts[1].replace("User:", "").strip(),
"action": parts[2].replace("Action:", "").strip(),
"details": parts[3].replace("Details:", "").strip()
})
# Filters
start_date = request.args.get("start_date")
end_date = request.args.get("end_date")
username = request.args.get("username")
filtered_logs = logs
# Date filter (inclusive)
if start_date or end_date:
try:
if start_date:
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
else:
start_dt = datetime.min
if end_date:
end_dt = datetime.strptime(end_date, "%Y-%m-%d")
else:
end_dt = datetime.max
filtered_logs = [
log for log in filtered_logs
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt.replace(hour=23, minute=59, second=59)
]
except Exception as e:
print("Date filter error:", e)
# Username filter
if username:
filtered_logs = [log for log in filtered_logs if log["user"].lower() == username.lower()]
return render_template(
"activity_log.html",
logs=filtered_logs,
start_date=start_date,
end_date=end_date,
username=username
)
return activity_log_controller()

Binary file not shown.

17
app/service/logger.py Normal file
View File

@@ -0,0 +1,17 @@
import os
from flask import current_app
from datetime import datetime
def log_activity(user, action, details=""):
try:
log_file = os.path.join(current_app.root_path, "activity.log")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(log_file, "a") as f:
f.write(
f"Timestamp: {timestamp} | User: {user} | "
f"Action: {action} | Details: {details}\n"
)
except Exception as e:
print(f"Logging failed: {e}")

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +0,0 @@
dn: dc=lcepl,dc=org
objectClass: top
objectClass: dcObject
objectClass: organization
o: LCEPL Organization
dc: lcepl

View File

@@ -1,2 +0,0 @@
[LocalizedFileNames]
ou.ldif=@ou,0

View File

@@ -1,13 +0,0 @@
dn: uid=laxmi,ou=users,dc=lcepl,dc=org
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
cn: Laxmi Bamnale
sn: Bamnale
uid: laxmi
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/laxmi
loginShell: /bin/bash
mail: laxmi@lcepl.org
userPassword: {SSHA}Vg2xl+biEhgR9DQPmCUuPJ/e70ECgND7

View File

@@ -1,3 +0,0 @@
dn: ou=users,dc=lcepl,dc=org
objectClass: organizationalUnit
ou: users

View File

@@ -1,3 +0,0 @@
dn: ou=users,dc=lcepl,dc=org
objectClass: organizationalUnit
ou: users

View File

@@ -1,6 +0,0 @@
dn: uid=prajakta,ou=users,dc=lcepl,dc=org
objectClass: inetOrgPerson
cn: Prajakta
sn: S
uid: prajakta
userPassword: prajakta123

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More