Client billing code push
This commit is contained in:
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
10
.idea/excel_upload_app.iml
generated
Normal file
10
.idea/excel_upload_app.iml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.13 (excel_upload_app)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.13 (excel_upload_app)" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/excel_upload_app.iml" filepath="$PROJECT_DIR$/.idea/excel_upload_app.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt gunicorn
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
ENV FLASK_APP=run:app
|
||||
ENV FLASK_ENV=development
|
||||
|
||||
CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "--timeout", "120", "run:app"]
|
||||
BIN
__pycache__/run.cpython-310.pyc
Normal file
BIN
__pycache__/run.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/run.cpython-312.pyc
Normal file
BIN
__pycache__/run.cpython-312.pyc
Normal file
Binary file not shown.
42
alembic.ini
Normal file
42
alembic.ini
Normal file
@@ -0,0 +1,42 @@
|
||||
# alembic.ini
|
||||
[alembic]
|
||||
# Path to migration scripts
|
||||
script_location = migrations
|
||||
# Database URL (replace with your MySQL credentials)
|
||||
sqlalchemy.url = mysql+pymysql://root:tiger@mysql:3306/excel_data7
|
||||
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
propagate = 0
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
propagate = 0
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
108
app/__init__.py
Normal file
108
app/__init__.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# app/__init__.py
|
||||
|
||||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from flask_login import LoginManager, UserMixin
|
||||
from flask_ldap3_login import LDAP3LoginManager
|
||||
import os
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
||||
# ---------------------------
|
||||
# Initialize extensions
|
||||
# ---------------------------
|
||||
db = SQLAlchemy()
|
||||
migrate = Migrate()
|
||||
login_manager = LoginManager()
|
||||
ldap_manager = LDAP3LoginManager()
|
||||
|
||||
# ---------------------------
|
||||
# LDAP User class for Flask-Login
|
||||
# ---------------------------
|
||||
class LDAPUser(UserMixin):
|
||||
def __init__(self, dn, username, data):
|
||||
self.id = username # Flask-Login requires .id
|
||||
self.username = username
|
||||
self.dn = dn
|
||||
self.data = data
|
||||
|
||||
# ---------------------------
|
||||
# Create Flask app factory
|
||||
# ---------------------------
|
||||
def create_app():
|
||||
app = Flask(__name__) # simplified, no APPLICATION_ROOT
|
||||
app.secret_key = "o17d88dba8ebd13565e862c752bf017b7"
|
||||
|
||||
# ---------------------------
|
||||
# Upload folder config
|
||||
# ---------------------------
|
||||
app.config['UPLOAD_FOLDER'] = os.path.join(os.getcwd(), 'uploads')
|
||||
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
||||
|
||||
# ---------------------------
|
||||
# Database config
|
||||
# ---------------------------
|
||||
# app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:admin@mysql:3306/excel_data7"
|
||||
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:root@localhost:3306/excel_data7"
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
|
||||
# ---------------------------
|
||||
# LDAP config
|
||||
# ---------------------------
|
||||
# app.config['LDAP_HOST'] = 'openldap'
|
||||
app.config['LDAP_HOST'] = 'localhost'
|
||||
# app.config['LDAP_PORT'] = 389
|
||||
app.config['LDAP_PORT'] = 389
|
||||
app.config['LDAP_USE_SSL'] = False
|
||||
|
||||
# Base DN for your directory
|
||||
app.config['LDAP_BASE_DN'] = 'dc=lcepl,dc=org'
|
||||
|
||||
# Admin bind credentials
|
||||
app.config['LDAP_BIND_USER_DN'] = 'uid=admin,dc=lcepl,dc=org'
|
||||
app.config['LDAP_BIND_USER_PASSWORD'] = 'admin123'
|
||||
|
||||
# User search config
|
||||
app.config['LDAP_USER_SEARCH_BASE'] = 'dc=lcepl,dc=org'
|
||||
app.config['LDAP_USER_SEARCH_FILTER'] = '(uid=%s)'
|
||||
app.config['LDAP_USER_RDN_ATTR'] = 'uid'
|
||||
app.config['LDAP_USER_LOGIN_ATTR'] = 'uid'
|
||||
|
||||
# ---------------------------
|
||||
# Initialize extensions
|
||||
# ---------------------------
|
||||
db.init_app(app)
|
||||
migrate.init_app(app, db)
|
||||
login_manager.init_app(app)
|
||||
ldap_manager.init_app(app)
|
||||
|
||||
# ---------------------------
|
||||
# Login view
|
||||
# ---------------------------
|
||||
login_manager.login_view = 'main.login' # blueprint + endpoint
|
||||
login_manager.login_message_category = 'info'
|
||||
|
||||
# ---------------------------
|
||||
# Flask-Login user loader
|
||||
# ---------------------------
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return LDAPUser(dn=None, username=user_id, data={})
|
||||
|
||||
# ---------------------------
|
||||
# Register blueprints
|
||||
# ---------------------------
|
||||
try:
|
||||
from app.routes.main import main as main_bp
|
||||
from app.routes.reports import reports as reports_bp
|
||||
app.register_blueprint(main_bp)
|
||||
app.register_blueprint(reports_bp)
|
||||
except ImportError as e:
|
||||
print(f"Error importing blueprints: {e}")
|
||||
|
||||
# ---------------------------
|
||||
# Fix proxy headers for reverse proxy (Nginx)
|
||||
# ---------------------------
|
||||
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1)
|
||||
|
||||
return app
|
||||
BIN
app/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
app/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
app/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
app/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
app/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
app/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
app/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/models.cpython-310.pyc
Normal file
BIN
app/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/models.cpython-312.pyc
Normal file
BIN
app/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/models.cpython-313.pyc
Normal file
BIN
app/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/models.cpython-314.pyc
Normal file
BIN
app/__pycache__/models.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/models.cpython-39.pyc
Normal file
BIN
app/__pycache__/models.cpython-39.pyc
Normal file
Binary file not shown.
4286
app/activity.log
Normal file
4286
app/activity.log
Normal file
File diff suppressed because it is too large
Load Diff
74
app/models.py
Normal file
74
app/models.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from . import db
|
||||
from flask_login import UserMixin
|
||||
from datetime import datetime
|
||||
|
||||
String_size = db.String(255)
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(String_size, unique=True, nullable=False)
|
||||
email = db.Column(String_size, unique=True, nullable=False)
|
||||
password_hash = db.Column(String_size, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
def set_password(self, password):
|
||||
from werkzeug.security import generate_password_hash
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def check_password(self, password):
|
||||
from werkzeug.security import check_password_hash
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<User {self.username}>'
|
||||
|
||||
# ==========================
|
||||
# Your existing models below
|
||||
# ==========================
|
||||
|
||||
class Task(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
district = db.Column(String_size)
|
||||
block_name = db.Column(String_size)
|
||||
village_name = db.Column(String_size)
|
||||
serial_number = db.Column(String_size)
|
||||
parent_id = db.Column(String_size, nullable=True)
|
||||
parent_task_name = db.Column(db.Text)
|
||||
task_name = db.Column(db.Text)
|
||||
unit = db.Column(String_size)
|
||||
qty = db.Column(String_size)
|
||||
rate = db.Column(String_size)
|
||||
boq_amount = db.Column(String_size)
|
||||
previous_billed_qty = db.Column(String_size)
|
||||
previous_billing_amount = db.Column(String_size)
|
||||
remaining_amount = db.Column(String_size)
|
||||
in_this_ra_bill_qty = db.Column(String_size)
|
||||
in_this_ra_billing_amount = db.Column(String_size)
|
||||
cumulative_billed_qty = db.Column(String_size)
|
||||
cumulative_billed_amount = db.Column(String_size)
|
||||
variation_qty = db.Column(String_size)
|
||||
variation_amount = db.Column(String_size)
|
||||
remark = db.Column(String_size, nullable=True)
|
||||
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
class WorkDetail(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name_of_work = db.Column(String_size)
|
||||
cover_agreement_no = db.Column(String_size)
|
||||
name_of_contractor = db.Column(String_size)
|
||||
name_of_tpi_agency = db.Column(String_size)
|
||||
name_of_division = db.Column(String_size)
|
||||
name_of_village = db.Column(String_size)
|
||||
block = db.Column(String_size)
|
||||
scheme_id = db.Column(String_size)
|
||||
measurement_book = db.Column(String_size)
|
||||
date_of_billing = db.Column(String_size)
|
||||
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
district = db.Column(db.String(255))
|
||||
|
||||
class ActivityLog(db.Model): # ✅ OUTSIDE WorkDetail
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user = db.Column(db.String(100)) # can link with User model later
|
||||
action = db.Column(db.String(255))
|
||||
details = db.Column(db.Text)
|
||||
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
68
app/routes/__init__.py
Normal file
68
app/routes/__init__.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# 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
|
||||
BIN
app/routes/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
app/routes/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
app/routes/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
app/routes/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
app/routes/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
app/routes/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
app/routes/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/ldap_user.cpython-313.pyc
Normal file
BIN
app/routes/__pycache__/ldap_user.cpython-313.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/main.cpython-310.pyc
Normal file
BIN
app/routes/__pycache__/main.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/main.cpython-311.pyc
Normal file
BIN
app/routes/__pycache__/main.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/main.cpython-312.pyc
Normal file
BIN
app/routes/__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/main.cpython-313.pyc
Normal file
BIN
app/routes/__pycache__/main.cpython-313.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/main.cpython-314.pyc
Normal file
BIN
app/routes/__pycache__/main.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/main.cpython-39.pyc
Normal file
BIN
app/routes/__pycache__/main.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/reports.cpython-310.pyc
Normal file
BIN
app/routes/__pycache__/reports.cpython-310.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/reports.cpython-311.pyc
Normal file
BIN
app/routes/__pycache__/reports.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/reports.cpython-312.pyc
Normal file
BIN
app/routes/__pycache__/reports.cpython-312.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/reports.cpython-313.pyc
Normal file
BIN
app/routes/__pycache__/reports.cpython-313.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/reports.cpython-314.pyc
Normal file
BIN
app/routes/__pycache__/reports.cpython-314.pyc
Normal file
Binary file not shown.
BIN
app/routes/__pycache__/reports.cpython-39.pyc
Normal file
BIN
app/routes/__pycache__/reports.cpython-39.pyc
Normal file
Binary file not shown.
10
app/routes/ldap_user.py
Normal file
10
app/routes/ldap_user.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# 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)
|
||||
837
app/routes/main.py
Normal file
837
app/routes/main.py
Normal file
@@ -0,0 +1,837 @@
|
||||
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 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
|
||||
|
||||
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"))
|
||||
|
||||
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')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
# flash("You have been logged out.", "info")
|
||||
return redirect(url_for("main.login"))
|
||||
|
||||
|
||||
# Home route
|
||||
@main.route('/upload_excel')
|
||||
@login_required
|
||||
def upload_excel():
|
||||
log_activity(current_user.username, "Page Load", "Upload Excel page accessed")
|
||||
return render_template('upload.html')
|
||||
|
||||
|
||||
# @main.route('/dashboard')
|
||||
# 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"
|
||||
|
||||
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
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
@main.route('/')
|
||||
|
||||
@login_required
|
||||
def dashboard():
|
||||
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]
|
||||
|
||||
# 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')
|
||||
|
||||
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]})
|
||||
|
||||
@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})
|
||||
|
||||
@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')
|
||||
|
||||
# ✅ 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
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
210
app/routes/reports.py
Normal file
210
app/routes/reports.py
Normal file
@@ -0,0 +1,210 @@
|
||||
from flask import Blueprint, request, render_template, send_from_directory, redirect, url_for, current_app, flash
|
||||
from app.__init__ import db
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from app.models import Task
|
||||
|
||||
reports = Blueprint('reports', __name__)
|
||||
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
def clean_text(text):
|
||||
if not isinstance(text, str):
|
||||
return ""
|
||||
return text.strip().replace(",", "").replace("(", "").replace(")", "")\
|
||||
.replace(".", "").replace("&", "").replace("\n", "").lower()
|
||||
|
||||
@reports.route('/report_excel', methods=['GET'])
|
||||
def generate_report():
|
||||
main_task_rexp = r'[\\/*?:"<>|]'
|
||||
block = request.args.get('block', '')
|
||||
main_task = request.args.get('main_task', '')
|
||||
|
||||
block_clean = clean_text(block)
|
||||
main_task_clean = clean_text(main_task)
|
||||
|
||||
if not block_clean:
|
||||
return "Please select a Block.", 400
|
||||
if not main_task_clean:
|
||||
return "Please select a Main Task.", 400
|
||||
|
||||
print(f"Block selected: {block}")
|
||||
print(f"Main task selected: {main_task}")
|
||||
|
||||
# Filter main task records based on cleaned block and task name
|
||||
main_task_records = [task for task in Task.query.filter_by(block_name=block).all()
|
||||
if clean_text(task.task_name) == main_task_clean]
|
||||
|
||||
print(f"Found {len(main_task_records)} main task records")
|
||||
|
||||
# if not main_task_records:
|
||||
# return f"Main Task '{main_task}' not found in the selected block '{block}'.", 404
|
||||
if not main_task_records:
|
||||
flash("No data found for selected Block and Main Task", "error")
|
||||
# return redirect(url_for('generate_report_page'))
|
||||
return redirect(url_for('main.generate_report_page'))
|
||||
|
||||
report_data = {}
|
||||
|
||||
def safe_float(value):
|
||||
try:
|
||||
return round(float(value), 2)
|
||||
except (ValueError, TypeError):
|
||||
return 0.0
|
||||
|
||||
# Process subtasks for each main task
|
||||
for main_task_record in main_task_records:
|
||||
main_task_serial_number = main_task_record.serial_number
|
||||
|
||||
|
||||
# Get all subtasks under the selected main task (match cleaned names)
|
||||
subtasks_query = [
|
||||
task for task in Task.query.filter(Task.block_name == block).all()
|
||||
if clean_text(task.parent_task_name) == main_task_clean
|
||||
]
|
||||
|
||||
print(f"Found {len(subtasks_query)} subtasks for main task '{main_task}'")
|
||||
|
||||
for task in subtasks_query:
|
||||
key = task.village_name.strip() if task.village_name else ""
|
||||
totalElemList = 26
|
||||
if key not in report_data:
|
||||
report_data[key] = [None] * totalElemList
|
||||
report_data[key][0] = task.id
|
||||
report_data[key][1] = task.village_name
|
||||
|
||||
boq_amount = safe_float(task.boq_amount)
|
||||
previous_billing_amount = safe_float(task.previous_billing_amount)
|
||||
remaining_amount = safe_float(boq_amount - previous_billing_amount)
|
||||
tender_amount = safe_float(task.qty) * safe_float(task.rate)
|
||||
values = [
|
||||
safe_float(task.qty),
|
||||
safe_float(task.rate),
|
||||
tender_amount,
|
||||
safe_float(task.previous_billed_qty),
|
||||
previous_billing_amount,
|
||||
remaining_amount
|
||||
]
|
||||
|
||||
# Determine task type section
|
||||
task_name_clean = task.task_name.lower() if task.task_name else ""
|
||||
start, end = (
|
||||
(2, 8) if "supply" in task_name_clean else
|
||||
(8, 14) if "erection" in task_name_clean else
|
||||
(14, 20) if "testing" in task_name_clean else
|
||||
(20, 26) if "commissioning" in task_name_clean else
|
||||
(None, None)
|
||||
)
|
||||
|
||||
if start is not None and end is not None:
|
||||
for i in range(start, end):
|
||||
current_value = safe_float(report_data[key][i]) if report_data[key][i] is not None else 0
|
||||
report_data[key][i] = current_value + values[i - start]
|
||||
|
||||
print(f"Number of villages in report data: {len(report_data)}")
|
||||
|
||||
# if not report_data:
|
||||
# return f"No matching data found for the selected block '{block}' and main task '{main_task}'.", 404
|
||||
if not report_data:
|
||||
flash("Sub task data not found", "error")
|
||||
# return redirect(url_for('generate_report_page'))
|
||||
return redirect(url_for('main.generate_report_page'))
|
||||
|
||||
# Generate Excel report
|
||||
sanitized_main_task = re.sub(main_task_rexp, "", main_task)
|
||||
|
||||
max_length = 30
|
||||
|
||||
if len(sanitized_main_task) > max_length:
|
||||
sanitized_main_task = sanitized_main_task[:max_length]
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
# file_name = f"{sanitized_main_task}_Report.xlsx"
|
||||
file_name = f"{sanitized_main_task}_{timestamp}.xlsx"
|
||||
file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], file_name)
|
||||
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Subtask Report"
|
||||
|
||||
# Excel formatting
|
||||
thin_border = Border(left=Side(style="thin"), right=Side(style="thin"),
|
||||
top=Side(style="thin"), bottom=Side(style="thin"))
|
||||
header_fill = PatternFill(start_color="FFC000", end_color="FFC000", fill_type="solid")
|
||||
subheader_fill = PatternFill(start_color="92D050", end_color="92D050", fill_type="solid")
|
||||
data_row_fill1 = PatternFill(start_color="FFFFFF", end_color="FFFFFF", fill_type="solid")
|
||||
data_row_fill2 = PatternFill(start_color="D9EAD3", end_color="D9EAD3", fill_type="solid")
|
||||
total_row_fill = PatternFill(start_color="FFF2CC", end_color="FFF2CC", fill_type="solid")
|
||||
|
||||
ws.merge_cells(start_row=1, start_column=3, end_row=1, end_column=8)
|
||||
ws["A1"] = "Task ID"
|
||||
ws["B1"] = "Village Name"
|
||||
ws["C1"] = "Supply (70%)"
|
||||
ws.merge_cells(start_row=1, start_column=9, end_row=1, end_column=14)
|
||||
ws["I1"] = "Erection (20%)"
|
||||
ws.merge_cells(start_row=1, start_column=15, end_row=1, end_column=20)
|
||||
ws["O1"] = "Testing (5%)"
|
||||
ws.merge_cells(start_row=1, start_column=21, end_row=1, end_column=26)
|
||||
ws["U1"] = "Commissioning (5%)"
|
||||
|
||||
for start_col in [3, 9, 15, 21]:
|
||||
ws.cell(row=2, column=start_col).value = "Tender Qty"
|
||||
ws.cell(row=2, column=start_col + 1).value = "Tender Rate"
|
||||
ws.cell(row=2, column=start_col + 2).value = "Tender Amount"
|
||||
ws.cell(row=2, column=start_col + 3).value = "Previous Bill QTY"
|
||||
ws.cell(row=2, column=start_col + 4).value = "Previous Bill Amount"
|
||||
ws.cell(row=2, column=start_col + 5).value = "Remaining Amount"
|
||||
|
||||
# Style header and subheaders
|
||||
for col in range(1, 27):
|
||||
col_letter = ws.cell(row=2, column=col).column_letter
|
||||
ws[f"{col_letter}1"].font = Font(bold=True, size=12)
|
||||
ws[f"{col_letter}1"].alignment = Alignment(horizontal="center", vertical="center")
|
||||
ws[f"{col_letter}1"].fill = header_fill
|
||||
ws[f"{col_letter}1"].border = thin_border
|
||||
|
||||
ws[f"{col_letter}2"].font = Font(bold=True, size=11)
|
||||
ws[f"{col_letter}2"].alignment = Alignment(horizontal="center", vertical="center")
|
||||
ws[f"{col_letter}2"].fill = subheader_fill
|
||||
ws[f"{col_letter}2"].border = thin_border
|
||||
|
||||
# Fill data
|
||||
row_index = 3
|
||||
totals = ["Total", ""] + [0] * 24
|
||||
sum_columns = [4, 6, 7, 10, 12, 13, 16, 18, 19, 22, 24, 25]
|
||||
|
||||
for row_data in report_data.values():
|
||||
ws.append(row_data)
|
||||
fill = data_row_fill1 if row_index % 2 != 0 else data_row_fill2
|
||||
for col in range(1, 27):
|
||||
ws.cell(row=row_index, column=col).fill = fill
|
||||
ws.cell(row=row_index, column=col).border = thin_border
|
||||
for i in sum_columns:
|
||||
totals[i] += safe_float(row_data[i])
|
||||
row_index += 1
|
||||
|
||||
# Add totals
|
||||
ws.append(totals)
|
||||
for col in range(1, 27):
|
||||
ws.cell(row=row_index, column=col).font = Font(bold=True)
|
||||
ws.cell(row=row_index, column=col).fill = total_row_fill
|
||||
ws.cell(row=row_index, column=col).alignment = Alignment(horizontal="center")
|
||||
ws.cell(row=row_index, column=col).border = thin_border
|
||||
|
||||
for i in range(1, 27):
|
||||
ws.column_dimensions[ws.cell(row=2, column=i).column_letter].width = 20
|
||||
|
||||
wb.save(file_path)
|
||||
print(f"Report generated: {file_name}")
|
||||
return redirect(url_for('reports.download_report', filename=file_name))
|
||||
|
||||
|
||||
@reports.route('/download/<filename>')
|
||||
def download_report(filename):
|
||||
return send_from_directory(current_app.config['UPLOAD_FOLDER'], filename, as_attachment=True)
|
||||
110
app/templates/activity_log.html
Normal file
110
app/templates/activity_log.html
Normal file
@@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Activity Logs</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Segoe UI", Tahoma, sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
margin: 20px;
|
||||
}
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.top-buttons {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
input, button {
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
table {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
box-shadow: 0 0 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
th, td {
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
}
|
||||
th {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Activity Logs</h2>
|
||||
|
||||
<div class="top-buttons">
|
||||
<a href="{{ url_for('main.dashboard') }}">
|
||||
<button type="button">⬅ Back to Dashboard</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="get" action="{{ url_for('main.activity_log') }}" class="filter-form">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" placeholder="Enter username" value="{{ username or '' }}">
|
||||
|
||||
<label for="start_date">Start Date:</label>
|
||||
<input type="date" id="start_date" name="start_date" value="{{ start_date or '' }}">
|
||||
|
||||
<label for="end_date">End Date:</label>
|
||||
<input type="date" id="end_date" name="end_date" value="{{ end_date or '' }}">
|
||||
|
||||
<button type="submit" class="btn btn-primary">Filter</button>
|
||||
</form>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<th>User</th>
|
||||
<th>Action</th>
|
||||
<th>Details</th>
|
||||
</tr>
|
||||
{% for log in logs %}
|
||||
<tr>
|
||||
<td>{{ log.timestamp }}</td>
|
||||
<td>{{ log.user }}</td>
|
||||
<td>{{ log.action }}</td>
|
||||
<td>{{ log.details }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if logs|length == 0 %}
|
||||
<tr>
|
||||
<td colspan="4">No logs found</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function resetFilter() {
|
||||
window.location.href = "{{ url_for('main.activity_log') }}";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
439
app/templates/filter_tasks.html
Normal file
439
app/templates/filter_tasks.html
Normal file
@@ -0,0 +1,439 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Filtered Task Display</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f7f9fc;
|
||||
margin: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
select,
|
||||
button {
|
||||
padding: 5px 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
button[type="submit"] {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
button[type="submit"]:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.button-container1 {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
.button-container1 button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button-container1 button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.edit-mode-button {
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.edit-mode-button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
#selectedFilters {
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 30px;
|
||||
margin-left: 275px;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.main-task-row {
|
||||
background-color: #d9eaf7;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtask-row {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.edit-field {
|
||||
display: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.no-tasks-message {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
select {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
/* Sidebar Styling */
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
background-color: #f4f4f4;
|
||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.sidebar .logo {
|
||||
width: 60px;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
display: block;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
padding: 10px 15px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar a:hover {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
h1.report_title {
|
||||
text-align: center;
|
||||
margin-top: 100px;
|
||||
color: #333;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-y: auto;
|
||||
}
|
||||
th {
|
||||
position: sticky; /* Ensures the header stays on top */
|
||||
top: 58px; /* Keeps the header fixed at the top */
|
||||
z-index: 1; /* Ensures it's above the content */
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Sidebar -->
|
||||
<div class="sidebar">
|
||||
<img
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA+CAYAAAB3NHh5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4JpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTMyIDc5LjE1OTI4NCwgMjAxNi8wNC8xOS0xMzoxMzo0MCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NkEzOEVGMzEwQTQxMUU4QUFBQkY0QTA2QzlEM0MxNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2QUQzQTIzRDE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2QUQzQTIzQzE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNS41IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhNmE4ZTBmLTI5ZWItNDBlMy05ZWFhLTYzNTdiYjdkMzcwNCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmRhMjgyMWMwLTYwYmQtMTE3Yi04ZGU3LWNjZmQ1MDgzNjUxNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrVd/GcAAASESURBVHja7FtrSBRRFL7rrg9MMjUzspTSSqUg6GFBmohh9SeIMOhXhBX0KzL80YN+SBD+qJ+RBhFEQogV0RMLKyuiqIig6GlZRmlRavl2O8c9I7dtZ2adM3d9zB742GHuzpn7zZl7zrnn3nF5vV7hJHEh4aLKaxfgeAXAjee0NoDX7ziCjl2yDsANwHbAsQD3mALYA3ii04e9gEZAP6BM5z9nAA2AKos8FwK+1pcX53noxCJAMuPBzQLEATbotEfpnMeHfIiuSzK4vhnw2KA9GJkryGIoz5hvyjlAr0F7e4BzuYBbdPwG8Mvg+m+APmYfh/qgWbiXqWzQpN3fUSyj19hj8gbYKV7Zwsp9hXS8BHBXIhsqiQglYTf9LgbcGQWywxKqG7cAssiy0aMVkWQLpzGVoYfvMWjvBlQwyKKPiWT2MU628DZACkPZa0CGQXs00zHOBnwBrLF4fTbgnb+X7mJ0qN/EH3iZYUVLfKz2sQMwSSZ8kkKFVTkKOKxw/KF1Zkhx26rUaFb5xFTUpjiWRtmQeHSGMiyNGQkTdgrhVKaeJBvycbM4zE2S/vHSpTRv7beo7CEgXiFh7GwTTSetCE5/f8iEMdOaajJFM5KtgHyFhDcC/gBeMfKEeJnwAcDyMTz0FgAqmTowG6zWxnCrA/zVc9lp9TmAcEgLAOE4HCY8zgoAZuI28RPdiuO45umHw9LuIKoWHPlOiY1RNeIETfI5gkWM6YABv/MZFMeHCWONOAFwWRFhrKjkEPFAshMwB/CReZ9LZn/QCM8ErBe+ZQ9VUid8JdpAclH4llO40kmFjBdmTmsXYL4NFQWzIkGXDt7adA8cGlgZzQrGS+OMpABwW2HVwkrbSAWH5gNBa0nBhCUk3TjOQ9BkIp0phyQ9wl6a+dwb56QTiEOmkNa2IgzyzpWA+zZ2oMNiG0eSydLp/l5aj/Qq4VsLyrXh5jgJz9NpK1Bo6UQqHuCCfbVZ2aSP5smbBW+pYx5gh4FDRM+6D/BZEWksQf2ULVxC5g80L8YqyFPx/xrvSOQUjaUSnfYyevWyFVr6t0wY92AsVXizJJNximnfFuFbAVElGHnyNKfVothjDpr4iyjNAgqlVfbSA+GKR7gAMLEIexzA1SMTjnUA4VjZspsAMQxluGqxVqhbFMetjQcB7xk6umXChUZzyCCkFnBcIeGblI3tZ+h4if3UCJczE49EoXbLA87VcWNLBUMHLvjVamO42YZqxljf8tAcjsNOIuxyGuEMGyoLPQr72SP4Ww/T5LBURZ226rweyWUUC+I2mW9jZ3GTeCnjHk0y4fOCNl8yvHQO4/oBYbzJPIaszKmmdsiEa4R+vSkYwe0IRyxee0X4PkEoNPgPfiKQSsmDVcHaXL5HshBH2i2OsauAdXRstNgWacOcvU12WtyPl6xcf53yb01Ub7sIeQHA6zcZKJ7IiYdMth5QNNpxOJ2pZ5rJGMZcGOvbuEq4WifZSWHoD0bSZS99FvCB6QHRcdXptONOuNP0KuuN+wYiZkV/MPJg6Ck77WPLvwIMAHzX4zyhUFlrAAAAAElFTkSuQmCC"
|
||||
alt="LCEPL Logo"
|
||||
/>
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/upload_excel">Upload Excel</a>
|
||||
<a href="/generate_report_page">Print Report</a>
|
||||
<a href="/filter_tasks">Filter Tasks</a>
|
||||
</div>
|
||||
|
||||
<div class="header">LAXMI CIVIL ENGINEERING SERVICES PVT. LTD.</div>
|
||||
<h1 class="report_title">Filtered Tasks</h1>
|
||||
|
||||
<form method="GET" action="{{ url_for('main.filter_tasks') }}">
|
||||
<!-- District Dropdown -->
|
||||
<label for="district">District:</label>
|
||||
<select name="district" id="district" onchange="this.form.submit()">
|
||||
<option value="">-- Select District --</option>
|
||||
{% for d in districts %}
|
||||
<option value="{{ d }}" {% if d == selected_district %}selected{% endif %}>{{ d }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<!-- Block Dropdown -->
|
||||
<label for="block">Block:</label>
|
||||
<select name="block" id="block" onchange="this.form.submit()">
|
||||
<option value="">-- Select Block --</option>
|
||||
{% for b in blocks %}
|
||||
<option value="{{ b }}" {% if b == selected_block %}selected{% endif %}>{{ b }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<!-- Village Dropdown -->
|
||||
<label for="village">Village:</label>
|
||||
<select name="village" id="village" onchange="this.form.submit()">
|
||||
<option value="">-- Select Village --</option>
|
||||
{% for v in villages %}
|
||||
<option value="{{ v }}" {% if v == selected_village %}selected{% endif %}>{{ v }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<div id="selectedFilters">
|
||||
{% if selected_district or selected_block or selected_village %}
|
||||
<p>
|
||||
{% if selected_district %}<strong>District:</strong> {{ selected_district }}{% endif %}
|
||||
{% if selected_block %} <strong> | Block:</strong> {{ selected_block }}{% endif %}
|
||||
{% if selected_village %} <strong> | Village:</strong> {{ selected_village }}{% endif %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p><strong>No filters applied. Showing all tasks.</strong></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="button-container1">
|
||||
<button onclick="window.location.href='/'">Home Page</button>
|
||||
</div>
|
||||
|
||||
<div class="button-container">
|
||||
<button class="edit-mode-button" type="button" onclick="toggleEditMode()">Edit Tasks</button>
|
||||
<button class="edit-mode-button" type="button" id="cancelBtn" onclick="cancelEditMode()" style="display: none;">Cancel</button>
|
||||
<button class="edit-mode-button" type="button" onclick="submitChangedFields()">Submit Updates</button>
|
||||
</div>
|
||||
|
||||
{% if grouped_tasks %}
|
||||
<table class="table-container">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Task Name</th>
|
||||
<th>Unit</th>
|
||||
<th>Qty</th>
|
||||
<th>Rate</th>
|
||||
<th>BOQ Amt</th>
|
||||
<th>Prev Billed Qty</th>
|
||||
<th>Prev Bill Amt</th>
|
||||
<th>RA Bill Qty</th>
|
||||
<th>RA Bill Amt</th>
|
||||
<th>Cum Billed Qty</th>
|
||||
<th>Cum Billed Amt</th>
|
||||
<th>Var Qty</th>
|
||||
<th>Var Amt</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% set formula_fields = [
|
||||
"previous_billing_amount",
|
||||
"in_this_ra_billing_amount",
|
||||
"cumulative_billed_qty",
|
||||
"cumulative_billed_amount",
|
||||
"variation_qty",
|
||||
"variation_amount"
|
||||
] %}
|
||||
|
||||
{% for group in grouped_tasks %}
|
||||
<tr class="main-task-row">
|
||||
<td colspan="1">{{ group.task_name }}</td>
|
||||
</tr>
|
||||
|
||||
{% for sub in group.subtasks %}
|
||||
<tr class="subtask-row">
|
||||
{% for field in ["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"] %}
|
||||
<td>
|
||||
<span class="static-text">{{ sub[field] }}</span>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
class="edit-field"
|
||||
name="{{ field }}_{{ sub.id }}"
|
||||
value="{{ sub[field] }}"
|
||||
data-original-value="{{ sub[field] | trim }}"
|
||||
{% if field in formula_fields %} readonly style="background:#f5f5f5;" {% endif %}
|
||||
>
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="no-tasks-message">No tasks found for the selected filters.</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- ✅ Scripts -->
|
||||
<script>
|
||||
// Toggle edit mode
|
||||
function toggleEditMode() {
|
||||
document.querySelectorAll('.static-text').forEach(span => span.style.display = 'none');
|
||||
document.querySelectorAll('.edit-field').forEach(input => input.style.display = 'inline-block');
|
||||
document.getElementById('cancelBtn').style.display = 'inline-block';
|
||||
}
|
||||
|
||||
// Cancel edit mode
|
||||
function cancelEditMode() {
|
||||
document.querySelectorAll('.edit-field').forEach(input => {
|
||||
input.style.display = 'none';
|
||||
input.value = input.dataset.originalValue;
|
||||
});
|
||||
document.querySelectorAll('.static-text').forEach(span => span.style.display = 'inline-block');
|
||||
document.getElementById('cancelBtn').style.display = 'none';
|
||||
}
|
||||
|
||||
// Submit changed fields only
|
||||
function submitChangedFields() {
|
||||
const updates = {};
|
||||
document.querySelectorAll('.edit-field').forEach(input => {
|
||||
const original = input.dataset.originalValue;
|
||||
const current = input.value;
|
||||
if (current !== original) {
|
||||
updates[input.name] = current;
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(updates).length === 0) {
|
||||
alert('No changes detected.');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/update_tasks', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(updates)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
alert(data.message || 'Tasks updated successfully.');
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating tasks:', error);
|
||||
alert('Failed to update tasks.');
|
||||
});
|
||||
}
|
||||
|
||||
// ✅ Filtering dependent dropdowns
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const districtDropdown = document.getElementById("district");
|
||||
const blockDropdown = document.getElementById("block");
|
||||
const villageDropdown = document.getElementById("village");
|
||||
|
||||
districtDropdown.addEventListener("change", function () {
|
||||
let district = this.value;
|
||||
blockDropdown.innerHTML = '<option value="">Select Block</option>';
|
||||
villageDropdown.innerHTML = '<option value="">Select Village</option>';
|
||||
|
||||
if (district) {
|
||||
fetch(`/get_blocks?district=${district}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
data.forEach(block => {
|
||||
let option = document.createElement("option");
|
||||
option.value = block;
|
||||
option.textContent = block;
|
||||
blockDropdown.appendChild(option);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
blockDropdown.addEventListener("change", function () {
|
||||
let block = this.value;
|
||||
villageDropdown.innerHTML = '<option value="">Select Village</option>';
|
||||
|
||||
if (block) {
|
||||
fetch(`/get_villages?block=${block}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
data.forEach(village => {
|
||||
let option = document.createElement("option");
|
||||
option.value = village;
|
||||
option.textContent = village;
|
||||
villageDropdown.appendChild(option);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
516
app/templates/index.html
Normal file
516
app/templates/index.html
Normal file
@@ -0,0 +1,516 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
|
||||
<title>LAXMI CIVIL ENGINEERING SERVICES PVT. LTD.</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
/* display: flex; */
|
||||
flex-direction: row;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
background-color: #f4f4f4;
|
||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 1001;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar .logo {
|
||||
width: 150px;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
display: block;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
padding: 10px 15px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar a:hover {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 100px 20px 20px 330px;
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
width: calc(100% - 270px);
|
||||
max-width: 1500px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin: 20px auto;
|
||||
width: 100%;
|
||||
max-height: calc(18 * 40px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
position: sticky; /* Ensures the header stays on top */
|
||||
top: 0; /* Keeps the header fixed at the top */
|
||||
z-index: 1; /* Ensures it's above the content */
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.total-row {
|
||||
position: -webkit-sticky; /* For Safari */
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background-color: #007bff !important;
|
||||
font-weight: bold;
|
||||
border-top: 2px solid #ddd;
|
||||
z-index: 10;
|
||||
color: #ddd;
|
||||
display: table-row; /* Ensures row alignment is maintained */
|
||||
}
|
||||
|
||||
.amount {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.summary-title {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
/* Style for the filter and search inputs */
|
||||
.filter-container,
|
||||
.search-container {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-container label,
|
||||
.search-container label {
|
||||
font-size: 16px;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#blockSelect,
|
||||
#search-input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box; /* Ensures padding is included in width */
|
||||
}
|
||||
|
||||
#blockSelect {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
#search-input {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
#blockSelect:focus,
|
||||
#search-input:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
/* Adjust layout for mobile responsiveness */
|
||||
.filter-container,
|
||||
.search-container {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#blockSelect,
|
||||
#search-input {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
/* Smaller dropdown container */
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 180px; /* reduced width */
|
||||
font-size: 14px; /* smaller font */
|
||||
}
|
||||
|
||||
/* Smaller dropdown button */
|
||||
.dropdown-btn {
|
||||
background-color: #ffffff;
|
||||
color: black;
|
||||
padding: 6px 8px; /* smaller padding */
|
||||
border: 1px solid #ccc;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
font-size: 14px; /* smaller font */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Smaller dropdown content */
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #ffffff;
|
||||
min-width: 180px; /* match button width */
|
||||
border: 1px solid #ccc;
|
||||
z-index: 1;
|
||||
max-height: 150px; /* slightly smaller max height */
|
||||
overflow-y: auto;
|
||||
font-size: 13px; /* smaller font */
|
||||
}
|
||||
|
||||
/* Smaller checkbox labels */
|
||||
.dropdown-content label {
|
||||
display: block;
|
||||
padding: 4px 8px; /* reduced padding */
|
||||
cursor: pointer;
|
||||
font-size: 13px; /* reduced font size */
|
||||
}
|
||||
|
||||
/* Hover effect */
|
||||
.dropdown-content label:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Show the dropdown content when clicked */
|
||||
.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
display: inline-block;
|
||||
margin-left: 20px;
|
||||
color:#ddd;
|
||||
background-color: #007bff;
|
||||
padding: 5px 8px 5px 8px;
|
||||
border-radius: 7px;
|
||||
border: 1px solid #007bff;
|
||||
height: 30px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="sidebar">
|
||||
<img
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA+CAYAAAB3NHh5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4JpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTMyIDc5LjE1OTI4NCwgMjAxNi8wNC8xOS0xMzoxMzo0MCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NkEzOEVGMzEwQTQxMUU4QUFBQkY0QTA2QzlEM0MxNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2QUQzQTIzRDE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2QUQzQTIzQzE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNS41IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhNmE4ZTBmLTI5ZWItNDBlMy05ZWFhLTYzNTdiYjdkMzcwNCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmRhMjgyMWMwLTYwYmQtMTE3Yi04ZGU3LWNjZmQ1MDgzNjUxNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrVd/GcAAASESURBVHja7FtrSBRRFL7rrg9MMjUzspTSSqUg6GFBmohh9SeIMOhXhBX0KzL80YN+SBD+qJ+RBhFEQogV0RMLKyuiqIig6GlZRmlRavl2O8c9I7dtZ2adM3d9zB742GHuzpn7zZl7zrnn3nF5vV7hJHEh4aLKaxfgeAXAjee0NoDX7ziCjl2yDsANwHbAsQD3mALYA3ii04e9gEZAP6BM5z9nAA2AKos8FwK+1pcX53noxCJAMuPBzQLEATbotEfpnMeHfIiuSzK4vhnw2KA9GJkryGIoz5hvyjlAr0F7e4BzuYBbdPwG8Mvg+m+APmYfh/qgWbiXqWzQpN3fUSyj19hj8gbYKV7Zwsp9hXS8BHBXIhsqiQglYTf9LgbcGQWywxKqG7cAssiy0aMVkWQLpzGVoYfvMWjvBlQwyKKPiWT2MU628DZACkPZa0CGQXs00zHOBnwBrLF4fTbgnb+X7mJ0qN/EH3iZYUVLfKz2sQMwSSZ8kkKFVTkKOKxw/KF1Zkhx26rUaFb5xFTUpjiWRtmQeHSGMiyNGQkTdgrhVKaeJBvycbM4zE2S/vHSpTRv7beo7CEgXiFh7GwTTSetCE5/f8iEMdOaajJFM5KtgHyFhDcC/gBeMfKEeJnwAcDyMTz0FgAqmTowG6zWxnCrA/zVc9lp9TmAcEgLAOE4HCY8zgoAZuI28RPdiuO45umHw9LuIKoWHPlOiY1RNeIETfI5gkWM6YABv/MZFMeHCWONOAFwWRFhrKjkEPFAshMwB/CReZ9LZn/QCM8ErBe+ZQ9VUid8JdpAclH4llO40kmFjBdmTmsXYL4NFQWzIkGXDt7adA8cGlgZzQrGS+OMpABwW2HVwkrbSAWH5gNBa0nBhCUk3TjOQ9BkIp0phyQ9wl6a+dwb56QTiEOmkNa2IgzyzpWA+zZ2oMNiG0eSydLp/l5aj/Qq4VsLyrXh5jgJz9NpK1Bo6UQqHuCCfbVZ2aSP5smbBW+pYx5gh4FDRM+6D/BZEWksQf2ULVxC5g80L8YqyFPx/xrvSOQUjaUSnfYyevWyFVr6t0wY92AsVXizJJNximnfFuFbAVElGHnyNKfVothjDpr4iyjNAgqlVfbSA+GKR7gAMLEIexzA1SMTjnUA4VjZspsAMQxluGqxVqhbFMetjQcB7xk6umXChUZzyCCkFnBcIeGblI3tZ+h4if3UCJczE49EoXbLA87VcWNLBUMHLvjVamO42YZqxljf8tAcjsNOIuxyGuEMGyoLPQr72SP4Ww/T5LBURZ226rweyWUUC+I2mW9jZ3GTeCnjHk0y4fOCNl8yvHQO4/oBYbzJPIaszKmmdsiEa4R+vSkYwe0IRyxee0X4PkEoNPgPfiKQSsmDVcHaXL5HshBH2i2OsauAdXRstNgWacOcvU12WtyPl6xcf53yb01Ub7sIeQHA6zcZKJ7IiYdMth5QNNpxOJ2pZ5rJGMZcGOvbuEq4WifZSWHoD0bSZS99FvCB6QHRcdXptONOuNP0KuuN+wYiZkV/MPJg6Ck77WPLvwIMAHzX4zyhUFlrAAAAAElFTkSuQmCC"
|
||||
alt="LCEPL Logo"
|
||||
/>
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/upload_excel">Upload Excel</a>
|
||||
<a href="/generate_report_page">Print Report</a>
|
||||
<!--<a href="/tasks">Show Tasks</a>-->
|
||||
<a href="/filter_tasks">Filter Tasks</a>
|
||||
<a href="/activity_log">Activity Logs</a>
|
||||
<a href="{{ url_for('main.logout') }}" style="margin-top:auto; color: #fff; background-color: #dc3545;">Logout</a>
|
||||
|
||||
</div>
|
||||
<div class="header">LAXMI CIVIL ENGINEERING SERVICES PVT. LTD.</div>
|
||||
<div class="container">
|
||||
<h1 class="summary-title">Dashboard</h1>
|
||||
<!-- Block Selection Dropdown -->
|
||||
<form method="get" action="{{ url_for('main.dashboard') }}">
|
||||
<div class="filter-container">
|
||||
<label>Select Blocks:</label>
|
||||
<div class="dropdown-container">
|
||||
<div class="dropdown">
|
||||
<div class="dropdown-btn" onclick="toggleDropdown()">Select Blocks</div>
|
||||
<div id="dropdownContent" class="dropdown-content">
|
||||
{% for block in blocks %}
|
||||
<label>
|
||||
<input class="input-style" type="checkbox" name="block[]" value="{{ block }}"
|
||||
{% if block in selected_blocks %}checked{% endif %}>
|
||||
{{ block }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="submit-btn">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<div class="search-container">
|
||||
<br><label for="search-input">Search Village:</label>
|
||||
<input class="input-style" type="text" id="search-input" placeholder="Enter village name..." onkeyup="filterVillages()">
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sr. No.</th>
|
||||
<th>Block</th>
|
||||
<th>Village Name</th>
|
||||
<th>Total BOQ Amount</th>
|
||||
<th>Claimed Amount (Rs.)</th>
|
||||
<th>To be Claimed Amount (Rs.)</th>
|
||||
<th>Cumulative Amount (Rs.)</th>
|
||||
<th>Total Variation Amount</th>
|
||||
<th>Balance against BOQ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="village-table-body">
|
||||
{% for village in villages %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ village.block_name }}</td>
|
||||
<td>{{ village.village_name }}</td>
|
||||
<td class="amount" data-amount="{{ village.total_boq_amount | default(0) }}">
|
||||
{{ village.total_boq_amount | round(2) }}
|
||||
</td>
|
||||
<!-- Claimed Amount -->
|
||||
<td class="amount" data-amount="{{ village.prev_billed_amount | default(0) }}">
|
||||
{{ village.prev_billed_amount | round(2) }}
|
||||
</td>
|
||||
<!-- To Be Claimed Amount -->
|
||||
<td class="amount" data-amount="{{ village.to_be_claimed_amount }}">
|
||||
{{ village.to_be_claimed_amount | round(2) }}
|
||||
</td>
|
||||
<!-- Cumulative = prev billed + to be claimed -->
|
||||
<td class="amount" data-amount="{{ village.cumulative_billed_amount | default(0) | float }}">
|
||||
{{ village.cumulative_billed_amount | default(0) | float | round(2) }}
|
||||
</td>
|
||||
<!-- Total Variation Amount -->
|
||||
<td class="amount" data-amount="{{ village.total_variation_amount | default(0) }}">
|
||||
{{ village.total_variation_amount | round(2) }}
|
||||
</td>
|
||||
<!-- Balance against BOQ -->
|
||||
<td class="amount" data-amount="{{ (village.total_boq_amount | default(0) | float) - (village.cumulative_billed_amount | default(0) | float) - (village.total_variation_amount | default(0) | float) }}">
|
||||
{{ ((village.total_boq_amount | default(0) | float) - (village.cumulative_billed_amount | default(0) | float) - (village.total_variation_amount | default(0) | float)) | round(2) }}
|
||||
</td>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="9" style="text-align: center">No data available.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr class="total-row">
|
||||
<td colspan="3"><strong>Total</strong></td>
|
||||
<td class="amount" id="total-boq"></td>
|
||||
<td class="amount" id="total-billed"></td>
|
||||
<td class="amount" id="total-to-be-claimed"></td>
|
||||
<td class="amount" id="total-cumulative"></td>
|
||||
<td class="amount" id="total-variation"></td>
|
||||
<td class="amount" id="total-balance-against-boq"></td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function formatAmount(amount) {
|
||||
return parseFloat(amount).toFixed(2);
|
||||
}
|
||||
|
||||
|
||||
// Apply formatting to all cells on load
|
||||
window.onload = function () {
|
||||
// Format all amount cells
|
||||
document.querySelectorAll('.amount').forEach(function (cell) {
|
||||
var amount = parseFloat(cell.getAttribute('data-amount'));
|
||||
if (!isNaN(amount)) {
|
||||
cell.textContent = formatAmount(amount);
|
||||
}
|
||||
});
|
||||
|
||||
// Totals
|
||||
let totalBOQ = 0;
|
||||
let totalBilled = 0;
|
||||
let totalToBeClaimed = 0;
|
||||
let totalCumulative = 0;
|
||||
let totalVariation = 0;
|
||||
let totalBalanceAgainstBOQ = 0;
|
||||
|
||||
const rows = document.querySelectorAll('tbody tr:not(.total-row)');
|
||||
rows.forEach(row => {
|
||||
const cells = row.querySelectorAll('td');
|
||||
|
||||
const boq = parseFloat(cells[3]?.getAttribute('data-amount') || 0); // Total BOQ Amount
|
||||
const billed = parseFloat(cells[4]?.getAttribute('data-amount') || 0); // Claimed Amount
|
||||
const toBeClaimed = parseFloat(cells[5]?.getAttribute('data-amount') || 0); // To Be Claimed
|
||||
const cumulative = parseFloat(cells[6]?.getAttribute('data-amount') || 0); // Cumulative
|
||||
const variation = parseFloat(cells[7]?.getAttribute('data-amount') || 0); // Total Variation
|
||||
const balanceAgainstBOQ = parseFloat(cells[8]?.getAttribute('data-amount') || 0); // Balance BOQ
|
||||
|
||||
totalBOQ += boq;
|
||||
totalBilled += billed;
|
||||
totalToBeClaimed += toBeClaimed;
|
||||
totalCumulative += cumulative;
|
||||
totalVariation += variation;
|
||||
totalBalanceAgainstBOQ += balanceAgainstBOQ;
|
||||
});
|
||||
|
||||
document.getElementById('total-boq').textContent = formatAmount(totalBOQ);
|
||||
document.getElementById('total-billed').textContent = formatAmount(totalBilled);
|
||||
document.getElementById('total-to-be-claimed').textContent = formatAmount(totalToBeClaimed);
|
||||
document.getElementById('total-cumulative').textContent = formatAmount(totalCumulative);
|
||||
document.getElementById('total-variation').textContent = formatAmount(totalVariation);
|
||||
document.getElementById('total-balance-against-boq').textContent = formatAmount(totalBalanceAgainstBOQ);
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function filterVillages() {
|
||||
const searchInput = document.getElementById("search-input").value.toLowerCase();
|
||||
const tableBody = document.getElementById("village-table-body");
|
||||
const rows = tableBody.getElementsByTagName("tr");
|
||||
|
||||
Array.from(rows).forEach((row) => {
|
||||
const villageNameCell = row.getElementsByTagName("td")[2]; // 3rd column (Village Name)
|
||||
|
||||
if (villageNameCell) {
|
||||
const villageName = villageNameCell.textContent || villageNameCell.innerText;
|
||||
row.style.display = villageName.toLowerCase().indexOf(searchInput) > -1 ? "" : "none";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleDropdown() {
|
||||
var dropdown = document.getElementById("dropdownContent");
|
||||
dropdown.style.display = dropdown.style.display === "block" ? "none" : "block";
|
||||
}
|
||||
|
||||
document.getElementById("dropdownContent").addEventListener("click", function (event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
// Convert number to Cr-Lakh-Rs format
|
||||
function formatAmountCRL(amount) {
|
||||
if (amount >= 10000000) { // 1 Crore
|
||||
let crore = Math.floor(amount / 10000000);
|
||||
let lakh = Math.floor((amount % 10000000) / 100000);
|
||||
let rupees = Math.floor(amount % 100000);
|
||||
return `${crore} Cr ${lakh} Lakh ${rupees} Rs`;
|
||||
} else if (amount >= 100000) { // 1 Lakh
|
||||
let lakh = Math.floor(amount / 100000);
|
||||
let rupees = Math.floor(amount % 100000);
|
||||
return `${lakh} Lakh ${rupees} Rs`;
|
||||
} else {
|
||||
return `${amount.toFixed(2)} Rs`;
|
||||
}
|
||||
}
|
||||
// Convert number to Cr or Lakh dynamically
|
||||
function formatAmountCRL(amount) {
|
||||
if (amount >= 10000000) { // 1 Crore
|
||||
return (amount / 10000000).toFixed(2) + ' Cr';
|
||||
} else if (amount >= 100000) { // 1 Lakh
|
||||
return (amount / 100000).toFixed(2) + ' Lakh';
|
||||
} else {
|
||||
return amount.toFixed(2);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply formatting to total row on load
|
||||
window.onload = function () {
|
||||
// Calculate totals
|
||||
let totalBOQ = 0;
|
||||
let totalBilled = 0;
|
||||
let totalToBeClaimed = 0;
|
||||
let totalCumulative = 0;
|
||||
let totalVariation = 0;
|
||||
let totalBalanceAgainstBOQ = 0;
|
||||
|
||||
const rows = document.querySelectorAll('tbody tr:not(.total-row)');
|
||||
rows.forEach(row => {
|
||||
const cells = row.querySelectorAll('td');
|
||||
|
||||
totalBOQ += parseFloat(cells[3]?.getAttribute('data-amount') || 0);
|
||||
totalBilled += parseFloat(cells[4]?.getAttribute('data-amount') || 0);
|
||||
totalToBeClaimed += parseFloat(cells[5]?.getAttribute('data-amount') || 0);
|
||||
totalCumulative += parseFloat(cells[6]?.getAttribute('data-amount') || 0);
|
||||
totalVariation += parseFloat(cells[7]?.getAttribute('data-amount') || 0);
|
||||
totalBalanceAgainstBOQ += parseFloat(cells[8]?.getAttribute('data-amount') || 0);
|
||||
});
|
||||
|
||||
document.getElementById('total-boq').textContent = formatAmountCRL(totalBOQ);
|
||||
document.getElementById('total-billed').textContent = formatAmountCRL(totalBilled);
|
||||
document.getElementById('total-to-be-claimed').textContent = formatAmountCRL(totalToBeClaimed);
|
||||
document.getElementById('total-cumulative').textContent = formatAmountCRL(totalCumulative);
|
||||
document.getElementById('total-variation').textContent = formatAmountCRL(totalVariation);
|
||||
document.getElementById('total-balance-against-boq').textContent = formatAmountCRL(totalBalanceAgainstBOQ);
|
||||
};
|
||||
|
||||
|
||||
window.onclick = function (e) {
|
||||
if (!e.target.matches('.dropdown-btn')) {
|
||||
var dropdowns = document.getElementsByClassName("dropdown-content");
|
||||
for (var i = 0; i < dropdowns.length; i++) {
|
||||
var openDropdown = dropdowns[i];
|
||||
if (openDropdown.classList.contains('show')) {
|
||||
openDropdown.classList.remove('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
90
app/templates/login.html
Normal file
90
app/templates/login.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>LCEPL Client Billing Software</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background: #f3f3f3;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.flash {
|
||||
color: red;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.title {
|
||||
text-align: center;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<h1 class="title">Laxmi Civil Engineering Services</h1>
|
||||
<h4 class="subtitle">LOGIN</h4>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="flash flash-{{ category }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
|
||||
|
||||
<form method="post">
|
||||
<input type="text" name="username" placeholder="Username" required />
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
required
|
||||
/>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
244
app/templates/task_report.html
Normal file
244
app/templates/task_report.html
Normal file
@@ -0,0 +1,244 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Task Report Generator</title>
|
||||
|
||||
<!-- Select2 CSS -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background-color: #f4f7fc;
|
||||
padding: 0 20px;
|
||||
margin-left: 275px;
|
||||
}
|
||||
|
||||
h1.report_title {
|
||||
text-align: center;
|
||||
margin-top: 100px;
|
||||
color: #333;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
form {
|
||||
max-width: 500px;
|
||||
margin: 60px auto;
|
||||
padding: 30px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
form label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
select, button {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 16px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ddd;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover { background-color: #218838; }
|
||||
|
||||
.form-group { margin-bottom: 15px; }
|
||||
|
||||
.sidebar {
|
||||
width: 295px;
|
||||
background-color: #f4f4f4;
|
||||
box-shadow: 2px 0 5px rgba(0,0,0,0.1);
|
||||
padding: 20px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.sidebar .logo { width: 60px; margin-bottom: 20px; }
|
||||
|
||||
.sidebar a {
|
||||
display: block;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
padding: 10px 15px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar a:hover {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="sidebar">
|
||||
<!-- <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA..." class="logo"/> -->
|
||||
<img
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA+CAYAAAB3NHh5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4JpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTMyIDc5LjE1OTI4NCwgMjAxNi8wNC8xOS0xMzoxMzo0MCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NkEzOEVGMzEwQTQxMUU4QUFBQkY0QTA2QzlEM0MxNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2QUQzQTIzRDE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2QUQzQTIzQzE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNS41IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhNmE4ZTBmLTI5ZWItNDBlMy05ZWFhLTYzNTdiYjdkMzcwNCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmRhMjgyMWMwLTYwYmQtMTE3Yi04ZGU3LWNjZmQ1MDgzNjUxNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrVd/GcAAASESURBVHja7FtrSBRRFL7rrg9MMjUzspTSSqUg6GFBmohh9SeIMOhXhBX0KzL80YN+SBD+qJ+RBhFEQogV0RMLKyuiqIig6GlZRmlRavl2O8c9I7dtZ2adM3d9zB742GHuzpn7zZl7zrnn3nF5vV7hJHEh4aLKaxfgeAXAjee0NoDX7ziCjl2yDsANwHbAsQD3mALYA3ii04e9gEZAP6BM5z9nAA2AKos8FwK+1pcX53noxCJAMuPBzQLEATbotEfpnMeHfIiuSzK4vhnw2KA9GJkryGIoz5hvyjlAr0F7e4BzuYBbdPwG8Mvg+m+APmYfh/qgWbiXqWzQpN3fUSyj19hj8gbYKV7Zwsp9hXS8BHBXIhsqiQglYTf9LgbcGQWywxKqG7cAssiy0aMVkWQLpzGVoYfvMWjvBlQwyKKPiWT2MU628DZACkPZa0CGQXs00zHOBnwBrLF4fTbgnb+X7mJ0qN/EH3iZYUVLfKz2sQMwSSZ8kkKFVTkKOKxw/KF1Zkhx26rUaFb5xFTUpjiWRtmQeHSGMiyNGQkTdgrhVKaeJBvycbM4zE2S/vHSpTRv7beo7CEgXiFh7GwTTSetCE5/f8iEMdOaajJFM5KtgHyFhDcC/gBeMfKEeJnwAcDyMTz0FgAqmTowG6zWxnCrA/zVc9lp9TmAcEgLAOE4HCY8zgoAZuI28RPdiuO45umHw9LuIKoWHPlOiY1RNeIETfI5gkWM6YABv/MZFMeHCWONOAFwWRFhrKjkEPFAshMwB/CReZ9LZn/QCM8ErBe+ZQ9VUid8JdpAclH4llO40kmFjBdmTmsXYL4NFQWzIkGXDt7adA8cGlgZzQrGS+OMpABwW2HVwkrbSAWH5gNBa0nBhCUk3TjOQ9BkIp0phyQ9wl6a+dwb56QTiEOmkNa2IgzyzpWA+zZ2oMNiG0eSydLp/l5aj/Qq4VsLyrXh5jgJz9NpK1Bo6UQqHuCCfbVZ2aSP5smbBW+pYx5gh4FDRM+6D/BZEWksQf2ULVxC5g80L8YqyFPx/xrvSOQUjaUSnfYyevWyFVr6t0wY92AsVXizJJNximnfFuFbAVElGHnyNKfVothjDpr4iyjNAgqlVfbSA+GKR7gAMLEIexzA1SMTjnUA4VjZspsAMQxluGqxVqhbFMetjQcB7xk6umXChUZzyCCkFnBcIeGblI3tZ+h4if3UCJczE49EoXbLA87VcWNLBUMHLvjVamO42YZqxljf8tAcjsNOIuxyGuEMGyoLPQr72SP4Ww/T5LBURZ226rweyWUUC+I2mW9jZ3GTeCnjHk0y4fOCNl8yvHQO4/oBYbzJPIaszKmmdsiEa4R+vSkYwe0IRyxee0X4PkEoNPgPfiKQSsmDVcHaXL5HshBH2i2OsauAdXRstNgWacOcvU12WtyPl6xcf53yb01Ub7sIeQHA6zcZKJ7IiYdMth5QNNpxOJ2pZ5rJGMZcGOvbuEq4WifZSWHoD0bSZS99FvCB6QHRcdXptONOuNP0KuuN+wYiZkV/MPJg6Ck77WPLvwIMAHzX4zyhUFlrAAAAAElFTkSuQmCC"
|
||||
alt="LCEPL Logo"
|
||||
/>
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/upload_excel">Upload Excel</a>
|
||||
<a href="/generate_report_page">Print Report</a>
|
||||
<a href="/filter_tasks">Filter Tasks</a>
|
||||
</div>
|
||||
|
||||
<div class="header">LAXMI CIVIL ENGINEERING SERVICES PVT. LTD.</div>
|
||||
|
||||
<h1 class="report_title">Generate Task Report</h1>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<script>
|
||||
alert("{{ message }}");
|
||||
</script>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form action="/report_excel" method="GET">
|
||||
|
||||
<!-- District -->
|
||||
<div class="form-group">
|
||||
<label for="district">District</label>
|
||||
<select name="district" id="district" required>
|
||||
<option value="" disabled selected>Select District</option>
|
||||
{% for d in districts %}
|
||||
<option value="{{ d }}">{{ d }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Block -->
|
||||
<div class="form-group">
|
||||
<label for="block">Block</label>
|
||||
<select name="block" id="block" required>
|
||||
<option value="" disabled selected>Select Block</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Main Task -->
|
||||
<div class="form-group">
|
||||
<label for="main_task">Main Task</label>
|
||||
<select name="main_task" id="main_task" required>
|
||||
<option value="" disabled selected>Select Main Task</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit">Generate Report</button>
|
||||
|
||||
</form>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
|
||||
<!-- Select2 -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
|
||||
$('#district').select2({ width: '100%' });
|
||||
$('#block').select2({ width: '100%' });
|
||||
$('#main_task').select2({ width: '100%' });
|
||||
|
||||
// District → Blocks
|
||||
$('#district').on('change', function () {
|
||||
|
||||
const district = $(this).val();
|
||||
|
||||
$('#block').empty().append('<option value="">Select Block</option>');
|
||||
$('#main_task').empty().append('<option value="">Select Main Task</option>');
|
||||
|
||||
if (!district) return;
|
||||
|
||||
$.ajax({
|
||||
url: '/get_blocks_by_district',
|
||||
method: 'GET',
|
||||
data: { district: district },
|
||||
success: function (response) {
|
||||
|
||||
response.blocks.forEach(block => {
|
||||
$('#block').append(`<option value="${block}">${block}</option>`);
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Block → Tasks
|
||||
$('#block').on('change', function () {
|
||||
|
||||
const block = $(this).val();
|
||||
|
||||
$('#main_task').empty().append('<option value="">Select Main Task</option>');
|
||||
|
||||
if (!block) return;
|
||||
|
||||
$.ajax({
|
||||
url: '/get_tasks_by_block',
|
||||
method: 'GET',
|
||||
data: { block: block },
|
||||
success: function (response) {
|
||||
|
||||
response.tasks.forEach(task => {
|
||||
$('#main_task').append(`<option value="${task}">${task}</option>`);
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
366
app/templates/tasks_display.html
Normal file
366
app/templates/tasks_display.html
Normal file
@@ -0,0 +1,366 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Tasks</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f8f9fa;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
text-align: center;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 90%;
|
||||
margin: 10px auto;
|
||||
border-collapse: collapse;
|
||||
background-color: #fff;
|
||||
font-size: 0.85em;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
margin-left: 285px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.main-task-row {
|
||||
background-color: #e3f2fd;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtask-row td {
|
||||
background-color: #fefefe;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.button-container a,
|
||||
.button-container button {
|
||||
padding: 10px 20px;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
background-color: #007bff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button-container a {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.top-buttons {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
.top-buttons button {
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.top-buttons button:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
.static-text {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.edit-field {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#searchBox {
|
||||
width: 300px;
|
||||
padding: 8px;
|
||||
margin: 0 auto 10px;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#taskTableWrapper {
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
margin: 15px auto;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #007bff;
|
||||
border-radius: 50%;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
animation: spin 0.8s linear infinite;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Sidebar Styling */
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
background-color: #f4f4f4;
|
||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.sidebar .logo {
|
||||
width: 60px;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.sidebar a {
|
||||
display: block;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
padding: 10px 15px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar a:hover {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Sidebar -->
|
||||
<div class="sidebar">
|
||||
<img
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA+CAYAAAB3NHh5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4JpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTMyIDc5LjE1OTI4NCwgMjAxNi8wNC8xOS0xMzoxMzo0MCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NkEzOEVGMzEwQTQxMUU4QUFBQkY0QTA2QzlEM0MxNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2QUQzQTIzRDE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2QUQzQTIzQzE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNS41IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhNmE4ZTBmLTI5ZWItNDBlMy05ZWFhLTYzNTdiYjdkMzcwNCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmRhMjgyMWMwLTYwYmQtMTE3Yi04ZGU3LWNjZmQ1MDgzNjUxNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrVd/GcAAASESURBVHja7FtrSBRRFL7rrg9MMjUzspTSSqUg6GFBmohh9SeIMOhXhBX0KzL80YN+SBD+qJ+RBhFEQogV0RMLKyuiqIig6GlZRmlRavl2O8c9I7dtZ2adM3d9zB742GHuzpn7zZl7zrnn3nF5vV7hJHEh4aLKaxfgeAXAjee0NoDX7ziCjl2yDsANwHbAsQD3mALYA3ii04e9gEZAP6BM5z9nAA2AKos8FwK+1pcX53noxCJAMuPBzQLEATbotEfpnMeHfIiuSzK4vhnw2KA9GJkryGIoz5hvyjlAr0F7e4BzuYBbdPwG8Mvg+m+APmYfh/qgWbiXqWzQpN3fUSyj19hj8gbYKV7Zwsp9hXS8BHBXIhsqiQglYTf9LgbcGQWywxKqG7cAssiy0aMVkWQLpzGVoYfvMWjvBlQwyKKPiWT2MU628DZACkPZa0CGQXs00zHOBnwBrLF4fTbgnb+X7mJ0qN/EH3iZYUVLfKz2sQMwSSZ8kkKFVTkKOKxw/KF1Zkhx26rUaFb5xFTUpjiWRtmQeHSGMiyNGQkTdgrhVKaeJBvycbM4zE2S/vHSpTRv7beo7CEgXiFh7GwTTSetCE5/f8iEMdOaajJFM5KtgHyFhDcC/gBeMfKEeJnwAcDyMTz0FgAqmTowG6zWxnCrA/zVc9lp9TmAcEgLAOE4HCY8zgoAZuI28RPdiuO45umHw9LuIKoWHPlOiY1RNeIETfI5gkWM6YABv/MZFMeHCWONOAFwWRFhrKjkEPFAshMwB/CReZ9LZn/QCM8ErBe+ZQ9VUid8JdpAclH4llO40kmFjBdmTmsXYL4NFQWzIkGXDt7adA8cGlgZzQrGS+OMpABwW2HVwkrbSAWH5gNBa0nBhCUk3TjOQ9BkIp0phyQ9wl6a+dwb56QTiEOmkNa2IgzyzpWA+zZ2oMNiG0eSydLp/l5aj/Qq4VsLyrXh5jgJz9NpK1Bo6UQqHuCCfbVZ2aSP5smbBW+pYx5gh4FDRM+6D/BZEWksQf2ULVxC5g80L8YqyFPx/xrvSOQUjaUSnfYyevWyFVr6t0wY92AsVXizJJNximnfFuFbAVElGHnyNKfVothjDpr4iyjNAgqlVfbSA+GKR7gAMLEIexzA1SMTjnUA4VjZspsAMQxluGqxVqhbFMetjQcB7xk6umXChUZzyCCkFnBcIeGblI3tZ+h4if3UCJczE49EoXbLA87VcWNLBUMHLvjVamO42YZqxljf8tAcjsNOIuxyGuEMGyoLPQr72SP4Ww/T5LBURZ226rweyWUUC+I2mW9jZ3GTeCnjHk0y4fOCNl8yvHQO4/oBYbzJPIaszKmmdsiEa4R+vSkYwe0IRyxee0X4PkEoNPgPfiKQSsmDVcHaXL5HshBH2i2OsauAdXRstNgWacOcvU12WtyPl6xcf53yb01Ub7sIeQHA6zcZKJ7IiYdMth5QNNpxOJ2pZ5rJGMZcGOvbuEq4WifZSWHoD0bSZS99FvCB6QHRcdXptONOuNP0KuuN+wYiZkV/MPJg6Ck77WPLvwIMAHzX4zyhUFlrAAAAAElFTkSuQmCC"
|
||||
alt="LCEPL Logo"
|
||||
class="logo"
|
||||
/>
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/upload_excel">Upload Excel</a>
|
||||
<a href="/generate_report_page">Print Report</a>
|
||||
<!--<a href="/tasks">Show Tasks</a>-->
|
||||
<a href="/filter_tasks">Filter Tasks</a>
|
||||
</div>
|
||||
<div class="header">LAXMI CIVIL ENGINEERING SERVICES PVT. LTD.</div>
|
||||
<h1>Uploaded Tasks</h1>
|
||||
|
||||
<div class="top-buttons">
|
||||
<button onclick="window.location.href='/'">Home Page</button>
|
||||
</div>
|
||||
|
||||
<div class="button-container">
|
||||
<a href="/generate_report_page">Print Task Wise Report</a>
|
||||
<button onclick="toggleEditMode()">Edit Tasks</button>
|
||||
<button onclick="cancelEditMode()" id="cancelBtn" style="display: none; background-color: #dc3545;">Cancel Edit</button>
|
||||
<button onclick="submitChangedFields()">Submit Updates</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="text" id="searchBox" placeholder="Search tasks..." />
|
||||
</div>
|
||||
|
||||
{% if work_details %}
|
||||
<h2>Work Details</h2>
|
||||
<table class="work-details">
|
||||
<tr><th>Name of Work</th><td>{{ work_details.name_of_work }}</td></tr>
|
||||
<tr><th>Cover Agreement No</th><td>{{ work_details.cover_agreement_no }}</td></tr>
|
||||
<tr><th>Name of Contractor</th><td>{{ work_details.name_of_contractor }}</td></tr>
|
||||
<tr><th>Name of TPI Agency</th><td>{{ work_details.name_of_tpi_agency }}</td></tr>
|
||||
<tr><th>Name of Division</th><td>{{ work_details.name_of_division }}</td></tr>
|
||||
<tr><th>Name of District</th><td>{{ work_details.district }}</td></tr>
|
||||
<tr><th>Village</th><td>{{ work_details.name_of_village }}</td></tr>
|
||||
<tr><th>Block</th><td>{{ work_details.block }}</td></tr>
|
||||
<tr><th>Scheme ID</th><td>{{ work_details.scheme_id }}</td></tr>
|
||||
<tr><th>Measurement Book</th><td>{{ work_details.measurement_book }}</td></tr>
|
||||
<tr><th>Date of Billing</th><td>{{ work_details.date_of_billing }}</td></tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
<h2>Task Details</h2>
|
||||
<div class="spinner" id="loader"></div>
|
||||
|
||||
<div id="taskTableWrapper" style="opacity: 1;">
|
||||
<form id="task-form">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Main Task</th>
|
||||
<th>Unit</th>
|
||||
<th>Qty</th>
|
||||
<th>Rate</th>
|
||||
<th>BOQ Amt</th>
|
||||
<th>Prev Billed Qty</th>
|
||||
<th>Prev Bill Amt</th>
|
||||
<th>RA Bill Qty</th>
|
||||
<th>RA Bill Amt</th>
|
||||
<th>Cum Billed Qty</th>
|
||||
<th>Cum Billed Amt</th>
|
||||
<th>Var Qty</th>
|
||||
<th>Var Amt</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for group in grouped_tasks %}
|
||||
<tr class="main-task-row">
|
||||
{% for field in ["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"] %}
|
||||
<td>
|
||||
<span class="static-text">{{ group[field] }}</span>
|
||||
<input type="text" class="edit-field" name="{{ field }}_{{ group.id }}" value="{{ group[field] }}" data-original-value="{{ group[field] | trim }}" style="display: none;" />
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% for sub in group.subtasks %}
|
||||
<tr class="subtask-row">
|
||||
{% for field in ["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"] %}
|
||||
<td>
|
||||
<span class="static-text">{{ sub[field] }}</span>
|
||||
<input type="text" class="edit-field" name="{{ field }}_{{ sub.id }}" value="{{ sub[field] }}" data-original-value="{{ sub[field] | trim }}" style="display: none;" />
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
function toggleEditMode() {
|
||||
document.querySelectorAll('.static-text').forEach(span => span.style.display = 'none');
|
||||
document.querySelectorAll('.edit-field').forEach(input => input.style.display = 'inline-block');
|
||||
document.getElementById('cancelBtn').style.display = 'inline-block';
|
||||
}
|
||||
|
||||
function cancelEditMode() {
|
||||
document.querySelectorAll('.static-text').forEach(span => span.style.display = 'inline-block');
|
||||
document.querySelectorAll('.edit-field').forEach(input => {
|
||||
input.style.display = 'none';
|
||||
input.value = input.getAttribute('data-original-value');
|
||||
});
|
||||
document.getElementById('cancelBtn').style.display = 'none';
|
||||
}
|
||||
|
||||
function submitChangedFields() {
|
||||
const updates = {};
|
||||
document.querySelectorAll('.edit-field').forEach(input => {
|
||||
const original = input.getAttribute('data-original-value') || '';
|
||||
const current = input.value.trim();
|
||||
if (current !== original.trim()) {
|
||||
updates[input.name] = current;
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(updates).length === 0) {
|
||||
alert("No changes made.");
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/update_tasks', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(updates)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
alert(data.message || 'Update complete!');
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Update error:', error);
|
||||
alert('Error updating tasks');
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const searchBox = document.getElementById("searchBox");
|
||||
const tableWrapper = document.getElementById("taskTableWrapper");
|
||||
const loader = document.getElementById("loader");
|
||||
|
||||
document.querySelectorAll("tbody tr").forEach(row => {
|
||||
const text = Array.from(row.querySelectorAll("td")).map(cell => {
|
||||
const span = cell.querySelector(".static-text")?.innerText || '';
|
||||
const input = cell.querySelector(".edit-field")?.value || '';
|
||||
return (span + " " + input).toLowerCase();
|
||||
}).join(" ");
|
||||
row.setAttribute("data-search-text", text);
|
||||
});
|
||||
|
||||
let timeout;
|
||||
searchBox.addEventListener("input", function () {
|
||||
clearTimeout(timeout);
|
||||
loader.style.display = "block";
|
||||
tableWrapper.style.opacity = "0.3";
|
||||
|
||||
const term = this.value.toLowerCase();
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
document.querySelectorAll("tbody tr").forEach(row => {
|
||||
const searchable = row.getAttribute("data-search-text");
|
||||
row.style.display = searchable.includes(term) ? "" : "none";
|
||||
});
|
||||
loader.style.display = "none";
|
||||
tableWrapper.style.opacity = "1";
|
||||
}, 150);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
166
app/templates/upload.html
Normal file
166
app/templates/upload.html
Normal file
@@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Upload Excel</title>
|
||||
<style>
|
||||
/* General Styling */
|
||||
*{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 50vh;
|
||||
background-color: #f4f7f9;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
/* Sidebar Styling */
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
background-color: #f4f4f4;
|
||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.sidebar .logo {
|
||||
width: 60px;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.sidebar a {
|
||||
display: block;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
padding: 10px 15px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar a:hover {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Main Container Styling */
|
||||
.main-content {
|
||||
margin: 400px auto;
|
||||
/* margin-left: 270px; */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
width: calc(100% - 270px);
|
||||
}
|
||||
|
||||
form {
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
color: #007bff;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
input[type="file"]:focus {
|
||||
border-color: #007bff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.upload {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: #007bff;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Sidebar -->
|
||||
<div class="sidebar">
|
||||
<img
|
||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA+CAYAAAB3NHh5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4JpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTMyIDc5LjE1OTI4NCwgMjAxNi8wNC8xOS0xMzoxMzo0MCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NkEzOEVGMzEwQTQxMUU4QUFBQkY0QTA2QzlEM0MxNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2QUQzQTIzRDE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2QUQzQTIzQzE4NTkxMUU4ODE2Q0IwMTY0RjgxQTVGNyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNS41IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhNmE4ZTBmLTI5ZWItNDBlMy05ZWFhLTYzNTdiYjdkMzcwNCIgc3RSZWY6ZG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOmRhMjgyMWMwLTYwYmQtMTE3Yi04ZGU3LWNjZmQ1MDgzNjUxNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrVd/GcAAASESURBVHja7FtrSBRRFL7rrg9MMjUzspTSSqUg6GFBmohh9SeIMOhXhBX0KzL80YN+SBD+qJ+RBhFEQogV0RMLKyuiqIig6GlZRmlRavl2O8c9I7dtZ2adM3d9zB742GHuzpn7zZl7zrnn3nF5vV7hJHEh4aLKaxfgeAXAjee0NoDX7ziCjl2yDsANwHbAsQD3mALYA3ii04e9gEZAP6BM5z9nAA2AKos8FwK+1pcX53noxCJAMuPBzQLEATbotEfpnMeHfIiuSzK4vhnw2KA9GJkryGIoz5hvyjlAr0F7e4BzuYBbdPwG8Mvg+m+APmYfh/qgWbiXqWzQpN3fUSyj19hj8gbYKV7Zwsp9hXS8BHBXIhsqiQglYTf9LgbcGQWywxKqG7cAssiy0aMVkWQLpzGVoYfvMWjvBlQwyKKPiWT2MU628DZACkPZa0CGQXs00zHOBnwBrLF4fTbgnb+X7mJ0qN/EH3iZYUVLfKz2sQMwSSZ8kkKFVTkKOKxw/KF1Zkhx26rUaFb5xFTUpjiWRtmQeHSGMiyNGQkTdgrhVKaeJBvycbM4zE2S/vHSpTRv7beo7CEgXiFh7GwTTSetCE5/f8iEMdOaajJFM5KtgHyFhDcC/gBeMfKEeJnwAcDyMTz0FgAqmTowG6zWxnCrA/zVc9lp9TmAcEgLAOE4HCY8zgoAZuI28RPdiuO45umHw9LuIKoWHPlOiY1RNeIETfI5gkWM6YABv/MZFMeHCWONOAFwWRFhrKjkEPFAshMwB/CReZ9LZn/QCM8ErBe+ZQ9VUid8JdpAclH4llO40kmFjBdmTmsXYL4NFQWzIkGXDt7adA8cGlgZzQrGS+OMpABwW2HVwkrbSAWH5gNBa0nBhCUk3TjOQ9BkIp0phyQ9wl6a+dwb56QTiEOmkNa2IgzyzpWA+zZ2oMNiG0eSydLp/l5aj/Qq4VsLyrXh5jgJz9NpK1Bo6UQqHuCCfbVZ2aSP5smbBW+pYx5gh4FDRM+6D/BZEWksQf2ULVxC5g80L8YqyFPx/xrvSOQUjaUSnfYyevWyFVr6t0wY92AsVXizJJNximnfFuFbAVElGHnyNKfVothjDpr4iyjNAgqlVfbSA+GKR7gAMLEIexzA1SMTjnUA4VjZspsAMQxluGqxVqhbFMetjQcB7xk6umXChUZzyCCkFnBcIeGblI3tZ+h4if3UCJczE49EoXbLA87VcWNLBUMHLvjVamO42YZqxljf8tAcjsNOIuxyGuEMGyoLPQr72SP4Ww/T5LBURZ226rweyWUUC+I2mW9jZ3GTeCnjHk0y4fOCNl8yvHQO4/oBYbzJPIaszKmmdsiEa4R+vSkYwe0IRyxee0X4PkEoNPgPfiKQSsmDVcHaXL5HshBH2i2OsauAdXRstNgWacOcvU12WtyPl6xcf53yb01Ub7sIeQHA6zcZKJ7IiYdMth5QNNpxOJ2pZ5rJGMZcGOvbuEq4WifZSWHoD0bSZS99FvCB6QHRcdXptONOuNP0KuuN+wYiZkV/MPJg6Ck77WPLvwIMAHzX4zyhUFlrAAAAAElFTkSuQmCC"
|
||||
alt="LCEPL Logo"
|
||||
class="logo"
|
||||
/>
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/upload_excel">Upload Excel</a>
|
||||
<a href="/generate_report_page">Print Report</a>
|
||||
<!--<a href="/tasks">Show Tasks</a>-->
|
||||
<a href="/filter_tasks">Filter Tasks</a>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="header">LAXMI CIVIL ENGINEERING SERVICES PVT. LTD.</div>
|
||||
<h1>Upload Excel File</h1>
|
||||
<input type="file" name="file" required />
|
||||
<button class="upload" type="submit">Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
app/uploads/task_report_report.xlsx
Normal file
BIN
app/uploads/task_report_report.xlsx
Normal file
Binary file not shown.
110
docker-compose.yml
Normal file
110
docker-compose.yml
Normal file
@@ -0,0 +1,110 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.11.28
|
||||
command:
|
||||
- "--api.insecure=true"
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80" # container internal port
|
||||
ports:
|
||||
- "8085:80" # host port changed to 8085
|
||||
- "8090:8080" # Traefik dashboard
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
networks:
|
||||
- billing-network
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://127.0.0.1:8080/api/overview || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
flask-app:
|
||||
build: .
|
||||
ports:
|
||||
- "5003:5002" # internal container port
|
||||
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
openldap:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
FLASK_APP: run:app
|
||||
FLASK_ENV: development
|
||||
FLASK_RUN_HOST: 0.0.0.0
|
||||
FLASK_RUN_PORT: 5002
|
||||
MYSQL_HOST: mysql
|
||||
MYSQL_PORT: 3306
|
||||
MYSQL_USER: root
|
||||
MYSQL_PASSWORD: admin
|
||||
MYSQL_DB: excel_data7
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.client-billing-flask.rule=Host(`flask.lcepl.local`)"
|
||||
- "traefik.http.routers.client-billing-flask.entrypoints=web"
|
||||
- "traefik.http.services.client-billing-flask.loadbalancer.server.port=5002"
|
||||
networks:
|
||||
- billing-network
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:5002/"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: admin
|
||||
MYSQL_DATABASE: excel_data7
|
||||
ports:
|
||||
- "3307:3306" # external access if needed
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
networks:
|
||||
- billing-network
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -padmin --silent"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
|
||||
openldap:
|
||||
image: osixia/openldap:1.5.0
|
||||
environment:
|
||||
LDAP_ORGANISATION: "LCEPL"
|
||||
LDAP_DOMAIN: "lcepl.org"
|
||||
LDAP_ADMIN_PASSWORD: admin
|
||||
ports:
|
||||
- "1389:389" # optional external access
|
||||
- "1636:636"
|
||||
volumes:
|
||||
- openldap-data:/var/lib/ldap
|
||||
- openldap-config:/etc/ldap/slapd.d
|
||||
- ./ldifs:/container/service/slapd/assets/bootstrap/ldifs:rw
|
||||
networks:
|
||||
- billing-network
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "ldapsearch -x -H ldap://127.0.0.1:389 -D 'cn=admin,dc=lcepl,dc=org' -w admin -b 'dc=lcepl,dc=org' >/dev/null 2>&1 || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 10s
|
||||
retries: 20
|
||||
start_period: 60s
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
openldap-data:
|
||||
openldap-config:
|
||||
|
||||
networks:
|
||||
billing-network:
|
||||
driver: bridge
|
||||
BIN
instance/default.db
Normal file
BIN
instance/default.db
Normal file
Binary file not shown.
23
ldap-config/cn=config.ldif
Normal file
23
ldap-config/cn=config.ldif
Normal file
@@ -0,0 +1,23 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 573b04a1
|
||||
dn: cn=config
|
||||
objectClass: olcGlobal
|
||||
cn: config
|
||||
olcArgsFile: /var/run/slapd/slapd.args
|
||||
olcLogLevel: none
|
||||
olcPidFile: /var/run/slapd/slapd.pid
|
||||
olcToolThreads: 1
|
||||
structuralObjectClass: olcGlobal
|
||||
entryUUID: 40b79a56-16b1-1040-911b-a716cb68c9fb
|
||||
creatorsName: cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
olcTLSCipherSuite: SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE
|
||||
-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC
|
||||
olcTLSCACertificateFile: /container/service/slapd/assets/certs/ca.crt
|
||||
olcTLSCertificateFile: /container/service/slapd/assets/certs/ldap.crt
|
||||
olcTLSCertificateKeyFile: /container/service/slapd/assets/certs/ldap.key
|
||||
olcTLSDHParamFile: /container/service/slapd/assets/certs/dhparam.pem
|
||||
olcTLSVerifyClient: demand
|
||||
entryCSN: 20250828121450.388111Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250828121450Z
|
||||
16
ldap-config/cn=config/cn=module{0}.ldif
Normal file
16
ldap-config/cn=config/cn=module{0}.ldif
Normal file
@@ -0,0 +1,16 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 05e25886
|
||||
dn: cn=module{0}
|
||||
objectClass: olcModuleList
|
||||
cn: module{0}
|
||||
olcModulePath: /usr/lib/ldap
|
||||
olcModuleLoad: {0}back_mdb
|
||||
olcModuleLoad: {1}memberof
|
||||
olcModuleLoad: {2}refint
|
||||
structuralObjectClass: olcModuleList
|
||||
entryUUID: 40d348aa-16b1-1040-9123-a716cb68c9fb
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
entryCSN: 20250826101454.917289Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250826101454Z
|
||||
12
ldap-config/cn=config/cn=schema.ldif
Normal file
12
ldap-config/cn=config/cn=schema.ldif
Normal file
@@ -0,0 +1,12 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 3c314b20
|
||||
dn: cn=schema
|
||||
objectClass: olcSchemaConfig
|
||||
cn: schema
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 40c25608-16b1-1040-911e-a716cb68c9fb
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
entryCSN: 20250826101453.598216Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20250826101453Z
|
||||
249
ldap-config/cn=config/cn=schema/cn={0}core.ldif
Normal file
249
ldap-config/cn=config/cn=schema/cn={0}core.ldif
Normal file
@@ -0,0 +1,249 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 663efec9
|
||||
dn: cn={0}core
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {0}core
|
||||
olcAttributeTypes: {0}( 2.5.4.2 NAME 'knowledgeInformation' DESC 'RFC2256: k
|
||||
nowledge information' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.15{32768} )
|
||||
olcAttributeTypes: {1}( 2.5.4.4 NAME ( 'sn' 'surname' ) DESC 'RFC2256: last
|
||||
(family) name(s) for which the entity is known by' SUP name )
|
||||
olcAttributeTypes: {2}( 2.5.4.5 NAME 'serialNumber' DESC 'RFC2256: serial nu
|
||||
mber of the entity' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{64} )
|
||||
olcAttributeTypes: {3}( 2.5.4.6 NAME ( 'c' 'countryName' ) DESC 'RFC4519: tw
|
||||
o-letter ISO-3166 country code' SUP name SYNTAX 1.3.6.1.4.1.1466.115.121.1.
|
||||
11 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 2.5.4.7 NAME ( 'l' 'localityName' ) DESC 'RFC2256: l
|
||||
ocality which this object resides in' SUP name )
|
||||
olcAttributeTypes: {5}( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' ) DESC 'RF
|
||||
C2256: state or province which this object resides in' SUP name )
|
||||
olcAttributeTypes: {6}( 2.5.4.9 NAME ( 'street' 'streetAddress' ) DESC 'RFC2
|
||||
256: street address of this object' EQUALITY caseIgnoreMatch SUBSTR caseIgn
|
||||
oreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {7}( 2.5.4.10 NAME ( 'o' 'organizationName' ) DESC 'RFC22
|
||||
56: organization this object belongs to' SUP name )
|
||||
olcAttributeTypes: {8}( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' ) DESC
|
||||
'RFC2256: organizational unit this object belongs to' SUP name )
|
||||
olcAttributeTypes: {9}( 2.5.4.12 NAME 'title' DESC 'RFC2256: title associate
|
||||
d with the entity' SUP name )
|
||||
olcAttributeTypes: {10}( 2.5.4.14 NAME 'searchGuide' DESC 'RFC2256: search g
|
||||
uide, deprecated by enhancedSearchGuide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.
|
||||
25 )
|
||||
olcAttributeTypes: {11}( 2.5.4.15 NAME 'businessCategory' DESC 'RFC2256: bus
|
||||
iness category' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch S
|
||||
YNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {12}( 2.5.4.16 NAME 'postalAddress' DESC 'RFC2256: postal
|
||||
address' EQUALITY caseIgnoreListMatch SUBSTR caseIgnoreListSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
|
||||
olcAttributeTypes: {13}( 2.5.4.17 NAME 'postalCode' DESC 'RFC2256: postal co
|
||||
de' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.15{40} )
|
||||
olcAttributeTypes: {14}( 2.5.4.18 NAME 'postOfficeBox' DESC 'RFC2256: Post O
|
||||
ffice Box' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX
|
||||
1.3.6.1.4.1.1466.115.121.1.15{40} )
|
||||
olcAttributeTypes: {15}( 2.5.4.19 NAME 'physicalDeliveryOfficeName' DESC 'RF
|
||||
C2256: Physical Delivery Office Name' EQUALITY caseIgnoreMatch SUBSTR caseI
|
||||
gnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {16}( 2.5.4.20 NAME 'telephoneNumber' DESC 'RFC2256: Tele
|
||||
phone Number' EQUALITY telephoneNumberMatch SUBSTR telephoneNumberSubstring
|
||||
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.50{32} )
|
||||
olcAttributeTypes: {17}( 2.5.4.21 NAME 'telexNumber' DESC 'RFC2256: Telex Nu
|
||||
mber' SYNTAX 1.3.6.1.4.1.1466.115.121.1.52 )
|
||||
olcAttributeTypes: {18}( 2.5.4.22 NAME 'teletexTerminalIdentifier' DESC 'RFC
|
||||
2256: Teletex Terminal Identifier' SYNTAX 1.3.6.1.4.1.1466.115.121.1.51 )
|
||||
olcAttributeTypes: {19}( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' )
|
||||
DESC 'RFC2256: Facsimile (Fax) Telephone Number' SYNTAX 1.3.6.1.4.1.1466.11
|
||||
5.121.1.22 )
|
||||
olcAttributeTypes: {20}( 2.5.4.24 NAME 'x121Address' DESC 'RFC2256: X.121 Ad
|
||||
dress' EQUALITY numericStringMatch SUBSTR numericStringSubstringsMatch SYNT
|
||||
AX 1.3.6.1.4.1.1466.115.121.1.36{15} )
|
||||
olcAttributeTypes: {21}( 2.5.4.25 NAME 'internationaliSDNNumber' DESC 'RFC22
|
||||
56: international ISDN number' EQUALITY numericStringMatch SUBSTR numericSt
|
||||
ringSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{16} )
|
||||
olcAttributeTypes: {22}( 2.5.4.26 NAME 'registeredAddress' DESC 'RFC2256: re
|
||||
gistered postal address' SUP postalAddress SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.41 )
|
||||
olcAttributeTypes: {23}( 2.5.4.27 NAME 'destinationIndicator' DESC 'RFC2256:
|
||||
destination indicator' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring
|
||||
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{128} )
|
||||
olcAttributeTypes: {24}( 2.5.4.28 NAME 'preferredDeliveryMethod' DESC 'RFC22
|
||||
56: preferred delivery method' SYNTAX 1.3.6.1.4.1.1466.115.121.1.14 SINGLE-
|
||||
VALUE )
|
||||
olcAttributeTypes: {25}( 2.5.4.29 NAME 'presentationAddress' DESC 'RFC2256:
|
||||
presentation address' EQUALITY presentationAddressMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.43 SINGLE-VALUE )
|
||||
olcAttributeTypes: {26}( 2.5.4.30 NAME 'supportedApplicationContext' DESC 'R
|
||||
FC2256: supported application context' EQUALITY objectIdentifierMatch SYNTA
|
||||
X 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||
olcAttributeTypes: {27}( 2.5.4.31 NAME 'member' DESC 'RFC2256: member of a g
|
||||
roup' SUP distinguishedName )
|
||||
olcAttributeTypes: {28}( 2.5.4.32 NAME 'owner' DESC 'RFC2256: owner (of the
|
||||
object)' SUP distinguishedName )
|
||||
olcAttributeTypes: {29}( 2.5.4.33 NAME 'roleOccupant' DESC 'RFC2256: occupan
|
||||
t of role' SUP distinguishedName )
|
||||
olcAttributeTypes: {30}( 2.5.4.36 NAME 'userCertificate' DESC 'RFC2256: X.50
|
||||
9 user certificate, use ;binary' EQUALITY certificateExactMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.8 )
|
||||
olcAttributeTypes: {31}( 2.5.4.37 NAME 'cACertificate' DESC 'RFC2256: X.509
|
||||
CA certificate, use ;binary' EQUALITY certificateExactMatch SYNTAX 1.3.6.1.
|
||||
4.1.1466.115.121.1.8 )
|
||||
olcAttributeTypes: {32}( 2.5.4.38 NAME 'authorityRevocationList' DESC 'RFC22
|
||||
56: X.509 authority revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.9 )
|
||||
olcAttributeTypes: {33}( 2.5.4.39 NAME 'certificateRevocationList' DESC 'RFC
|
||||
2256: X.509 certificate revocation list, use ;binary' SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.9 )
|
||||
olcAttributeTypes: {34}( 2.5.4.40 NAME 'crossCertificatePair' DESC 'RFC2256:
|
||||
X.509 cross certificate pair, use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121
|
||||
.1.10 )
|
||||
olcAttributeTypes: {35}( 2.5.4.42 NAME ( 'givenName' 'gn' ) DESC 'RFC2256: f
|
||||
irst name(s) for which the entity is known by' SUP name )
|
||||
olcAttributeTypes: {36}( 2.5.4.43 NAME 'initials' DESC 'RFC2256: initials of
|
||||
some or all of names, but not the surname(s).' SUP name )
|
||||
olcAttributeTypes: {37}( 2.5.4.44 NAME 'generationQualifier' DESC 'RFC2256:
|
||||
name qualifier indicating a generation' SUP name )
|
||||
olcAttributeTypes: {38}( 2.5.4.45 NAME 'x500UniqueIdentifier' DESC 'RFC2256:
|
||||
X.500 unique identifier' EQUALITY bitStringMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.6 )
|
||||
olcAttributeTypes: {39}( 2.5.4.46 NAME 'dnQualifier' DESC 'RFC2256: DN quali
|
||||
fier' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR case
|
||||
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
|
||||
olcAttributeTypes: {40}( 2.5.4.47 NAME 'enhancedSearchGuide' DESC 'RFC2256:
|
||||
enhanced search guide' SYNTAX 1.3.6.1.4.1.1466.115.121.1.21 )
|
||||
olcAttributeTypes: {41}( 2.5.4.48 NAME 'protocolInformation' DESC 'RFC2256:
|
||||
protocol information' EQUALITY protocolInformationMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.42 )
|
||||
olcAttributeTypes: {42}( 2.5.4.50 NAME 'uniqueMember' DESC 'RFC2256: unique
|
||||
member of a group' EQUALITY uniqueMemberMatch SYNTAX 1.3.6.1.4.1.1466.115.1
|
||||
21.1.34 )
|
||||
olcAttributeTypes: {43}( 2.5.4.51 NAME 'houseIdentifier' DESC 'RFC2256: hous
|
||||
e identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYN
|
||||
TAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
|
||||
olcAttributeTypes: {44}( 2.5.4.52 NAME 'supportedAlgorithms' DESC 'RFC2256:
|
||||
supported algorithms' SYNTAX 1.3.6.1.4.1.1466.115.121.1.49 )
|
||||
olcAttributeTypes: {45}( 2.5.4.53 NAME 'deltaRevocationList' DESC 'RFC2256:
|
||||
delta revocation list; use ;binary' SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
|
||||
olcAttributeTypes: {46}( 2.5.4.54 NAME 'dmdName' DESC 'RFC2256: name of DMD'
|
||||
SUP name )
|
||||
olcAttributeTypes: {47}( 2.5.4.65 NAME 'pseudonym' DESC 'X.520(4th): pseudon
|
||||
ym for the object' SUP name )
|
||||
olcAttributeTypes: {48}( 0.9.2342.19200300.100.1.3 NAME ( 'mail' 'rfc822Mail
|
||||
box' ) DESC 'RFC1274: RFC822 Mailbox' EQUALITY caseIgnoreIA5Match SUBST
|
||||
R caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
|
||||
)
|
||||
olcAttributeTypes: {49}( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainCompo
|
||||
nent' ) DESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match S
|
||||
UBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SIN
|
||||
GLE-VALUE )
|
||||
olcAttributeTypes: {50}( 0.9.2342.19200300.100.1.37 NAME 'associatedDomain'
|
||||
DESC 'RFC1274: domain associated with object' EQUALITY caseIgnoreIA5Match S
|
||||
UBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {51}( 1.2.840.113549.1.9.1 NAME ( 'email' 'emailAddress'
|
||||
'pkcs9email' ) DESC 'RFC3280: legacy attribute for email addresses in DNs'
|
||||
EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.26{128} )
|
||||
olcObjectClasses: {0}( 2.5.6.2 NAME 'country' DESC 'RFC2256: a country' SUP
|
||||
top STRUCTURAL MUST c MAY ( searchGuide $ description ) )
|
||||
olcObjectClasses: {1}( 2.5.6.3 NAME 'locality' DESC 'RFC2256: a locality' SU
|
||||
P top STRUCTURAL MAY ( street $ seeAlso $ searchGuide $ st $ l $ descriptio
|
||||
n ) )
|
||||
olcObjectClasses: {2}( 2.5.6.4 NAME 'organization' DESC 'RFC2256: an organiz
|
||||
ation' SUP top STRUCTURAL MUST o MAY ( userPassword $ searchGuide $ seeAlso
|
||||
$ businessCategory $ x121Address $ registeredAddress $ destinationIndicato
|
||||
r $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ tel
|
||||
ephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street
|
||||
$ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName
|
||||
$ st $ l $ description ) )
|
||||
olcObjectClasses: {3}( 2.5.6.5 NAME 'organizationalUnit' DESC 'RFC2256: an o
|
||||
rganizational unit' SUP top STRUCTURAL MUST ou MAY ( userPassword $ searchG
|
||||
uide $ seeAlso $ businessCategory $ x121Address $ registeredAddress $ desti
|
||||
nationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalId
|
||||
entifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNu
|
||||
mber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDelive
|
||||
ryOfficeName $ st $ l $ description ) )
|
||||
olcObjectClasses: {4}( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person' SUP to
|
||||
p STRUCTURAL MUST ( sn $ cn ) MAY ( userPassword $ telephoneNumber $ seeAls
|
||||
o $ description ) )
|
||||
olcObjectClasses: {5}( 2.5.6.7 NAME 'organizationalPerson' DESC 'RFC2256: an
|
||||
organizational person' SUP person STRUCTURAL MAY ( title $ x121Address $ r
|
||||
egisteredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNu
|
||||
mber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumbe
|
||||
r $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ posta
|
||||
lAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) )
|
||||
olcObjectClasses: {6}( 2.5.6.8 NAME 'organizationalRole' DESC 'RFC2256: an o
|
||||
rganizational role' SUP top STRUCTURAL MUST cn MAY ( x121Address $ register
|
||||
edAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $
|
||||
teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ fac
|
||||
simileTelephoneNumber $ seeAlso $ roleOccupant $ preferredDeliveryMethod $
|
||||
street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOffic
|
||||
eName $ ou $ st $ l $ description ) )
|
||||
olcObjectClasses: {7}( 2.5.6.9 NAME 'groupOfNames' DESC 'RFC2256: a group of
|
||||
names (DNs)' SUP top STRUCTURAL MUST ( member $ cn ) MAY ( businessCategor
|
||||
y $ seeAlso $ owner $ ou $ o $ description ) )
|
||||
olcObjectClasses: {8}( 2.5.6.10 NAME 'residentialPerson' DESC 'RFC2256: an r
|
||||
esidential person' SUP person STRUCTURAL MUST l MAY ( businessCategory $ x1
|
||||
21Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMet
|
||||
hod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internati
|
||||
onaliSDNNumber $ facsimileTelephoneNumber $ preferredDeliveryMethod $ stree
|
||||
t $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName
|
||||
$ st $ l ) )
|
||||
olcObjectClasses: {9}( 2.5.6.11 NAME 'applicationProcess' DESC 'RFC2256: an
|
||||
application process' SUP top STRUCTURAL MUST cn MAY ( seeAlso $ ou $ l $ de
|
||||
scription ) )
|
||||
olcObjectClasses: {10}( 2.5.6.12 NAME 'applicationEntity' DESC 'RFC2256: an
|
||||
application entity' SUP top STRUCTURAL MUST ( presentationAddress $ cn ) MA
|
||||
Y ( supportedApplicationContext $ seeAlso $ ou $ o $ l $ description ) )
|
||||
olcObjectClasses: {11}( 2.5.6.13 NAME 'dSA' DESC 'RFC2256: a directory syste
|
||||
m agent (a server)' SUP applicationEntity STRUCTURAL MAY knowledgeInformati
|
||||
on )
|
||||
olcObjectClasses: {12}( 2.5.6.14 NAME 'device' DESC 'RFC2256: a device' SUP
|
||||
top STRUCTURAL MUST cn MAY ( serialNumber $ seeAlso $ owner $ ou $ o $ l $
|
||||
description ) )
|
||||
olcObjectClasses: {13}( 2.5.6.15 NAME 'strongAuthenticationUser' DESC 'RFC22
|
||||
56: a strong authentication user' SUP top AUXILIARY MUST userCertificate )
|
||||
olcObjectClasses: {14}( 2.5.6.16 NAME 'certificationAuthority' DESC 'RFC2256
|
||||
: a certificate authority' SUP top AUXILIARY MUST ( authorityRevocationList
|
||||
$ certificateRevocationList $ cACertificate ) MAY crossCertificatePair )
|
||||
olcObjectClasses: {15}( 2.5.6.17 NAME 'groupOfUniqueNames' DESC 'RFC2256: a
|
||||
group of unique names (DN and Unique Identifier)' SUP top STRUCTURAL MUST (
|
||||
uniqueMember $ cn ) MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ de
|
||||
scription ) )
|
||||
olcObjectClasses: {16}( 2.5.6.18 NAME 'userSecurityInformation' DESC 'RFC225
|
||||
6: a user security information' SUP top AUXILIARY MAY ( supportedAlgorithms
|
||||
) )
|
||||
olcObjectClasses: {17}( 2.5.6.16.2 NAME 'certificationAuthority-V2' SUP cert
|
||||
ificationAuthority AUXILIARY MAY ( deltaRevocationList ) )
|
||||
olcObjectClasses: {18}( 2.5.6.19 NAME 'cRLDistributionPoint' SUP top STRUCTU
|
||||
RAL MUST ( cn ) MAY ( certificateRevocationList $ authorityRevocationList $
|
||||
deltaRevocationList ) )
|
||||
olcObjectClasses: {19}( 2.5.6.20 NAME 'dmd' SUP top STRUCTURAL MUST ( dmdNam
|
||||
e ) MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $ x121Add
|
||||
ress $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $
|
||||
telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationali
|
||||
SDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode
|
||||
$ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )
|
||||
olcObjectClasses: {20}( 2.5.6.21 NAME 'pkiUser' DESC 'RFC2587: a PKI user' S
|
||||
UP top AUXILIARY MAY userCertificate )
|
||||
olcObjectClasses: {21}( 2.5.6.22 NAME 'pkiCA' DESC 'RFC2587: PKI certificate
|
||||
authority' SUP top AUXILIARY MAY ( authorityRevocationList $ certificateRe
|
||||
vocationList $ cACertificate $ crossCertificatePair ) )
|
||||
olcObjectClasses: {22}( 2.5.6.23 NAME 'deltaCRL' DESC 'RFC2587: PKI user' SU
|
||||
P top AUXILIARY MAY deltaRevocationList )
|
||||
olcObjectClasses: {23}( 1.3.6.1.4.1.250.3.15 NAME 'labeledURIObject' DESC 'R
|
||||
FC2079: object that contains the URI attribute type' MAY ( labeledURI ) SUP
|
||||
top AUXILIARY )
|
||||
olcObjectClasses: {24}( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObjec
|
||||
t' DESC 'RFC1274: simple security object' SUP top AUXILIARY MUST userPasswo
|
||||
rd )
|
||||
olcObjectClasses: {25}( 1.3.6.1.4.1.1466.344 NAME 'dcObject' DESC 'RFC2247:
|
||||
domain component object' SUP top AUXILIARY MUST dc )
|
||||
olcObjectClasses: {26}( 1.3.6.1.1.3.1 NAME 'uidObject' DESC 'RFC2377: uid ob
|
||||
ject' SUP top AUXILIARY MUST uid )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 40c42136-16b1-1040-911f-a716cb68c9fb
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
entryCSN: 20250826101453.609974Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20250826101453Z
|
||||
178
ldap-config/cn=config/cn=schema/cn={1}cosine.ldif
Normal file
178
ldap-config/cn=config/cn=schema/cn={1}cosine.ldif
Normal file
@@ -0,0 +1,178 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 c41ea9d7
|
||||
dn: cn={1}cosine
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {1}cosine
|
||||
olcAttributeTypes: {0}( 0.9.2342.19200300.100.1.2 NAME 'textEncodedORAddress
|
||||
' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.
|
||||
4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {1}( 0.9.2342.19200300.100.1.4 NAME 'info' DESC 'RFC1274:
|
||||
general information' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{2048} )
|
||||
olcAttributeTypes: {2}( 0.9.2342.19200300.100.1.5 NAME ( 'drink' 'favouriteD
|
||||
rink' ) DESC 'RFC1274: favorite drink' EQUALITY caseIgnoreMatch SUBSTR case
|
||||
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {3}( 0.9.2342.19200300.100.1.6 NAME 'roomNumber' DESC 'RF
|
||||
C1274: room number' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {4}( 0.9.2342.19200300.100.1.7 NAME 'photo' DESC 'RFC1274
|
||||
: photo (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.23{25000} )
|
||||
olcAttributeTypes: {5}( 0.9.2342.19200300.100.1.8 NAME 'userClass' DESC 'RFC
|
||||
1274: category of user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring
|
||||
sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {6}( 0.9.2342.19200300.100.1.9 NAME 'host' DESC 'RFC1274:
|
||||
host computer' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch S
|
||||
YNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {7}( 0.9.2342.19200300.100.1.10 NAME 'manager' DESC 'RFC1
|
||||
274: DN of manager' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466
|
||||
.115.121.1.12 )
|
||||
olcAttributeTypes: {8}( 0.9.2342.19200300.100.1.11 NAME 'documentIdentifier'
|
||||
DESC 'RFC1274: unique identifier of document' EQUALITY caseIgnoreMatch SUB
|
||||
STR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {9}( 0.9.2342.19200300.100.1.12 NAME 'documentTitle' DESC
|
||||
'RFC1274: title of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSub
|
||||
stringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {10}( 0.9.2342.19200300.100.1.13 NAME 'documentVersion' D
|
||||
ESC 'RFC1274: version of document' EQUALITY caseIgnoreMatch SUBSTR caseIgno
|
||||
reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {11}( 0.9.2342.19200300.100.1.14 NAME 'documentAuthor' DE
|
||||
SC 'RFC1274: DN of author of document' EQUALITY distinguishedNameMatch SYNT
|
||||
AX 1.3.6.1.4.1.1466.115.121.1.12 )
|
||||
olcAttributeTypes: {12}( 0.9.2342.19200300.100.1.15 NAME 'documentLocation'
|
||||
DESC 'RFC1274: location of document original' EQUALITY caseIgnoreMatch SUBS
|
||||
TR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {13}( 0.9.2342.19200300.100.1.20 NAME ( 'homePhone' 'home
|
||||
TelephoneNumber' ) DESC 'RFC1274: home telephone number' EQUALITY telephone
|
||||
NumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.50 )
|
||||
olcAttributeTypes: {14}( 0.9.2342.19200300.100.1.21 NAME 'secretary' DESC 'R
|
||||
FC1274: DN of secretary' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1
|
||||
.1466.115.121.1.12 )
|
||||
olcAttributeTypes: {15}( 0.9.2342.19200300.100.1.22 NAME 'otherMailbox' SYNT
|
||||
AX 1.3.6.1.4.1.1466.115.121.1.39 )
|
||||
olcAttributeTypes: {16}( 0.9.2342.19200300.100.1.26 NAME 'aRecord' EQUALITY
|
||||
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {17}( 0.9.2342.19200300.100.1.27 NAME 'mDRecord' EQUALITY
|
||||
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {18}( 0.9.2342.19200300.100.1.28 NAME 'mXRecord' EQUALITY
|
||||
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {19}( 0.9.2342.19200300.100.1.29 NAME 'nSRecord' EQUALITY
|
||||
caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {20}( 0.9.2342.19200300.100.1.30 NAME 'sOARecord' EQUALIT
|
||||
Y caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {21}( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord' EQUAL
|
||||
ITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {22}( 0.9.2342.19200300.100.1.38 NAME 'associatedName' DE
|
||||
SC 'RFC1274: DN of entry associated with domain' EQUALITY distinguishedName
|
||||
Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
|
||||
olcAttributeTypes: {23}( 0.9.2342.19200300.100.1.39 NAME 'homePostalAddress'
|
||||
DESC 'RFC1274: home postal address' EQUALITY caseIgnoreListMatch SUBSTR ca
|
||||
seIgnoreListSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
|
||||
olcAttributeTypes: {24}( 0.9.2342.19200300.100.1.40 NAME 'personalTitle' DES
|
||||
C 'RFC1274: personal title' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubst
|
||||
ringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {25}( 0.9.2342.19200300.100.1.41 NAME ( 'mobile' 'mobileT
|
||||
elephoneNumber' ) DESC 'RFC1274: mobile telephone number' EQUALITY telephon
|
||||
eNumberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.
|
||||
115.121.1.50 )
|
||||
olcAttributeTypes: {26}( 0.9.2342.19200300.100.1.42 NAME ( 'pager' 'pagerTel
|
||||
ephoneNumber' ) DESC 'RFC1274: pager telephone number' EQUALITY telephoneNu
|
||||
mberMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115
|
||||
.121.1.50 )
|
||||
olcAttributeTypes: {27}( 0.9.2342.19200300.100.1.43 NAME ( 'co' 'friendlyCou
|
||||
ntryName' ) DESC 'RFC1274: friendly country name' EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {28}( 0.9.2342.19200300.100.1.44 NAME 'uniqueIdentifier'
|
||||
DESC 'RFC1274: unique identifer' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {29}( 0.9.2342.19200300.100.1.45 NAME 'organizationalStat
|
||||
us' DESC 'RFC1274: organizational status' EQUALITY caseIgnoreMatch SUBSTR c
|
||||
aseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {30}( 0.9.2342.19200300.100.1.46 NAME 'janetMailbox' DESC
|
||||
'RFC1274: Janet mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5S
|
||||
ubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
|
||||
olcAttributeTypes: {31}( 0.9.2342.19200300.100.1.47 NAME 'mailPreferenceOpti
|
||||
on' DESC 'RFC1274: mail preference option' SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.27 )
|
||||
olcAttributeTypes: {32}( 0.9.2342.19200300.100.1.48 NAME 'buildingName' DESC
|
||||
'RFC1274: name of building' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubs
|
||||
tringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {33}( 0.9.2342.19200300.100.1.49 NAME 'dSAQuality' DESC '
|
||||
RFC1274: DSA Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.19 SINGLE-VALUE )
|
||||
olcAttributeTypes: {34}( 0.9.2342.19200300.100.1.50 NAME 'singleLevelQuality
|
||||
' DESC 'RFC1274: Single Level Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.13
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {35}( 0.9.2342.19200300.100.1.51 NAME 'subtreeMinimumQual
|
||||
ity' DESC 'RFC1274: Subtree Mininum Quality' SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.13 SINGLE-VALUE )
|
||||
olcAttributeTypes: {36}( 0.9.2342.19200300.100.1.52 NAME 'subtreeMaximumQual
|
||||
ity' DESC 'RFC1274: Subtree Maximun Quality' SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.13 SINGLE-VALUE )
|
||||
olcAttributeTypes: {37}( 0.9.2342.19200300.100.1.53 NAME 'personalSignature'
|
||||
DESC 'RFC1274: Personal Signature (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.23 )
|
||||
olcAttributeTypes: {38}( 0.9.2342.19200300.100.1.54 NAME 'dITRedirect' DESC
|
||||
'RFC1274: DIT Redirect' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.12 )
|
||||
olcAttributeTypes: {39}( 0.9.2342.19200300.100.1.55 NAME 'audio' DESC 'RFC12
|
||||
74: audio (u-law)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.4{25000} )
|
||||
olcAttributeTypes: {40}( 0.9.2342.19200300.100.1.56 NAME 'documentPublisher'
|
||||
DESC 'RFC1274: publisher of document' EQUALITY caseIgnoreMatch SUBSTR case
|
||||
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcObjectClasses: {0}( 0.9.2342.19200300.100.4.4 NAME ( 'pilotPerson' 'newPi
|
||||
lotPerson' ) SUP person STRUCTURAL MAY ( userid $ textEncodedORAddress $ rf
|
||||
c822Mailbox $ favouriteDrink $ roomNumber $ userClass $ homeTelephoneNumber
|
||||
$ homePostalAddress $ secretary $ personalTitle $ preferredDeliveryMethod
|
||||
$ businessCategory $ janetMailbox $ otherMailbox $ mobileTelephoneNumber $
|
||||
pagerTelephoneNumber $ organizationalStatus $ mailPreferenceOption $ person
|
||||
alSignature ) )
|
||||
olcObjectClasses: {1}( 0.9.2342.19200300.100.4.5 NAME 'account' SUP top STRU
|
||||
CTURAL MUST userid MAY ( description $ seeAlso $ localityName $ organizatio
|
||||
nName $ organizationalUnitName $ host ) )
|
||||
olcObjectClasses: {2}( 0.9.2342.19200300.100.4.6 NAME 'document' SUP top STR
|
||||
UCTURAL MUST documentIdentifier MAY ( commonName $ description $ seeAlso $
|
||||
localityName $ organizationName $ organizationalUnitName $ documentTitle $
|
||||
documentVersion $ documentAuthor $ documentLocation $ documentPublisher ) )
|
||||
olcObjectClasses: {3}( 0.9.2342.19200300.100.4.7 NAME 'room' SUP top STRUCTU
|
||||
RAL MUST commonName MAY ( roomNumber $ description $ seeAlso $ telephoneNum
|
||||
ber ) )
|
||||
olcObjectClasses: {4}( 0.9.2342.19200300.100.4.9 NAME 'documentSeries' SUP t
|
||||
op STRUCTURAL MUST commonName MAY ( description $ seeAlso $ telephonenumber
|
||||
$ localityName $ organizationName $ organizationalUnitName ) )
|
||||
olcObjectClasses: {5}( 0.9.2342.19200300.100.4.13 NAME 'domain' SUP top STRU
|
||||
CTURAL MUST domainComponent MAY ( associatedName $ organizationName $ descr
|
||||
iption $ businessCategory $ seeAlso $ searchGuide $ userPassword $ locality
|
||||
Name $ stateOrProvinceName $ streetAddress $ physicalDeliveryOfficeName $ p
|
||||
ostalAddress $ postalCode $ postOfficeBox $ streetAddress $ facsimileTeleph
|
||||
oneNumber $ internationalISDNNumber $ telephoneNumber $ teletexTerminalIden
|
||||
tifier $ telexNumber $ preferredDeliveryMethod $ destinationIndicator $ reg
|
||||
isteredAddress $ x121Address ) )
|
||||
olcObjectClasses: {6}( 0.9.2342.19200300.100.4.14 NAME 'RFC822localPart' SUP
|
||||
domain STRUCTURAL MAY ( commonName $ surname $ description $ seeAlso $ tel
|
||||
ephoneNumber $ physicalDeliveryOfficeName $ postalAddress $ postalCode $ po
|
||||
stOfficeBox $ streetAddress $ facsimileTelephoneNumber $ internationalISDNN
|
||||
umber $ telephoneNumber $ teletexTerminalIdentifier $ telexNumber $ preferr
|
||||
edDeliveryMethod $ destinationIndicator $ registeredAddress $ x121Address )
|
||||
)
|
||||
olcObjectClasses: {7}( 0.9.2342.19200300.100.4.15 NAME 'dNSDomain' SUP domai
|
||||
n STRUCTURAL MAY ( ARecord $ MDRecord $ MXRecord $ NSRecord $ SOARecord $ C
|
||||
NAMERecord ) )
|
||||
olcObjectClasses: {8}( 0.9.2342.19200300.100.4.17 NAME 'domainRelatedObject'
|
||||
DESC 'RFC1274: an object related to an domain' SUP top AUXILIARY MUST asso
|
||||
ciatedDomain )
|
||||
olcObjectClasses: {9}( 0.9.2342.19200300.100.4.18 NAME 'friendlyCountry' SUP
|
||||
country STRUCTURAL MUST friendlyCountryName )
|
||||
olcObjectClasses: {10}( 0.9.2342.19200300.100.4.20 NAME 'pilotOrganization'
|
||||
SUP ( organization $ organizationalUnit ) STRUCTURAL MAY buildingName )
|
||||
olcObjectClasses: {11}( 0.9.2342.19200300.100.4.21 NAME 'pilotDSA' SUP dsa S
|
||||
TRUCTURAL MAY dSAQuality )
|
||||
olcObjectClasses: {12}( 0.9.2342.19200300.100.4.22 NAME 'qualityLabelledData
|
||||
' SUP top AUXILIARY MUST dsaQuality MAY ( subtreeMinimumQuality $ subtreeMa
|
||||
ximumQuality ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 40c836c2-16b1-1040-9120-a716cb68c9fb
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
entryCSN: 20250826101453.636741Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20250826101453Z
|
||||
108
ldap-config/cn=config/cn=schema/cn={2}nis.ldif
Normal file
108
ldap-config/cn=config/cn=schema/cn={2}nis.ldif
Normal file
@@ -0,0 +1,108 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 3b70abfe
|
||||
dn: cn={2}nis
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {2}nis
|
||||
olcAttributeTypes: {0}( 1.3.6.1.1.1.1.2 NAME 'gecos' DESC 'The GECOS field;
|
||||
the common name' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Substrings
|
||||
Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.1.1.1.3 NAME 'homeDirectory' DESC 'The absol
|
||||
ute path to the home directory' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.1.1.1.4 NAME 'loginShell' DESC 'The path to
|
||||
the login shell' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121
|
||||
.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange' EQUALITY int
|
||||
egerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.1.1.1.6 NAME 'shadowMin' EQUALITY integerMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.1.1.1.7 NAME 'shadowMax' EQUALITY integerMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.1.1.1.8 NAME 'shadowWarning' EQUALITY intege
|
||||
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.1.1.1.9 NAME 'shadowInactive' EQUALITY integ
|
||||
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.1.1.1.10 NAME 'shadowExpire' EQUALITY intege
|
||||
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.1.1.1.11 NAME 'shadowFlag' EQUALITY integerM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {10}( 1.3.6.1.1.1.1.12 NAME 'memberUid' EQUALITY caseExac
|
||||
tIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.26 )
|
||||
olcAttributeTypes: {11}( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup' EQUALITY
|
||||
caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.146
|
||||
6.115.121.1.26 )
|
||||
olcAttributeTypes: {12}( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple' DESC 'Net
|
||||
group triple' SYNTAX 1.3.6.1.1.1.0.0 )
|
||||
olcAttributeTypes: {13}( 1.3.6.1.1.1.1.15 NAME 'ipServicePort' EQUALITY inte
|
||||
gerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {14}( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol' SUP name
|
||||
)
|
||||
olcAttributeTypes: {15}( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber' EQUALITY i
|
||||
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {16}( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber' EQUALITY integ
|
||||
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {17}( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber' DESC 'IP addre
|
||||
ss' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||
olcAttributeTypes: {18}( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber' DESC 'IP ne
|
||||
twork' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128
|
||||
} SINGLE-VALUE )
|
||||
olcAttributeTypes: {19}( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber' DESC 'IP ne
|
||||
tmask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128
|
||||
} SINGLE-VALUE )
|
||||
olcAttributeTypes: {20}( 1.3.6.1.1.1.1.22 NAME 'macAddress' DESC 'MAC addres
|
||||
s' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||
olcAttributeTypes: {21}( 1.3.6.1.1.1.1.23 NAME 'bootParameter' DESC 'rpc.boo
|
||||
tparamd parameter' SYNTAX 1.3.6.1.1.1.0.1 )
|
||||
olcAttributeTypes: {22}( 1.3.6.1.1.1.1.24 NAME 'bootFile' DESC 'Boot image n
|
||||
ame' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {23}( 1.3.6.1.1.1.1.26 NAME 'nisMapName' SUP name )
|
||||
olcAttributeTypes: {24}( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry' EQUALITY caseEx
|
||||
actIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.26{1024} SINGLE-VALUE )
|
||||
olcObjectClasses: {0}( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction
|
||||
of an account with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ u
|
||||
idNumber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ ge
|
||||
cos $ description ) )
|
||||
olcObjectClasses: {1}( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' DESC 'Additional
|
||||
attributes for shadow passwords' SUP top AUXILIARY MUST uid MAY ( userPass
|
||||
word $ shadowLastChange $ shadowMin $ shadowMax $ shadowWarning $ shadowIna
|
||||
ctive $ shadowExpire $ shadowFlag $ description ) )
|
||||
olcObjectClasses: {2}( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction o
|
||||
f a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( use
|
||||
rPassword $ memberUid $ description ) )
|
||||
olcObjectClasses: {3}( 1.3.6.1.1.1.2.3 NAME 'ipService' DESC 'Abstraction an
|
||||
Internet Protocol service' SUP top STRUCTURAL MUST ( cn $ ipServicePort $
|
||||
ipServiceProtocol ) MAY description )
|
||||
olcObjectClasses: {4}( 1.3.6.1.1.1.2.4 NAME 'ipProtocol' DESC 'Abstraction o
|
||||
f an IP protocol' SUP top STRUCTURAL MUST ( cn $ ipProtocolNumber $ descrip
|
||||
tion ) MAY description )
|
||||
olcObjectClasses: {5}( 1.3.6.1.1.1.2.5 NAME 'oncRpc' DESC 'Abstraction of an
|
||||
ONC/RPC binding' SUP top STRUCTURAL MUST ( cn $ oncRpcNumber $ description
|
||||
) MAY description )
|
||||
olcObjectClasses: {6}( 1.3.6.1.1.1.2.6 NAME 'ipHost' DESC 'Abstraction of a
|
||||
host, an IP device' SUP top AUXILIARY MUST ( cn $ ipHostNumber ) MAY ( l $
|
||||
description $ manager ) )
|
||||
olcObjectClasses: {7}( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' DESC 'Abstraction of
|
||||
an IP network' SUP top STRUCTURAL MUST ( cn $ ipNetworkNumber ) MAY ( ipNe
|
||||
tmaskNumber $ l $ description $ manager ) )
|
||||
olcObjectClasses: {8}( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup' DESC 'Abstraction
|
||||
of a netgroup' SUP top STRUCTURAL MUST cn MAY ( nisNetgroupTriple $ memberN
|
||||
isNetgroup $ description ) )
|
||||
olcObjectClasses: {9}( 1.3.6.1.1.1.2.9 NAME 'nisMap' DESC 'A generic abstrac
|
||||
tion of a NIS map' SUP top STRUCTURAL MUST nisMapName MAY description )
|
||||
olcObjectClasses: {10}( 1.3.6.1.1.1.2.10 NAME 'nisObject' DESC 'An entry in
|
||||
a NIS map' SUP top STRUCTURAL MUST ( cn $ nisMapEntry $ nisMapName ) MAY de
|
||||
scription )
|
||||
olcObjectClasses: {11}( 1.3.6.1.1.1.2.11 NAME 'ieee802Device' DESC 'A device
|
||||
with a MAC address' SUP top AUXILIARY MAY macAddress )
|
||||
olcObjectClasses: {12}( 1.3.6.1.1.1.2.12 NAME 'bootableDevice' DESC 'A devic
|
||||
e with boot parameters' SUP top AUXILIARY MAY ( bootFile $ bootParameter )
|
||||
)
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 40cc26a6-16b1-1040-9121-a716cb68c9fb
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
entryCSN: 20250826101453.662538Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20250826101453Z
|
||||
49
ldap-config/cn=config/cn=schema/cn={3}inetorgperson.ldif
Normal file
49
ldap-config/cn=config/cn=schema/cn={3}inetorgperson.ldif
Normal file
@@ -0,0 +1,49 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 de7f4198
|
||||
dn: cn={3}inetorgperson
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {3}inetorgperson
|
||||
olcAttributeTypes: {0}( 2.16.840.1.113730.3.1.1 NAME 'carLicense' DESC 'RFC2
|
||||
798: vehicle license or registration plate' EQUALITY caseIgnoreMatch SUBSTR
|
||||
caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {1}( 2.16.840.1.113730.3.1.2 NAME 'departmentNumber' DESC
|
||||
'RFC2798: identifies a department within an organization' EQUALITY caseIgn
|
||||
oreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
|
||||
.15 )
|
||||
olcAttributeTypes: {2}( 2.16.840.1.113730.3.1.241 NAME 'displayName' DESC 'R
|
||||
FC2798: preferred name to be used when displaying entries' EQUALITY caseIgn
|
||||
oreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
|
||||
.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 2.16.840.1.113730.3.1.3 NAME 'employeeNumber' DESC '
|
||||
RFC2798: numerically identifies an employee within an organization' EQUALIT
|
||||
Y caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.
|
||||
115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 2.16.840.1.113730.3.1.4 NAME 'employeeType' DESC 'RF
|
||||
C2798: type of employment for a person' EQUALITY caseIgnoreMatch SUBSTR cas
|
||||
eIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {5}( 0.9.2342.19200300.100.1.60 NAME 'jpegPhoto' DESC 'RF
|
||||
C2798: a JPEG image' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )
|
||||
olcAttributeTypes: {6}( 2.16.840.1.113730.3.1.39 NAME 'preferredLanguage' DE
|
||||
SC 'RFC2798: preferred written or spoken language for a person' EQUALITY ca
|
||||
seIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 2.16.840.1.113730.3.1.40 NAME 'userSMIMECertificate'
|
||||
DESC 'RFC2798: PKCS#7 SignedData used to support S/MIME' SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.5 )
|
||||
olcAttributeTypes: {8}( 2.16.840.1.113730.3.1.216 NAME 'userPKCS12' DESC 'RF
|
||||
C2798: personal identity information, a PKCS #12 PFX' SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.5 )
|
||||
olcObjectClasses: {0}( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' DESC 'RF
|
||||
C2798: Internet Organizational Person' SUP organizationalPerson STRUCTURAL
|
||||
MAY ( audio $ businessCategory $ carLicense $ departmentNumber $ displayNam
|
||||
e $ employeeNumber $ employeeType $ givenName $ homePhone $ homePostalAddre
|
||||
ss $ initials $ jpegPhoto $ labeledURI $ mail $ manager $ mobile $ o $ page
|
||||
r $ photo $ roomNumber $ secretary $ uid $ userCertificate $ x500uniqueIden
|
||||
tifier $ preferredLanguage $ userSMIMECertificate $ userPKCS12 ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 40d0893a-16b1-1040-9122-a716cb68c9fb
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
entryCSN: 20250826101453.691278Z#000000#000#000000
|
||||
modifiersName: cn=admin,cn=config
|
||||
modifyTimestamp: 20250826101453Z
|
||||
65
ldap-config/cn=config/cn=schema/cn={4}ppolicy.ldif
Normal file
65
ldap-config/cn=config/cn=schema/cn={4}ppolicy.ldif
Normal file
@@ -0,0 +1,65 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 078a540a
|
||||
dn: cn={4}ppolicy
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {4}ppolicy
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.8.1.1 NAME 'pwdAttribute' EQUALI
|
||||
TY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.8.1.2 NAME 'pwdMinAge' EQUALITY
|
||||
integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.4.1.42.2.27.8.1.3 NAME 'pwdMaxAge' EQUALITY
|
||||
integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.8.1.4 NAME 'pwdInHistory' EQUALI
|
||||
TY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.1
|
||||
21.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.4.1.42.2.27.8.1.5 NAME 'pwdCheckQuality' EQU
|
||||
ALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.11
|
||||
5.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.4.1.42.2.27.8.1.6 NAME 'pwdMinLength' EQUALI
|
||||
TY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.4.1.42.2.27.8.1.7 NAME 'pwdExpireWarning' EQ
|
||||
UALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.
|
||||
115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.4.1.42.2.27.8.1.8 NAME 'pwdGraceAuthNLimit'
|
||||
EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.146
|
||||
6.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.4.1.42.2.27.8.1.9 NAME 'pwdLockout' EQUALITY
|
||||
booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.4.1.42.2.27.8.1.10 NAME 'pwdLockoutDuration'
|
||||
EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {10}( 1.3.6.1.4.1.42.2.27.8.1.11 NAME 'pwdMaxFailure' EQU
|
||||
ALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {11}( 1.3.6.1.4.1.42.2.27.8.1.12 NAME 'pwdFailureCountInt
|
||||
erval' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.
|
||||
4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {12}( 1.3.6.1.4.1.42.2.27.8.1.13 NAME 'pwdMustChange' EQU
|
||||
ALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {13}( 1.3.6.1.4.1.42.2.27.8.1.14 NAME 'pwdAllowUserChange
|
||||
' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {14}( 1.3.6.1.4.1.42.2.27.8.1.15 NAME 'pwdSafeModify' EQU
|
||||
ALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {15}( 1.3.6.1.4.1.4754.1.99.1 NAME 'pwdCheckModule' DESC
|
||||
'Loadable module that instantiates "check_password() function' EQUALITY cas
|
||||
eExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {16}( 1.3.6.1.4.1.42.2.27.8.1.30 NAME 'pwdMaxRecordedFail
|
||||
ure' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.4754.2.99.1 NAME 'pwdPolicyChecker' SUP t
|
||||
op AUXILIARY MAY pwdCheckModule )
|
||||
olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.8.2.1 NAME 'pwdPolicy' SUP top AU
|
||||
XILIARY MUST pwdAttribute MAY ( pwdMinAge $ pwdMaxAge $ pwdInHistory $ pwdC
|
||||
heckQuality $ pwdMinLength $ pwdExpireWarning $ pwdGraceAuthNLimit $ pwdLoc
|
||||
kout $ pwdLockoutDuration $ pwdMaxFailure $ pwdFailureCountInterval $ pwdMu
|
||||
stChange $ pwdAllowUserChange $ pwdSafeModify $ pwdMaxRecordedFailure ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 411f1b86-16b1-1040-995d-2797d7152165
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20250826101454Z
|
||||
entryCSN: 20250826101454.206194Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250826101454Z
|
||||
175
ldap-config/cn=config/cn=schema/cn={5}kopano.ldif
Normal file
175
ldap-config/cn=config/cn=schema/cn={5}kopano.ldif
Normal file
@@ -0,0 +1,175 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 0214d98f
|
||||
dn: cn={5}kopano
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {5}kopano
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.47732.1.1.1.1 NAME 'kopanoQuotaOverride'
|
||||
DESC 'KOPANO: Override child quota' EQUALITY integerMatch SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.4.1.47732.1.1.1.2 NAME 'kopanoQuotaWarn' DES
|
||||
C 'KOPANO: Warning quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.4.1.47732.1.1.1.3 NAME 'kopanoQuotaSoft' DES
|
||||
C 'KOPANO: Soft quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.4.1.47732.1.1.1.4 NAME 'kopanoQuotaHard' DES
|
||||
C 'KOPANO: Hard quota size in MB' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.4.1.47732.1.1.1.5 NAME 'kopanoUserDefaultQuo
|
||||
taOverride' DESC 'KOPANO: Override User default quota for children' EQUALIT
|
||||
Y integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.4.1.47732.1.1.1.6 NAME 'kopanoUserDefaultQuo
|
||||
taWarn' DESC 'KOPANO: User default warning quota size in MB' EQUALITY integ
|
||||
erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.4.1.47732.1.1.1.7 NAME 'kopanoUserDefaultQuo
|
||||
taSoft' DESC 'KOPANO: User default soft quota size in MB' EQUALITY integerM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.4.1.47732.1.1.1.8 NAME 'kopanoUserDefaultQuo
|
||||
taHard' DESC 'KOPANO: User default hard quota size in MB' EQUALITY integerM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.4.1.47732.1.1.2.1 NAME 'kopanoAdmin' DESC 'K
|
||||
OPANO: Administrator of kopano' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.4.1.47732.1.1.2.2 NAME 'kopanoSharedStoreOnl
|
||||
y' DESC 'KOPANO: is store a shared store' EQUALITY integerMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {10}( 1.3.6.1.4.1.47732.1.1.2.3 NAME 'kopanoAccount' DESC
|
||||
'KOPANO: entry is a part of kopano' EQUALITY integerMatch SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {11}( 1.3.6.1.4.1.47732.1.1.2.4 NAME 'kopanoSendAsPrivile
|
||||
ge' DESC 'KOPANO: Users may directly send email as this user' EQUALITY case
|
||||
IgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.15 )
|
||||
olcAttributeTypes: {12}( 1.3.6.1.4.1.47732.1.1.2.5 NAME 'kopanoMrAccept' DES
|
||||
C 'KOPANO: user should auto-accept meeting requests' EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {13}( 1.3.6.1.4.1.47732.1.1.2.6 NAME 'kopanoMrDeclineConf
|
||||
lict' DESC 'KOPANO: user should automatically decline conflicting meeting r
|
||||
equests' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-
|
||||
VALUE )
|
||||
olcAttributeTypes: {14}( 1.3.6.1.4.1.47732.1.1.2.7 NAME 'kopanoMrDeclineRecu
|
||||
rring' DESC 'KOPANO: user should automatically decline recurring meeting re
|
||||
quests' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-V
|
||||
ALUE )
|
||||
olcAttributeTypes: {15}( 1.3.6.1.4.1.47732.1.1.2.8 NAME 'kopanoId' DESC 'KOP
|
||||
ANO: Generic unique ID' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.40 SINGLE-VALUE )
|
||||
olcAttributeTypes: {16}( 1.3.6.1.4.1.47732.1.1.2.9 NAME 'kopanoResourceType'
|
||||
DESC 'KOPANO: for shared stores, resource is type Room or Equipment' EQUAL
|
||||
ITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.146
|
||||
6.115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {17}( 1.3.6.1.4.1.47732.1.1.2.10 NAME 'kopanoResourceCapa
|
||||
city' DESC 'KOPANO: number of rooms or equipment available' EQUALITY intege
|
||||
rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {18}( 1.3.6.1.4.1.47732.1.1.2.11 NAME 'kopanoHidden' DESC
|
||||
'KOPANO: This object should be hidden from address book' EQUALITY integerM
|
||||
atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {19}( 1.3.6.1.4.1.47732.1.1.2.13 NAME 'kopanoEnabledFeatu
|
||||
res' DESC 'KOPANO: This user has these features explicitly enabled' EQUALIT
|
||||
Y caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
|
||||
olcAttributeTypes: {20}( 1.3.6.1.4.1.47732.1.1.2.14 NAME 'kopanoDisabledFeat
|
||||
ures' DESC 'KOPANO: This user has these features explicitly disabled' EQUAL
|
||||
ITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
|
||||
olcAttributeTypes: {21}( 1.3.6.1.4.1.47732.1.1.3.1 NAME 'kopanoAliases' DESC
|
||||
'KOPANO: All other email addresses for this user' EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {22}( 1.3.6.1.4.1.47732.1.1.4.1 NAME 'kopanoUserServer' D
|
||||
ESC 'KOPANO: Home server for the user' EQUALITY caseIgnoreMatch SUBSTR case
|
||||
IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {23}( 1.3.6.1.4.1.47732.1.1.6.1 NAME 'kopanoUserArchiveSe
|
||||
rvers' DESC 'KOPANO: List of server names that contain an archive store for
|
||||
the user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX
|
||||
1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {24}( 1.3.6.1.4.1.47732.1.1.6.2 NAME 'kopanoUserArchiveCo
|
||||
uplings' DESC 'KOPANO: List of username:foldername pairs that specify many-
|
||||
to-one archive locations' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstri
|
||||
ngsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {25}( 1.3.6.1.4.1.47732.1.2.2.1 NAME 'kopanoSecurityGroup
|
||||
' DESC 'KOPANO: group has security possibilities' EQUALITY integerMatch SYN
|
||||
TAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {26}( 1.3.6.1.4.1.47732.1.3.2.4 NAME 'kopanoViewPrivilege
|
||||
' DESC 'KOPANO: Companies with view privileges over selected company' EQUAL
|
||||
ITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.146
|
||||
6.115.121.1.15 )
|
||||
olcAttributeTypes: {27}( 1.3.6.1.4.1.47732.1.3.2.5 NAME 'kopanoAdminPrivileg
|
||||
e' DESC 'KOPANO: Users from different companies which are administrator ove
|
||||
r selected company' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {28}( 1.3.6.1.4.1.47732.1.3.2.6 NAME 'kopanoSystemAdmin'
|
||||
DESC 'KOPANO: The user who is the system administrator for this company' EQ
|
||||
UALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.
|
||||
1466.115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {29}( 1.3.6.1.4.1.47732.1.3.1.5 NAME 'kopanoQuotaUserWarn
|
||||
ingRecipients' DESC 'KOPANO: Users who will receive a notification email wh
|
||||
en a user exceeds his quota' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubs
|
||||
tringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {30}( 1.3.6.1.4.1.47732.1.3.1.6 NAME 'kopanoQuotaCompanyW
|
||||
arningRecipients' DESC 'KOPANO: Users who will receive a notification email
|
||||
when a company exceeds its quota' EQUALITY caseIgnoreMatch SUBSTR caseIgno
|
||||
reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
olcAttributeTypes: {31}( 1.3.6.1.4.1.47732.1.3.4.1 NAME 'kopanoCompanyServer
|
||||
' DESC 'KOPANO: Home server for the public folders for a company' EQUALITY
|
||||
caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.11
|
||||
5.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {32}( 1.3.6.1.4.1.47732.1.4.4.1 NAME 'kopanoHttpPort' DES
|
||||
C 'KOPANO: Port for the http connection' EQUALITY integerMatch SYNTAX 1.3.6
|
||||
.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {33}( 1.3.6.1.4.1.47732.1.4.4.2 NAME 'kopanoSslPort' DESC
|
||||
'KOPANO: Port for the ssl connection' EQUALITY integerMatch SYNTAX 1.3.6.1
|
||||
.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {34}( 1.3.6.1.4.1.47732.1.4.4.3 NAME 'kopanoFilePath' DES
|
||||
C 'KOPANO: The Unix socket or named pipe to the server' EQUALITY caseIgnore
|
||||
Match SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {35}( 1.3.6.1.4.1.47732.1.4.4.4 NAME 'kopanoContainsPubli
|
||||
c' DESC 'KOPANO: This server contains the public store' EQUALITY integerMat
|
||||
ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {36}( 1.3.6.1.4.1.47732.1.4.4.6 NAME 'kopanoProxyURL' DES
|
||||
C 'KOPANO: Full proxy URL for this server' EQUALITY caseIgnoreMatch SUBSTR
|
||||
caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
|
||||
)
|
||||
olcAttributeTypes: {37}( 1.3.6.1.4.1.47732.1.5.5.1 NAME 'kopanoFilter' DESC
|
||||
'KOPANO: LDAP Filter to apply' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSu
|
||||
bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
olcAttributeTypes: {38}( 1.3.6.1.4.1.47732.1.5.5.2 NAME 'kopanoBase' DESC 'K
|
||||
OPANO: LDAP Search base to apply' EQUALITY caseIgnoreMatch SUBSTR caseIgnor
|
||||
eSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.47732.1.1.0.0 NAME 'kopano-user' DESC 'KO
|
||||
PANO: an user of Kopano' SUP top AUXILIARY MUST cn MAY ( kopanoQuotaOverrid
|
||||
e $ kopanoQuotaWarn $ kopanoQuotaSoft $ kopanoSendAsPrivilege $ kopanoQuota
|
||||
Hard $ kopanoAdmin $ kopanoSharedStoreOnly $ kopanoResourceType $ kopanoRes
|
||||
ourceCapacity $ kopanoAccount $ kopanoHidden $ kopanoAliases $ kopanoUserSe
|
||||
rver $ kopanoEnabledFeatures $ kopanoDisabledFeatures $ kopanoUserArchiveSe
|
||||
rvers $ kopanoUserArchiveCouplings $ uidNumber ) )
|
||||
olcObjectClasses: {1}( 1.3.6.1.4.1.47732.1.6.0.0 NAME 'kopano-contact' DESC
|
||||
'KOPANO: a contact of Kopano' SUP top AUXILIARY MUST ( cn $ uidNumber ) MAY
|
||||
( kopanoSendAsPrivilege $ kopanoHidden $ kopanoAliases $ kopanoAccount ) )
|
||||
olcObjectClasses: {2}( 1.3.6.1.4.1.47732.1.2.0.0 NAME 'kopano-group' DESC 'K
|
||||
OPANO: a group of Kopano' SUP top AUXILIARY MUST cn MAY ( kopanoAccount $ k
|
||||
opanoHidden $ mail $ kopanoAliases $ kopanoSecurityGroup $ kopanoSendAsPriv
|
||||
ilege $ gidNumber ) )
|
||||
olcObjectClasses: {3}( 1.3.6.1.4.1.47732.1.3.0.0 NAME 'kopano-company' DESC
|
||||
'KOPANO: a company of Kopano' SUP top AUXILIARY MUST ou MAY ( kopanoAccount
|
||||
$ kopanoHidden $ kopanoViewPrivilege $ kopanoAdminPrivilege $ kopanoSystem
|
||||
Admin $ kopanoQuotaOverride $ kopanoQuotaWarn $ kopanoUserDefaultQuotaOverr
|
||||
ide $ kopanoUserDefaultQuotaWarn $ kopanoUserDefaultQuotaSoft $ kopanoUserD
|
||||
efaultQuotaHard $ kopanoQuotaUserWarningRecipients $ kopanoQuotaCompanyWarn
|
||||
ingRecipients $ kopanoCompanyServer ) )
|
||||
olcObjectClasses: {4}( 1.3.6.1.4.1.47732.1.4.0.0 NAME 'kopano-server' DESC '
|
||||
KOPANO: a Kopano server' SUP top AUXILIARY MUST cn MAY ( kopanoAccount $ ko
|
||||
panoHidden $ kopanoHttpPort $ kopanoSslPort $ kopanoFilePath $ kopanoContai
|
||||
nsPublic $ kopanoProxyURL ) )
|
||||
olcObjectClasses: {5}( 1.3.6.1.4.1.47732.1.5.0.0 NAME 'kopano-addresslist' D
|
||||
ESC 'KOPANO: a Kopano Addresslist' SUP top STRUCTURAL MUST cn MAY ( kopanoA
|
||||
ccount $ kopanoHidden $ kopanoFilter $ kopanoBase ) )
|
||||
olcObjectClasses: {6}( 1.3.6.1.4.1.47732.1.7.0.0 NAME 'kopano-dynamicgroup'
|
||||
DESC 'KOPANO: a Kopano dynamic group' SUP top STRUCTURAL MUST cn MAY ( kopa
|
||||
noAccount $ kopanoHidden $ mail $ kopanoAliases $ kopanoFilter $ kopanoBase
|
||||
) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 4144ac84-16b1-1040-995e-2797d7152165
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20250826101454Z
|
||||
entryCSN: 20250826101454.452403Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250826101454Z
|
||||
18
ldap-config/cn=config/cn=schema/cn={6}openssh-lpk.ldif
Normal file
18
ldap-config/cn=config/cn=schema/cn={6}openssh-lpk.ldif
Normal file
@@ -0,0 +1,18 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 c0c96705
|
||||
dn: cn={6}openssh-lpk
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {6}openssh-lpk
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' D
|
||||
ESC 'MANDATORY: OpenSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.40 )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' DE
|
||||
SC 'MANDATORY: OpenSSH LPK objectclass' SUP top AUXILIARY MAY ( sshPublicKe
|
||||
y $ uid ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 414c3832-16b1-1040-995f-2797d7152165
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20250826101454Z
|
||||
entryCSN: 20250826101454.501855Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250826101454Z
|
||||
50
ldap-config/cn=config/cn=schema/cn={7}postfix-book.ldif
Normal file
50
ldap-config/cn=config/cn=schema/cn={7}postfix-book.ldif
Normal file
@@ -0,0 +1,50 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 c7358a52
|
||||
dn: cn={7}postfix-book
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {7}postfix-book
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.29426.1.10.1 NAME 'mailHomeDirectory' DE
|
||||
SC 'The absolute path to the mail user home directory' EQUALITY caseExactIA
|
||||
5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.4.1.29426.1.10.2 NAME 'mailAlias' DESC 'RFC8
|
||||
22 Mailbox - mail alias' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Su
|
||||
bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.4.1.29426.1.10.3 NAME 'mailUidNumber' DESC '
|
||||
UID required to access the mailbox' EQUALITY integerMatch SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.4.1.29426.1.10.4 NAME 'mailGidNumber' DESC '
|
||||
GID required to access the mailbox' EQUALITY integerMatch SYNTAX 1.3.6.1.4.
|
||||
1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.4.1.29426.1.10.5 NAME 'mailEnabled' DESC 'TR
|
||||
UE to enable, FALSE to disable account' EQUALITY booleanMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.4.1.29426.1.10.6 NAME 'mailGroupMember' DESC
|
||||
'Name of a mail distribution list' EQUALITY caseExactIA5Match SYNTAX 1.3.6
|
||||
.1.4.1.1466.115.121.1.26 )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.4.1.29426.1.10.7 NAME 'mailQuota' DESC 'Mail
|
||||
quota limit in kilobytes' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.14
|
||||
66.115.121.1.26 )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.4.1.29426.1.10.8 NAME 'mailStorageDirectory'
|
||||
DESC 'The absolute path to the mail users mailbox' EQUALITY caseExactIA5Ma
|
||||
tch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.4.1.29426.1.10.9 NAME 'mailSieveRuleSource'
|
||||
DESC 'Sun ONE Messaging Server defined attribute' SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.26 X-ORIGIN 'Sun ONE Messaging Server' )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.4.1.29426.1.10.10 NAME 'mailForwardingAddres
|
||||
s' DESC 'Address(es) to forward all incoming messages to.' EQUALITY caseIgn
|
||||
oreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{320} )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.29426.1.2.2.1 NAME 'PostfixBookMailAccoun
|
||||
t' DESC 'Mail account used in Postfix Book' SUP top AUXILIARY MUST mail MAY
|
||||
( mailHomeDirectory $ mailAlias $ mailGroupMember $ mailUidNumber $ mailGi
|
||||
dNumber $ mailEnabled $ mailQuota $ mailStorageDirectory $ mailSieveRuleSou
|
||||
rce ) )
|
||||
olcObjectClasses: {1}( 1.3.6.1.4.1.29426.1.2.2.2 NAME 'PostfixBookMailForwar
|
||||
d' DESC 'Mail forward used in Postfix Book' SUP top AUXILIARY MUST ( mail $
|
||||
mailAlias ) MAY mailForwardingAddress )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 41542d80-16b1-1040-9960-2797d7152165
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20250826101454Z
|
||||
entryCSN: 20250826101454.554011Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250826101454Z
|
||||
235
ldap-config/cn=config/cn=schema/cn={8}samba.ldif
Normal file
235
ldap-config/cn=config/cn=schema/cn={8}samba.ldif
Normal file
@@ -0,0 +1,235 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 e6f2f44f
|
||||
dn: cn={8}samba
|
||||
objectClass: olcSchemaConfig
|
||||
cn: {8}samba
|
||||
olcAttributeTypes: {0}( 1.3.6.1.4.1.7165.2.1.24 NAME 'sambaLMPassword' DESC
|
||||
'LanManager Password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.26{32} SINGLE-VALUE )
|
||||
olcAttributeTypes: {1}( 1.3.6.1.4.1.7165.2.1.25 NAME 'sambaNTPassword' DESC
|
||||
'MD4 hash of the unicode password' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6
|
||||
.1.4.1.1466.115.121.1.26{32} SINGLE-VALUE )
|
||||
olcAttributeTypes: {2}( 1.3.6.1.4.1.7165.2.1.26 NAME 'sambaAcctFlags' DESC '
|
||||
Account Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.26{16} SINGLE-VALUE )
|
||||
olcAttributeTypes: {3}( 1.3.6.1.4.1.7165.2.1.27 NAME 'sambaPwdLastSet' DESC
|
||||
'Timestamp of the last password update' EQUALITY integerMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {4}( 1.3.6.1.4.1.7165.2.1.28 NAME 'sambaPwdCanChange' DES
|
||||
C 'Timestamp of when the user is allowed to update the password' EQUALITY i
|
||||
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {5}( 1.3.6.1.4.1.7165.2.1.29 NAME 'sambaPwdMustChange' DE
|
||||
SC 'Timestamp of when the password will expire' EQUALITY integerMatch SYNTA
|
||||
X 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {6}( 1.3.6.1.4.1.7165.2.1.30 NAME 'sambaLogonTime' DESC '
|
||||
Timestamp of last logon' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {7}( 1.3.6.1.4.1.7165.2.1.31 NAME 'sambaLogoffTime' DESC
|
||||
'Timestamp of last logoff' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.11
|
||||
5.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {8}( 1.3.6.1.4.1.7165.2.1.32 NAME 'sambaKickoffTime' DESC
|
||||
'Timestamp of when the user will be logged off automatically' EQUALITY int
|
||||
egerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {9}( 1.3.6.1.4.1.7165.2.1.48 NAME 'sambaBadPasswordCount'
|
||||
DESC 'Bad password attempt count' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1
|
||||
.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {10}( 1.3.6.1.4.1.7165.2.1.49 NAME 'sambaBadPasswordTime'
|
||||
DESC 'Time of the last bad password attempt' EQUALITY integerMatch SYNTAX
|
||||
1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {11}( 1.3.6.1.4.1.7165.2.1.55 NAME 'sambaLogonHours' DESC
|
||||
'Logon Hours' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.
|
||||
1.26{42} SINGLE-VALUE )
|
||||
olcAttributeTypes: {12}( 1.3.6.1.4.1.7165.2.1.33 NAME 'sambaHomeDrive' DESC
|
||||
'Driver letter of home directory mapping' EQUALITY caseIgnoreIA5Match SYNTA
|
||||
X 1.3.6.1.4.1.1466.115.121.1.26{4} SINGLE-VALUE )
|
||||
olcAttributeTypes: {13}( 1.3.6.1.4.1.7165.2.1.34 NAME 'sambaLogonScript' DES
|
||||
C 'Logon script path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.
|
||||
121.1.15{255} SINGLE-VALUE )
|
||||
olcAttributeTypes: {14}( 1.3.6.1.4.1.7165.2.1.35 NAME 'sambaProfilePath' DES
|
||||
C 'Roaming profile path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.15{255} SINGLE-VALUE )
|
||||
olcAttributeTypes: {15}( 1.3.6.1.4.1.7165.2.1.36 NAME 'sambaUserWorkstations
|
||||
' DESC 'List of user workstations the user is allowed to logon to' EQUALITY
|
||||
caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{255} SINGLE-VALUE )
|
||||
olcAttributeTypes: {16}( 1.3.6.1.4.1.7165.2.1.37 NAME 'sambaHomePath' DESC '
|
||||
Home directory UNC path' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.15{128} )
|
||||
olcAttributeTypes: {17}( 1.3.6.1.4.1.7165.2.1.38 NAME 'sambaDomainName' DESC
|
||||
'Windows NT domain to which the user belongs' EQUALITY caseIgnoreMatch SYN
|
||||
TAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {18}( 1.3.6.1.4.1.7165.2.1.47 NAME 'sambaMungedDial' DESC
|
||||
'Base64 encoded user parameter string' EQUALITY caseExactMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.15{1050} )
|
||||
olcAttributeTypes: {19}( 1.3.6.1.4.1.7165.2.1.54 NAME 'sambaPasswordHistory'
|
||||
DESC 'Concatenated MD5 hashes of the salted NT passwords used on this acco
|
||||
unt' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} )
|
||||
olcAttributeTypes: {20}( 1.3.6.1.4.1.7165.2.1.20 NAME 'sambaSID' DESC 'Secur
|
||||
ity ID' EQUALITY caseIgnoreIA5Match SUBSTR caseExactIA5SubstringsMatch SYNT
|
||||
AX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
|
||||
olcAttributeTypes: {21}( 1.3.6.1.4.1.7165.2.1.23 NAME 'sambaPrimaryGroupSID'
|
||||
DESC 'Primary Group Security ID' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
|
||||
olcAttributeTypes: {22}( 1.3.6.1.4.1.7165.2.1.51 NAME 'sambaSIDList' DESC 'S
|
||||
ecurity ID List' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.12
|
||||
1.1.26{64} )
|
||||
olcAttributeTypes: {23}( 1.3.6.1.4.1.7165.2.1.19 NAME 'sambaGroupType' DESC
|
||||
'NT Group Type' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {24}( 1.3.6.1.4.1.7165.2.1.21 NAME 'sambaNextUserRid' DES
|
||||
C 'Next NT rid to give our for users' EQUALITY integerMatch SYNTAX 1.3.6.1.
|
||||
4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {25}( 1.3.6.1.4.1.7165.2.1.22 NAME 'sambaNextGroupRid' DE
|
||||
SC 'Next NT rid to give out for groups' EQUALITY integerMatch SYNTAX 1.3.6.
|
||||
1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {26}( 1.3.6.1.4.1.7165.2.1.39 NAME 'sambaNextRid' DESC 'N
|
||||
ext NT rid to give out for anything' EQUALITY integerMatch SYNTAX 1.3.6.1.4
|
||||
.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {27}( 1.3.6.1.4.1.7165.2.1.40 NAME 'sambaAlgorithmicRidBa
|
||||
se' DESC 'Base at which the samba RID generation algorithm should operate'
|
||||
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {28}( 1.3.6.1.4.1.7165.2.1.41 NAME 'sambaShareName' DESC
|
||||
'Share Name' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {29}( 1.3.6.1.4.1.7165.2.1.42 NAME 'sambaOptionName' DESC
|
||||
'Option Name' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SY
|
||||
NTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
olcAttributeTypes: {30}( 1.3.6.1.4.1.7165.2.1.43 NAME 'sambaBoolOption' DESC
|
||||
'A boolean option' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
|
||||
.7 SINGLE-VALUE )
|
||||
olcAttributeTypes: {31}( 1.3.6.1.4.1.7165.2.1.44 NAME 'sambaIntegerOption' D
|
||||
ESC 'An integer option' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.1
|
||||
21.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {32}( 1.3.6.1.4.1.7165.2.1.45 NAME 'sambaStringOption' DE
|
||||
SC 'A string option' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115
|
||||
.121.1.26 SINGLE-VALUE )
|
||||
olcAttributeTypes: {33}( 1.3.6.1.4.1.7165.2.1.46 NAME 'sambaStringListOption
|
||||
' DESC 'A string list option' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1
|
||||
466.115.121.1.15 )
|
||||
olcAttributeTypes: {34}( 1.3.6.1.4.1.7165.2.1.53 NAME 'sambaTrustFlags' DESC
|
||||
'Trust Password Flags' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466
|
||||
.115.121.1.26 )
|
||||
olcAttributeTypes: {35}( 1.3.6.1.4.1.7165.2.1.58 NAME 'sambaMinPwdLength' DE
|
||||
SC 'Minimal password length (default: 5)' EQUALITY integerMatch SYNTAX 1.3.
|
||||
6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {36}( 1.3.6.1.4.1.7165.2.1.59 NAME 'sambaPwdHistoryLength
|
||||
' DESC 'Length of Password History Entries (default: 0 => off)' EQUALITY in
|
||||
tegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {37}( 1.3.6.1.4.1.7165.2.1.60 NAME 'sambaLogonToChgPwd' D
|
||||
ESC 'Force Users to logon for password change (default: 0 => off, 2 => on)'
|
||||
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {38}( 1.3.6.1.4.1.7165.2.1.61 NAME 'sambaMaxPwdAge' DESC
|
||||
'Maximum password age, in seconds (default: -1 => never expire passwords)'
|
||||
EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {39}( 1.3.6.1.4.1.7165.2.1.62 NAME 'sambaMinPwdAge' DESC
|
||||
'Minimum password age, in seconds (default: 0 => allow immediate password c
|
||||
hange)' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-V
|
||||
ALUE )
|
||||
olcAttributeTypes: {40}( 1.3.6.1.4.1.7165.2.1.63 NAME 'sambaLockoutDuration'
|
||||
DESC 'Lockout duration in minutes (default: 30, -1 => forever)' EQUALITY i
|
||||
ntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {41}( 1.3.6.1.4.1.7165.2.1.64 NAME 'sambaLockoutObservati
|
||||
onWindow' DESC 'Reset time after lockout in minutes (default: 30)' EQUALITY
|
||||
integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {42}( 1.3.6.1.4.1.7165.2.1.65 NAME 'sambaLockoutThreshold
|
||||
' DESC 'Lockout users after bad logon attempts (default: 0 => off)' EQUALIT
|
||||
Y integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {43}( 1.3.6.1.4.1.7165.2.1.66 NAME 'sambaForceLogoff' DES
|
||||
C 'Disconnect Users outside logon hours (default: -1 => off, 0 => on)' EQUA
|
||||
LITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {44}( 1.3.6.1.4.1.7165.2.1.67 NAME 'sambaRefuseMachinePwd
|
||||
Change' DESC 'Allow Machine Password changes (default: 0 => off)' EQUALITY
|
||||
integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {45}( 1.3.6.1.4.1.7165.2.1.68 NAME 'sambaClearTextPasswor
|
||||
d' DESC 'Clear text password (used for trusted domain passwords)' EQUALITY
|
||||
octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
|
||||
olcAttributeTypes: {46}( 1.3.6.1.4.1.7165.2.1.69 NAME 'sambaPreviousClearTex
|
||||
tPassword' DESC 'Previous clear text password (used for trusted domain pass
|
||||
words)' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
|
||||
olcAttributeTypes: {47}( 1.3.6.1.4.1.7165.2.1.70 NAME 'sambaTrustType' DESC
|
||||
'Type of trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
|
||||
SINGLE-VALUE )
|
||||
olcAttributeTypes: {48}( 1.3.6.1.4.1.7165.2.1.71 NAME 'sambaTrustAttributes'
|
||||
DESC 'Trust attributes for a trusted domain' EQUALITY integerMatch SYNTAX
|
||||
1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {49}( 1.3.6.1.4.1.7165.2.1.72 NAME 'sambaTrustDirection'
|
||||
DESC 'Direction of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.1
|
||||
15.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {50}( 1.3.6.1.4.1.7165.2.1.73 NAME 'sambaTrustPartner' DE
|
||||
SC 'Fully qualified name of the domain with which a trust exists' EQUALITY
|
||||
caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
olcAttributeTypes: {51}( 1.3.6.1.4.1.7165.2.1.74 NAME 'sambaFlatName' DESC '
|
||||
NetBIOS name of a domain' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.
|
||||
115.121.1.15{128} )
|
||||
olcAttributeTypes: {52}( 1.3.6.1.4.1.7165.2.1.75 NAME 'sambaTrustAuthOutgoin
|
||||
g' DESC 'Authentication information for the outgoing portion of a trust' EQ
|
||||
UALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||
olcAttributeTypes: {53}( 1.3.6.1.4.1.7165.2.1.76 NAME 'sambaTrustAuthIncomin
|
||||
g' DESC 'Authentication information for the incoming portion of a trust' EQ
|
||||
UALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||
olcAttributeTypes: {54}( 1.3.6.1.4.1.7165.2.1.77 NAME 'sambaSecurityIdentifi
|
||||
er' DESC 'SID of a trusted domain' EQUALITY caseIgnoreIA5Match SUBSTR caseE
|
||||
xactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALU
|
||||
E )
|
||||
olcAttributeTypes: {55}( 1.3.6.1.4.1.7165.2.1.78 NAME 'sambaTrustForestTrust
|
||||
Info' DESC 'Forest trust information for a trusted domain object' EQUALITY
|
||||
caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1050} )
|
||||
olcAttributeTypes: {56}( 1.3.6.1.4.1.7165.2.1.79 NAME 'sambaTrustPosixOffset
|
||||
' DESC 'POSIX offset of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1
|
||||
466.115.121.1.27 SINGLE-VALUE )
|
||||
olcAttributeTypes: {57}( 1.3.6.1.4.1.7165.2.1.80 NAME 'sambaSupportedEncrypt
|
||||
ionTypes' DESC 'Supported encryption types of a trust' EQUALITY integerMatc
|
||||
h SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
olcObjectClasses: {0}( 1.3.6.1.4.1.7165.2.2.6 NAME 'sambaSamAccount' DESC 'S
|
||||
amba 3.0 Auxilary SAM Account' SUP top AUXILIARY MUST ( uid $ sambaSID ) MA
|
||||
Y ( cn $ sambaLMPassword $ sambaNTPassword $ sambaPwdLastSet $ sambaLogonTi
|
||||
me $ sambaLogoffTime $ sambaKickoffTime $ sambaPwdCanChange $ sambaPwdMustC
|
||||
hange $ sambaAcctFlags $ displayName $ sambaHomePath $ sambaHomeDrive $ sam
|
||||
baLogonScript $ sambaProfilePath $ description $ sambaUserWorkstations $ sa
|
||||
mbaPrimaryGroupSID $ sambaDomainName $ sambaMungedDial $ sambaBadPasswordCo
|
||||
unt $ sambaBadPasswordTime $ sambaPasswordHistory $ sambaLogonHours ) )
|
||||
olcObjectClasses: {1}( 1.3.6.1.4.1.7165.2.2.4 NAME 'sambaGroupMapping' DESC
|
||||
'Samba Group Mapping' SUP top AUXILIARY MUST ( gidNumber $ sambaSID $ samba
|
||||
GroupType ) MAY ( displayName $ description $ sambaSIDList ) )
|
||||
olcObjectClasses: {2}( 1.3.6.1.4.1.7165.2.2.14 NAME 'sambaTrustPassword' DES
|
||||
C 'Samba Trust Password' SUP top STRUCTURAL MUST ( sambaDomainName $ sambaN
|
||||
TPassword $ sambaTrustFlags ) MAY ( sambaSID $ sambaPwdLastSet ) )
|
||||
olcObjectClasses: {3}( 1.3.6.1.4.1.7165.2.2.15 NAME 'sambaTrustedDomainPassw
|
||||
ord' DESC 'Samba Trusted Domain Password' SUP top STRUCTURAL MUST ( sambaDo
|
||||
mainName $ sambaSID $ sambaClearTextPassword $ sambaPwdLastSet ) MAY sambaP
|
||||
reviousClearTextPassword )
|
||||
olcObjectClasses: {4}( 1.3.6.1.4.1.7165.2.2.5 NAME 'sambaDomain' DESC 'Samba
|
||||
Domain Information' SUP top STRUCTURAL MUST ( sambaDomainName $ sambaSID )
|
||||
MAY ( sambaNextRid $ sambaNextGroupRid $ sambaNextUserRid $ sambaAlgorithm
|
||||
icRidBase $ sambaMinPwdLength $ sambaPwdHistoryLength $ sambaLogonToChgPwd
|
||||
$ sambaMaxPwdAge $ sambaMinPwdAge $ sambaLockoutDuration $ sambaLockoutObse
|
||||
rvationWindow $ sambaLockoutThreshold $ sambaForceLogoff $ sambaRefuseMachi
|
||||
nePwdChange ) )
|
||||
olcObjectClasses: {5}( 1.3.6.1.4.1.7165.2.2.7 NAME 'sambaUnixIdPool' DESC 'P
|
||||
ool for allocating UNIX uids/gids' SUP top AUXILIARY MUST ( uidNumber $ gid
|
||||
Number ) )
|
||||
olcObjectClasses: {6}( 1.3.6.1.4.1.7165.2.2.8 NAME 'sambaIdmapEntry' DESC 'M
|
||||
apping from a SID to an ID' SUP top AUXILIARY MUST sambaSID MAY ( uidNumber
|
||||
$ gidNumber ) )
|
||||
olcObjectClasses: {7}( 1.3.6.1.4.1.7165.2.2.9 NAME 'sambaSidEntry' DESC 'Str
|
||||
uctural Class for a SID' SUP top STRUCTURAL MUST sambaSID )
|
||||
olcObjectClasses: {8}( 1.3.6.1.4.1.7165.2.2.10 NAME 'sambaConfig' DESC 'Samb
|
||||
a Configuration Section' SUP top AUXILIARY MAY description )
|
||||
olcObjectClasses: {9}( 1.3.6.1.4.1.7165.2.2.11 NAME 'sambaShare' DESC 'Samba
|
||||
Share Section' SUP top STRUCTURAL MUST sambaShareName MAY description )
|
||||
olcObjectClasses: {10}( 1.3.6.1.4.1.7165.2.2.12 NAME 'sambaConfigOption' DES
|
||||
C 'Samba Configuration Option' SUP top STRUCTURAL MUST sambaOptionName MAY
|
||||
( sambaBoolOption $ sambaIntegerOption $ sambaStringOption $ sambaStringLis
|
||||
toption $ description ) )
|
||||
olcObjectClasses: {11}( 1.3.6.1.4.1.7165.2.2.16 NAME 'sambaTrustedDomain' DE
|
||||
SC 'Samba Trusted Domain Object' SUP top STRUCTURAL MUST cn MAY ( sambaTrus
|
||||
tType $ sambaTrustAttributes $ sambaTrustDirection $ sambaTrustPartner $ sa
|
||||
mbaFlatName $ sambaTrustAuthOutgoing $ sambaTrustAuthIncoming $ sambaSecuri
|
||||
tyIdentifier $ sambaTrustForestTrustInfo $ sambaTrustPosixOffset $ sambaSup
|
||||
portedEncryptionTypes ) )
|
||||
structuralObjectClass: olcSchemaConfig
|
||||
entryUUID: 415c59ce-16b1-1040-9961-2797d7152165
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20250826101454Z
|
||||
entryCSN: 20250826101454.607572Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250826101454Z
|
||||
18
ldap-config/cn=config/olcDatabase={-1}frontend.ldif
Normal file
18
ldap-config/cn=config/olcDatabase={-1}frontend.ldif
Normal file
@@ -0,0 +1,18 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 fe90ccf1
|
||||
dn: olcDatabase={-1}frontend
|
||||
objectClass: olcDatabaseConfig
|
||||
objectClass: olcFrontendConfig
|
||||
olcDatabase: {-1}frontend
|
||||
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=extern
|
||||
al,cn=auth manage by * break
|
||||
olcAccess: {1}to dn.exact="" by * read
|
||||
olcAccess: {2}to dn.base="cn=Subschema" by * read
|
||||
olcSizeLimit: 500
|
||||
structuralObjectClass: olcDatabaseConfig
|
||||
entryUUID: 40bc7da0-16b1-1040-911c-a716cb68c9fb
|
||||
creatorsName: cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
entryCSN: 20250826101453.559911Z#000000#000#000000
|
||||
modifiersName: cn=config
|
||||
modifyTimestamp: 20250826101453Z
|
||||
16
ldap-config/cn=config/olcDatabase={0}config.ldif
Normal file
16
ldap-config/cn=config/olcDatabase={0}config.ldif
Normal file
@@ -0,0 +1,16 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 c15d64f1
|
||||
dn: olcDatabase={0}config
|
||||
objectClass: olcDatabaseConfig
|
||||
olcDatabase: {0}config
|
||||
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=extern
|
||||
al,cn=auth manage by * break
|
||||
olcRootDN: cn=admin,cn=config
|
||||
structuralObjectClass: olcDatabaseConfig
|
||||
entryUUID: 40c0138e-16b1-1040-911d-a716cb68c9fb
|
||||
creatorsName: cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
olcRootPW:: e1NTSEF9ZkdLK0pYSC9xVDk3eEtWMWVHTmsrTHRkZlpxYTFwamk=
|
||||
entryCSN: 20250828121450.398436Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250828121450Z
|
||||
32
ldap-config/cn=config/olcDatabase={1}mdb.ldif
Normal file
32
ldap-config/cn=config/olcDatabase={1}mdb.ldif
Normal file
@@ -0,0 +1,32 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 7b4c0c45
|
||||
dn: olcDatabase={1}mdb
|
||||
objectClass: olcDatabaseConfig
|
||||
objectClass: olcMdbConfig
|
||||
olcDatabase: {1}mdb
|
||||
olcDbDirectory: /var/lib/ldap
|
||||
olcSuffix: dc=lcepl,dc=org
|
||||
olcLastMod: TRUE
|
||||
olcRootDN: cn=admin,dc=lcepl,dc=org
|
||||
olcDbCheckpoint: 512 30
|
||||
olcDbMaxSize: 1073741824
|
||||
structuralObjectClass: olcMdbConfig
|
||||
entryUUID: 40d5b3a6-16b1-1040-9124-a716cb68c9fb
|
||||
creatorsName: cn=admin,cn=config
|
||||
createTimestamp: 20250826101453Z
|
||||
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=extern
|
||||
al,cn=auth manage by * break
|
||||
olcAccess: {1}to attrs=userPassword,shadowLastChange by self write by dn="cn
|
||||
=admin,dc=lcepl,dc=org" write by anonymous auth by * none
|
||||
olcAccess: {2}to * by self read by dn="cn=admin,dc=lcepl,dc=org" write by *
|
||||
none
|
||||
olcDbIndex: uid eq
|
||||
olcDbIndex: mail eq
|
||||
olcDbIndex: memberOf eq
|
||||
olcDbIndex: entryCSN eq
|
||||
olcDbIndex: entryUUID eq
|
||||
olcDbIndex: objectClass eq
|
||||
olcRootPW:: e1NTSEF9UXh4Qi95TkhZc25LNzJ1NGRoWERhSFljRlEzaWdWc1Y=
|
||||
entryCSN: 20250828121450.413834Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250828121450Z
|
||||
@@ -0,0 +1,18 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 c294553c
|
||||
dn: olcOverlay={0}memberof
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcMemberOf
|
||||
olcOverlay: {0}memberof
|
||||
olcMemberOfDangling: ignore
|
||||
olcMemberOfRefInt: TRUE
|
||||
olcMemberOfGroupOC: groupOfUniqueNames
|
||||
olcMemberOfMemberAD: uniqueMember
|
||||
olcMemberOfMemberOfAD: memberOf
|
||||
structuralObjectClass: olcMemberOf
|
||||
entryUUID: 4182ac28-16b1-1040-9962-2797d7152165
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20250826101454Z
|
||||
entryCSN: 20250826101454.858707Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250826101454Z
|
||||
@@ -0,0 +1,18 @@
|
||||
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
|
||||
# CRC32 79898312
|
||||
dn: olcOverlay={1}refint
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcRefintConfig
|
||||
olcOverlay: {1}refint
|
||||
olcRefintAttribute: owner
|
||||
olcRefintAttribute: manager
|
||||
olcRefintAttribute: uniqueMember
|
||||
olcRefintAttribute: member
|
||||
olcRefintAttribute: memberOf
|
||||
structuralObjectClass: olcRefintConfig
|
||||
entryUUID: 418d5344-16b1-1040-9963-2797d7152165
|
||||
creatorsName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
createTimestamp: 20250826101454Z
|
||||
entryCSN: 20250826101454.928532Z#000000#000#000000
|
||||
modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
|
||||
modifyTimestamp: 20250826101454Z
|
||||
0
ldap-config/docker-openldap-was-admin-password-set
Normal file
0
ldap-config/docker-openldap-was-admin-password-set
Normal file
4
ldap-config/docker-openldap-was-started-with-tls
Normal file
4
ldap-config/docker-openldap-was-started-with-tls
Normal file
@@ -0,0 +1,4 @@
|
||||
export PREVIOUS_LDAP_TLS_CA_CRT_PATH=/container/service/slapd/assets/certs/ca.crt
|
||||
export PREVIOUS_LDAP_TLS_CRT_PATH=/container/service/slapd/assets/certs/ldap.crt
|
||||
export PREVIOUS_LDAP_TLS_KEY_PATH=/container/service/slapd/assets/certs/ldap.key
|
||||
export PREVIOUS_LDAP_TLS_DH_PARAM_PATH=/container/service/slapd/assets/certs/dhparam.pem
|
||||
BIN
ldap-data/data.mdb
Normal file
BIN
ldap-data/data.mdb
Normal file
Binary file not shown.
BIN
ldap-data/lock.mdb
Normal file
BIN
ldap-data/lock.mdb
Normal file
Binary file not shown.
6
ldifs/base.ldif
Normal file
6
ldifs/base.ldif
Normal file
@@ -0,0 +1,6 @@
|
||||
dn: dc=lcepl,dc=org
|
||||
objectClass: top
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: LCEPL Organization
|
||||
dc: lcepl
|
||||
2
ldifs/desktop.ini
Normal file
2
ldifs/desktop.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
[LocalizedFileNames]
|
||||
ou.ldif=@ou,0
|
||||
13
ldifs/laxmi_add.ldif
Normal file
13
ldifs/laxmi_add.ldif
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
3
ldifs/ou-users.ldif
Normal file
3
ldifs/ou-users.ldif
Normal file
@@ -0,0 +1,3 @@
|
||||
dn: ou=users,dc=lcepl,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
3
ldifs/ou.ldif
Normal file
3
ldifs/ou.ldif
Normal file
@@ -0,0 +1,3 @@
|
||||
dn: ou=users,dc=lcepl,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
6
ldifs/prajakta.ldif
Normal file
6
ldifs/prajakta.ldif
Normal file
@@ -0,0 +1,6 @@
|
||||
dn: uid=prajakta,ou=users,dc=lcepl,dc=org
|
||||
objectClass: inetOrgPerson
|
||||
cn: Prajakta
|
||||
sn: S
|
||||
uid: prajakta
|
||||
userPassword: prajakta123
|
||||
0
logs/activity.log
Normal file
0
logs/activity.log
Normal file
1
migrations/README
Normal file
1
migrations/README
Normal file
@@ -0,0 +1 @@
|
||||
Single-database configuration for Flask.
|
||||
BIN
migrations/__pycache__/env.cpython-39.pyc
Normal file
BIN
migrations/__pycache__/env.cpython-39.pyc
Normal file
Binary file not shown.
51
migrations/alembic.ini
Normal file
51
migrations/alembic.ini
Normal file
@@ -0,0 +1,51 @@
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
sqlalchemy.url = mysql+pymysql://root:tiger@mysql:3306/excel_data7
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic,flask_migrate
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[logger_flask_migrate]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = flask_migrate
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
113
migrations/env.py
Normal file
113
migrations/env.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import logging
|
||||
from logging.config import fileConfig
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from alembic import context
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
logger = logging.getLogger('alembic.env')
|
||||
|
||||
|
||||
def get_engine():
|
||||
try:
|
||||
# this works with Flask-SQLAlchemy<3 and Alchemical
|
||||
return current_app.extensions['migrate'].db.get_engine()
|
||||
except (TypeError, AttributeError):
|
||||
# this works with Flask-SQLAlchemy>=3
|
||||
return current_app.extensions['migrate'].db.engine
|
||||
|
||||
|
||||
def get_engine_url():
|
||||
try:
|
||||
return get_engine().url.render_as_string(hide_password=False).replace(
|
||||
'%', '%%')
|
||||
except AttributeError:
|
||||
return str(get_engine().url).replace('%', '%%')
|
||||
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
config.set_main_option('sqlalchemy.url', get_engine_url())
|
||||
target_db = current_app.extensions['migrate'].db
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
|
||||
def get_metadata():
|
||||
if hasattr(target_db, 'metadatas'):
|
||||
return target_db.metadatas[None]
|
||||
return target_db.metadata
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url, target_metadata=get_metadata(), literal_binds=True
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
|
||||
# this callback is used to prevent an auto-migration from being generated
|
||||
# when there are no changes to the schema
|
||||
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
||||
def process_revision_directives(context, revision, directives):
|
||||
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||
script = directives[0]
|
||||
if script.upgrade_ops.is_empty():
|
||||
directives[:] = []
|
||||
logger.info('No changes in schema detected.')
|
||||
|
||||
conf_args = current_app.extensions['migrate'].configure_args
|
||||
if conf_args.get("process_revision_directives") is None:
|
||||
conf_args["process_revision_directives"] = process_revision_directives
|
||||
|
||||
connectable = get_engine()
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=get_metadata(),
|
||||
**conf_args
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
24
migrations/script.py.mako
Normal file
24
migrations/script.py.mako
Normal file
@@ -0,0 +1,24 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
||||
Binary file not shown.
90
migrations/versions/fc9ad8ba55e5_initial_migrations.py
Normal file
90
migrations/versions/fc9ad8ba55e5_initial_migrations.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""initial migrations
|
||||
|
||||
Revision ID: fc9ad8ba55e5
|
||||
Revises:
|
||||
Create Date: 2025-08-28 09:19:51.758029
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fc9ad8ba55e5'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('activity_log',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user', sa.String(length=100), nullable=True),
|
||||
sa.Column('action', sa.String(length=255), nullable=True),
|
||||
sa.Column('details', sa.Text(), nullable=True),
|
||||
sa.Column('timestamp', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('task',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('district', sa.String(length=255), nullable=True),
|
||||
sa.Column('block_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('village_name', sa.String(length=255), nullable=True),
|
||||
sa.Column('serial_number', sa.String(length=255), nullable=True),
|
||||
sa.Column('parent_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('parent_task_name', sa.Text(), nullable=True),
|
||||
sa.Column('task_name', sa.Text(), nullable=True),
|
||||
sa.Column('unit', sa.String(length=255), nullable=True),
|
||||
sa.Column('qty', sa.String(length=255), nullable=True),
|
||||
sa.Column('rate', sa.String(length=255), nullable=True),
|
||||
sa.Column('boq_amount', sa.String(length=255), nullable=True),
|
||||
sa.Column('previous_billed_qty', sa.String(length=255), nullable=True),
|
||||
sa.Column('previous_billing_amount', sa.String(length=255), nullable=True),
|
||||
sa.Column('remaining_amount', sa.String(length=255), nullable=True),
|
||||
sa.Column('in_this_ra_bill_qty', sa.String(length=255), nullable=True),
|
||||
sa.Column('in_this_ra_billing_amount', sa.String(length=255), nullable=True),
|
||||
sa.Column('cumulative_billed_qty', sa.String(length=255), nullable=True),
|
||||
sa.Column('cumulative_billed_amount', sa.String(length=255), nullable=True),
|
||||
sa.Column('variation_qty', sa.String(length=255), nullable=True),
|
||||
sa.Column('variation_amount', sa.String(length=255), nullable=True),
|
||||
sa.Column('remark', sa.String(length=255), nullable=True),
|
||||
sa.Column('uploaded_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('user',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('username', sa.String(length=255), nullable=False),
|
||||
sa.Column('email', sa.String(length=255), nullable=False),
|
||||
sa.Column('password_hash', sa.String(length=255), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('email'),
|
||||
sa.UniqueConstraint('username')
|
||||
)
|
||||
op.create_table('work_detail',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name_of_work', sa.String(length=255), nullable=True),
|
||||
sa.Column('cover_agreement_no', sa.String(length=255), nullable=True),
|
||||
sa.Column('name_of_contractor', sa.String(length=255), nullable=True),
|
||||
sa.Column('name_of_tpi_agency', sa.String(length=255), nullable=True),
|
||||
sa.Column('name_of_division', sa.String(length=255), nullable=True),
|
||||
sa.Column('name_of_village', sa.String(length=255), nullable=True),
|
||||
sa.Column('block', sa.String(length=255), nullable=True),
|
||||
sa.Column('scheme_id', sa.String(length=255), nullable=True),
|
||||
sa.Column('measurement_book', sa.String(length=255), nullable=True),
|
||||
sa.Column('date_of_billing', sa.String(length=255), nullable=True),
|
||||
sa.Column('uploaded_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('district', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('work_detail')
|
||||
op.drop_table('user')
|
||||
op.drop_table('task')
|
||||
op.drop_table('activity_log')
|
||||
# ### end Alembic commands ###
|
||||
18
requirements.txt
Normal file
18
requirements.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
# Flask
|
||||
# Flask-SQLAlchemy
|
||||
# pymysql
|
||||
# openpyxl
|
||||
# pandas
|
||||
# python-dotenv
|
||||
# cryptography
|
||||
# Flask-Migrate
|
||||
Flask
|
||||
flask-login
|
||||
Flask-SQLAlchemy
|
||||
pymysql
|
||||
openpyxl
|
||||
pandas
|
||||
python-dotenv
|
||||
cryptography
|
||||
Flask-Migrate
|
||||
flask_ldap3_login
|
||||
10
run.py
Normal file
10
run.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from app import create_app, db
|
||||
|
||||
app = create_app()
|
||||
app.config['MAX_CONTENT_LENGTH'] = 64 * 1024 * 1024
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
app.run(host='0.0.0.0', port=5002, debug=True)
|
||||
BIN
static/images/lcepl_logo.png
Normal file
BIN
static/images/lcepl_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
uploads/150mm EMF_Report.xlsx
Normal file
BIN
uploads/150mm EMF_Report.xlsx
Normal file
Binary file not shown.
BIN
uploads/200 Kl 12 M Staging_Report.xlsx
Normal file
BIN
uploads/200 Kl 12 M Staging_Report.xlsx
Normal file
Binary file not shown.
BIN
uploads/250 KL 14 M Staging_Report.xlsx
Normal file
BIN
uploads/250 KL 14 M Staging_Report.xlsx
Normal file
Binary file not shown.
BIN
uploads/400 MMØ_Report.xlsx
Normal file
BIN
uploads/400 MMØ_Report.xlsx
Normal file
Binary file not shown.
BIN
uploads/450 MMØ_Report.xlsx
Normal file
BIN
uploads/450 MMØ_Report.xlsx
Normal file
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