Convert into MVC and optimize the code
This commit is contained in:
BIN
app/Controllers/__pycache__/activity_controller.cpython-314.pyc
Normal file
BIN
app/Controllers/__pycache__/activity_controller.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/Controllers/__pycache__/auth_controller.cpython-314.pyc
Normal file
BIN
app/Controllers/__pycache__/auth_controller.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/Controllers/__pycache__/dashboard_controller.cpython-314.pyc
Normal file
BIN
app/Controllers/__pycache__/dashboard_controller.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/Controllers/__pycache__/dropdown_controller.cpython-314.pyc
Normal file
BIN
app/Controllers/__pycache__/dropdown_controller.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/Controllers/__pycache__/filter_controller.cpython-314.pyc
Normal file
BIN
app/Controllers/__pycache__/filter_controller.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/Controllers/__pycache__/report_controller.cpython-314.pyc
Normal file
BIN
app/Controllers/__pycache__/report_controller.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/Controllers/__pycache__/task_controller.cpython-314.pyc
Normal file
BIN
app/Controllers/__pycache__/task_controller.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/Controllers/__pycache__/upload_controller.cpython-314.pyc
Normal file
BIN
app/Controllers/__pycache__/upload_controller.cpython-314.pyc
Normal file
Binary file not shown.
64
app/Controllers/activity_controller.py
Normal file
64
app/Controllers/activity_controller.py
Normal 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
|
||||
)
|
||||
84
app/Controllers/auth_controller.py
Normal file
84
app/Controllers/auth_controller.py
Normal 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"))
|
||||
59
app/Controllers/dashboard_controller.py
Normal file
59
app/Controllers/dashboard_controller.py
Normal 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
|
||||
)
|
||||
80
app/Controllers/dropdown_controller.py
Normal file
80
app/Controllers/dropdown_controller.py
Normal 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})
|
||||
97
app/Controllers/filter_controller.py
Normal file
97
app/Controllers/filter_controller.py
Normal 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
|
||||
)
|
||||
61
app/Controllers/report_controller.py
Normal file
61
app/Controllers/report_controller.py
Normal 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
|
||||
)
|
||||
189
app/Controllers/task_controller.py
Normal file
189
app/Controllers/task_controller.py
Normal 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
|
||||
)
|
||||
214
app/Controllers/upload_controller.py
Normal file
214
app/Controllers/upload_controller.py
Normal 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'))
|
||||
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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
|
||||
Binary file not shown.
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
app/service/__pycache__/logger.cpython-314.pyc
Normal file
BIN
app/service/__pycache__/logger.cpython-314.pyc
Normal file
Binary file not shown.
17
app/service/logger.py
Normal file
17
app/service/logger.py
Normal 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.
@@ -1,6 +0,0 @@
|
||||
dn: dc=lcepl,dc=org
|
||||
objectClass: top
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: LCEPL Organization
|
||||
dc: lcepl
|
||||
@@ -1,2 +0,0 @@
|
||||
[LocalizedFileNames]
|
||||
ou.ldif=@ou,0
|
||||
@@ -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
|
||||
@@ -1,3 +0,0 @@
|
||||
dn: ou=users,dc=lcepl,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
@@ -1,3 +0,0 @@
|
||||
dn: ou=users,dc=lcepl,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
@@ -1,6 +0,0 @@
|
||||
dn: uid=prajakta,ou=users,dc=lcepl,dc=org
|
||||
objectClass: inetOrgPerson
|
||||
cn: Prajakta
|
||||
sn: S
|
||||
uid: prajakta
|
||||
userPassword: prajakta123
|
||||
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.
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
Reference in New Issue
Block a user