Initial commit

This commit is contained in:
root
2026-07-18 16:04:40 +00:00
commit d96a087a16
6143 changed files with 827458 additions and 0 deletions

5
.env Normal file
View File

@@ -0,0 +1,5 @@
DB_NAME=pds
DB_USER=pds
DB_PASSWORD=pds
DB_HOST=172.31.31.51
DB_PORT=5432

0
accounts/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
accounts/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
accounts/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'

View File

110
accounts/models.py Normal file
View File

@@ -0,0 +1,110 @@
from django.db import models
class Plant(models.Model):
id = models.BigAutoField(primary_key=True)
company_id = models.IntegerField()
code = models.IntegerField(unique=True)
name = models.TextField(unique=True)
address = models.TextField()
created_at = models.DateTimeField()
updated_at = models.DateTimeField()
deleted_at = models.DateTimeField(
blank=True,
null=True
)
warehouse_number = models.TextField(
blank=True,
null=True
)
class Meta:
managed = False
db_table = 'plants'
class InvoiceValidation(models.Model):
plant = models.ForeignKey(
Plant,
on_delete=models.DO_NOTHING,
db_column='plant_id'
)
invoice_number = models.CharField(max_length=100)
quantity = models.IntegerField(null=True)
scanned_status = models.CharField(max_length=50)
serial_number = models.CharField(
max_length=100,
null=True
)
created_at = models.DateTimeField()
class Meta:
managed = False
db_table = "invoice_validations"
class Users(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=255)
email = models.CharField(max_length=255)
password = models.CharField(max_length=255)
class Meta:
managed = False
db_table = "users"
class Lines(models.Model):
id = models.BigAutoField(primary_key=True)
plant = models.ForeignKey(
'Plant',
models.DO_NOTHING,
db_column='plant_id'
)
name = models.TextField()
type = models.TextField()
created_at = models.DateTimeField()
updated_at = models.DateTimeField()
deleted_at = models.DateTimeField(blank=True, null=True)
line_capacity = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'lines'
class BreakdownLogs(models.Model):
id = models.BigAutoField(primary_key=True)
plant = models.ForeignKey('Plant',models.DO_NOTHING,db_column='plant_id')
line = models.ForeignKey('Lines',models.DO_NOTHING,db_column='line_id')
machine_name = models.CharField(max_length=255)
reason = models.TextField(blank=True,null=True)
breakdown_at = models.DateField(blank=True,null=True)
breakdown_time = models.TimeField(blank=True,null=True)
down_time = models.TimeField(blank=True,null=True)
created_at = models.DateTimeField(blank=True,null=True)
created_by = models.TextField(blank=True,null=True)
updated_at = models.DateTimeField(blank=True,null=True)
updated_by = models.TextField(blank=True,null=True)
deleted_at = models.DateTimeField(blank=True,null=True)
class Meta:
managed = False
db_table = "breakdown_logs"

3
accounts/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
accounts/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.login, name='login'),
path('dashboard/', views.dashboard, name='dashboard'),
path("get-lines/<int:plant_id>/",views.get_lines,name="get_lines"),
path("save-breakdown/", views.save_breakdown, name="save_breakdown"),
]

27
accounts/utils.py Normal file
View File

@@ -0,0 +1,27 @@
from datetime import timedelta
from django.utils import timezone
def get_shift_date_range(schedule="daily"):
now = timezone.now()
today_8am = now.replace(
hour=8,
minute=0,
second=0,
microsecond=0
)
if schedule.lower() == "daily":
start_date = today_8am - timedelta(days=1)
end_date = today_8am
else:
start_date = today_8am
end_date = today_8am + timedelta(days=1)
return start_date, end_date

224
accounts/views.py Normal file
View File

@@ -0,0 +1,224 @@
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.db.models import Count, Q
from .models import Plant, InvoiceValidation, Users, Lines, BreakdownLogs
from .utils import get_shift_date_range
import bcrypt
from django.contrib import messages
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json
from django.db import connection
from django.utils import timezone
from datetime import datetime, time, timedelta
def login(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
user = Users.objects.filter(name=username).first()
if user:
if bcrypt.checkpw(
password.encode(),
user.password.encode()
):
return redirect("dashboard")
else:
messages.error(request, "Invalid Password")
return redirect("login")
else:
messages.error(request, "Username not found")
return redirect("login")
return render(request, "login.html")
def get_lines(request, plant_id):
lines = Lines.objects.filter(
plant_id=plant_id,
deleted_at__isnull=True
).values("id", "name")
return JsonResponse({
"lines": list(lines)
})
@csrf_exempt
def save_breakdown(request):
try:
data = json.loads(request.body)
with connection.cursor() as cursor:
cursor.execute("""
INSERT INTO breakdown_logs
(
plant_id,
line_id,
machine_name,
reason,
breakdown_at,
breakdown_time,
down_time
)
VALUES (%s,%s,%s,%s,%s,%s,%s)
""", [
data["plant_id"],
data["line_id"],
data["machine_name"],
data["reason"],
data["breakdown_date"],
data["breakdown_time"],
data["down_time"]
])
return JsonResponse({"status": "success"})
except Exception as e:
print(e)
return JsonResponse({
"status": "error",
"message": str(e)
}, status=500)
def dashboard(request):
start_date, end_date = get_shift_date_range("daily")
plants = Plant.objects.all()
total_plants = plants.count()
invoice_report = []
for plant in plants:
# Serial Invoice
total_serial = (
InvoiceValidation.objects
.filter(
plant_id=plant.id,
quantity__isnull=True,
created_at__gte=start_date,
created_at__lt=end_date
)
.values('invoice_number')
.distinct()
.count()
)
scanned_serial = (
InvoiceValidation.objects
.filter(
plant_id=plant.id,
quantity__isnull=True,
scanned_status="Scanned",
created_at__gte=start_date,
created_at__lt=end_date
)
.values('invoice_number')
.distinct()
.count()
)
# Material Invoice
total_material = (
InvoiceValidation.objects
.filter(
plant_id=plant.id,
quantity=1,
created_at__gte=start_date,
created_at__lt=end_date
)
.values('invoice_number')
.distinct()
.count()
)
# Bundle Invoice
total_bundle = (
InvoiceValidation.objects
.filter(
plant_id=plant.id,
quantity__gt=1,
created_at__gte=start_date,
created_at__lt=end_date
)
.values('invoice_number')
.distinct()
.count()
)
invoice_report.append({
"plant": plant.name,
"serial_total": total_serial,
"serial_scanned": scanned_serial,
"material_total": total_material,
"bundle_total": total_bundle
})
plants = Plant.objects.filter(deleted_at__isnull=True).order_by("name")
now = timezone.localtime()
now1 = timezone.now()
start_time = timezone.make_aware(
datetime.combine(now.date(), time(8, 0, 0))
)
# If current time is before 8 AM, shift starts yesterday 8 AM
if now < start_time:
start_time = start_time - timedelta(days=1)
# Next day 08:00 AM
end_time = start_time + timedelta(days=1)
breakdowns = BreakdownLogs.objects.filter(
deleted_at__isnull=True,
created_at__gte=start_time,
created_at__lt=end_time
)
return render(
request,
"dashboard.html",
{
"total_plants": total_plants,
"plants": plants,
"breakdowns": breakdowns,
"total_serial": total_serial,
"scanned_serial": scanned_serial,
"invoice_report": invoice_report,
"start_date":start_date,
"end_date":end_date
}
)

0
dashboard/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
dashboard/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
dashboard/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class DashboardConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'dashboard'

View File

3
dashboard/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
dashboard/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

6
dashboard/urls.py Normal file
View File

@@ -0,0 +1,6 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='dashboard'),
]

6
dashboard/views.py Normal file
View File

@@ -0,0 +1,6 @@
from django.shortcuts import render
from django.http import HttpResponse
def home(request):
return HttpResponse("Dashboard")

0
departments/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
departments/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
departments/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class DepartmentsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'departments'

View File

3
departments/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
departments/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

6
departments/urls.py Normal file
View File

@@ -0,0 +1,6 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='departments'),
]

7
departments/views.py Normal file
View File

@@ -0,0 +1,7 @@
from django.shortcuts import render
# Create your views here.
from django.http import HttpResponse
def index(request):
return HttpResponse("Departments")

0
employees/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
employees/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
employees/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class EmployeesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'employees'

View File

3
employees/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
employees/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

6
employees/urls.py Normal file
View File

@@ -0,0 +1,6 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='employees'),
]

7
employees/views.py Normal file
View File

@@ -0,0 +1,7 @@
from django.shortcuts import render
# Create your views here.
from django.http import HttpResponse
def index(request):
return HttpResponse("Employees")

22
manage.py Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oee.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

0
oee/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
oee/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for oee project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oee.settings')
application = get_asgi_application()

165
oee/settings.py Normal file
View File

@@ -0,0 +1,165 @@
"""
Django settings for oee project.
Generated by 'django-admin startproject' using Django 5.2.16.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
from dotenv import load_dotenv
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv()
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-cei!of3&6g1r5l7ytt)(j!qpd86^_^9b(lmyf2ofs9rqg_@gkp'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'dashboard',
'employees',
'departments',
'accounts',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'oee.urls'
# TEMPLATES = [
# {
# 'BACKEND': 'django.template.backends.django.DjangoTemplates',
# 'DIRS': [],
# 'APP_DIRS': True,
# 'OPTIONS': {
# 'context_processors': [
# 'django.template.context_processors.request',
# 'django.contrib.auth.context_processors.auth',
# 'django.contrib.messages.context_processors.messages',
# ],
# },
# },
# ]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'oee.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASSWORD'),
'HOST': os.getenv('DB_HOST'),
'PORT': os.getenv('DB_PORT'),
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

28
oee/urls.py Normal file
View File

@@ -0,0 +1,28 @@
"""
URL configuration for oee project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('accounts.urls')),
path('dashboard/', include('dashboard.urls')),
path('employees/', include('employees.urls')),
path('departments/', include('departments.urls')),
]

16
oee/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for oee project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'oee.settings')
application = get_wsgi_application()

5
requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
asgiref==3.11.1
bcrypt==5.0.0
python-dotenv==1.2.2
sqlparse==0.5.5
typing_extensions==4.16.0

BIN
static/images/motor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

1343
templates/dashboard.html Normal file

File diff suppressed because it is too large Load Diff

556
templates/login.html Normal file
View File

@@ -0,0 +1,556 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRI Pumps - MIS Portal</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;600;700;800&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500&display=swap" rel="stylesheet">
<style>
:root{
--navy-950:#071B2E;
--navy-800:#0C3B5E;
--navy-700:#124a72;
--teal-500:#00AEEF;
--teal-400:#3FC6F0;
--steel-300:#9FB8C9;
--slate-50:#F4F7FA;
--ink-800:#1E2A33;
--ink-500:#5B6B78;
--line:#E1E8ED;
}
*{
margin:0;
padding:0;
box-sizing:border-box;
}
body{
font-family:'Inter',Arial,sans-serif;
height:100vh;
display:flex;
background:var(--slate-50);
color:var(--ink-800);
}
/* ============ LEFT PANEL ============ */
.left-panel{
position:relative;
width:58%;
overflow:hidden;
background:linear-gradient(160deg,var(--navy-950) 0%,var(--navy-800) 55%,var(--navy-700) 100%);
/*background:
linear-gradient(rgba(7,27,46,.88),rgba(12,59,94,.90)),
url("{% static 'images/motor.png' %}");*/
color:#fff;
display:flex;
flex-direction:column;
justify-content:center;
padding:80px 90px;
}
/* fine industrial grid texture */
.left-panel::before{
content:"";
position:absolute;
inset:0;
background-image:
linear-gradient(rgba(255,255,255,.035) 1px,transparent 1px),
linear-gradient(90deg,rgba(255,255,255,.035) 1px,transparent 1px);
background-size:38px 38px;
pointer-events:none;
}
/* radial glow, top right this time so it doesn't fight the wave */
.left-panel::after{
content:"";
position:absolute;
width:560px;height:560px;
right:-160px;
top:-180px;
background:radial-gradient(circle,rgba(0,174,239,.16) 0%,rgba(0,174,239,0) 70%);
pointer-events:none;
}
.left-content{
position:relative;
z-index:2;
}
.eyebrow{
display:inline-block;
font-family:'IBM Plex Mono',monospace;
font-size:12px;
letter-spacing:.16em;
color:var(--teal-400);
background:rgba(0,174,239,.10);
border:1px solid rgba(0,174,239,.35);
padding:6px 14px;
border-radius:100px;
margin-bottom:22px;
}
/* rotating live-focus ticker */
.ticker-row{
display:flex;
align-items:baseline;
gap:9px;
margin-bottom:20px;
font-family:'IBM Plex Mono',monospace;
}
.ticker-label{
font-size:11px;
letter-spacing:.12em;
text-transform:uppercase;
color:rgba(255,255,255,.4);
}
.ticker-label::before{
content:"● ";
color:#2FBF71;
font-size:8px;
}
.ticker-text{
font-size:15px;
font-weight:500;
letter-spacing:.06em;
color:var(--teal-400);
text-transform:uppercase;
display:inline-block;
transition:opacity .22s ease, transform .22s ease;
}
.ticker-text.swap{
opacity:0;
transform:translateY(-6px);
}
.left-panel h1{
font-family:'Sora',sans-serif;
font-weight:800;
font-size:46px;
line-height:1.12;
letter-spacing:-.01em;
margin-bottom:14px;
}
.left-panel h3{
font-family:'Sora',sans-serif;
font-weight:600;
font-size:17px;
color:var(--steel-300);
margin-bottom:22px;
}
.left-panel p{
font-size:15.5px;
line-height:1.75;
color:rgba(255,255,255,.78);
width:88%;
margin-bottom:36px;
}
/* module chips - now double as the rotation indicator */
.modules{
display:flex;
flex-wrap:wrap;
gap:10px;
position:relative;
z-index:2;
}
.module-chip{
font-family:'IBM Plex Mono',monospace;
font-size:11.5px;
letter-spacing:.05em;
color:var(--steel-300);
border:1px solid rgba(255,255,255,.16);
background:rgba(255,255,255,.04);
padding:8px 14px;
border-radius:8px;
display:flex;
align-items:center;
gap:8px;
transition:border-color .3s ease, background .3s ease, transform .3s ease, box-shadow .3s ease, color .3s ease;
}
.module-chip span.dot{
width:6px;height:6px;
border-radius:50%;
background:rgba(255,255,255,.3);
transition:background .3s ease, box-shadow .3s ease;
}
.module-chip.active{
color:#fff;
border-color:rgba(0,174,239,.6);
background:rgba(0,174,239,.14);
box-shadow:0 0 0 3px rgba(0,174,239,.1);
transform:translateY(-1px);
}
.module-chip.active span.dot{
background:var(--teal-400);
box-shadow:0 0 8px var(--teal-400);
}
/* ============ LIQUID WAVE FOOTER (signature element) ============ */
.wave-wrap{
position:absolute;
left:0; right:0; bottom:0;
height:120px;
overflow:hidden;
z-index:1;
pointer-events:none;
}
.wave-layer{
position:absolute;
bottom:0; left:0;
width:200%; height:100%;
background-repeat:repeat-x;
background-size:480px 100%;
}
.wave-back{
opacity:.28;
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 480 120'><path d='M0,64 C 60,20 180,20 240,64 C 300,108 420,108 480,64 L480,120 L0,120 Z' fill='%233FC6F0'/></svg>");
animation:waveScroll 9s linear infinite;
}
.wave-front{
opacity:.5;
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 480 120'><path d='M0,74 C 60,108 180,108 240,74 C 300,40 420,40 480,74 L480,120 L0,120 Z' fill='%2300AEEF'/></svg>");
animation:waveScroll 6s linear infinite reverse;
}
@keyframes waveScroll{
from{ background-position-x:0; }
to{ background-position-x:480px; }
}
.wave-crest-dot{
position:absolute;
bottom:78px;
width:6px; height:6px;
border-radius:50%;
background:var(--teal-400);
box-shadow:0 0 10px var(--teal-400);
animation:crestMove 3.2s ease-in-out infinite;
}
.wave-crest-dot.d2{ animation-delay:-1.6s; opacity:.6; }
@keyframes crestMove{
0% { left:6%; transform:translateY(0); }
50% { left:52%; transform:translateY(-10px); }
100% { left:6%; transform:translateY(0); }
}
/* ============ RIGHT PANEL ============ */
.right-panel{
width:42%;
display:flex;
justify-content:center;
align-items:center;
background:var(--slate-50);
}
.login-card{
width:400px;
padding:44px 40px;
background:#fff;
border:1px solid var(--line);
border-radius:16px;
box-shadow:0 20px 45px -20px rgba(7,27,46,.18);
position:relative;
overflow:hidden;
}
.login-card::before{
content:"";
position:absolute;
top:0;left:0;right:0;
height:4px;
background:linear-gradient(90deg,var(--teal-500),var(--navy-700));
}
.logo{
text-align:center;
margin-bottom:32px;
}
.logo-mark{
width:46px;height:46px;
margin:0 auto 16px;
border-radius:12px;
background:linear-gradient(135deg,var(--navy-800),var(--navy-950));
display:flex;
align-items:center;
justify-content:center;
}
.logo h2{
font-family:'Sora',sans-serif;
font-weight:700;
color:var(--navy-800);
font-size:24px;
letter-spacing:-.01em;
}
.logo p{
color:var(--ink-500);
margin-top:6px;
font-size:14px;
}
.input-box{
margin-bottom:20px;
}
.input-box label{
display:block;
margin-bottom:8px;
color:var(--ink-800);
font-weight:600;
font-size:13.5px;
}
.field{
position:relative;
display:flex;
align-items:center;
}
.field svg{
position:absolute;
left:14px;
width:18px;height:18px;
stroke:var(--ink-500);
pointer-events:none;
}
.field input{
width:100%;
padding:13px 14px 13px 42px;
border:1.5px solid var(--line);
border-radius:10px;
font-size:14.5px;
font-family:'Inter',sans-serif;
color:var(--ink-800);
transition:border-color .18s, box-shadow .18s;
}
.field input::placeholder{ color:#a7b3bd; }
.field input:focus{
outline:none;
border-color:var(--teal-500);
box-shadow:0 0 0 4px rgba(0,174,239,.14);
}
.toggle-pass{
position:absolute;
right:14px;
cursor:pointer;
width:18px;height:18px;
stroke:var(--ink-500);
background:none;
border:none;
padding:0;
}
button.login-btn{
width:100%;
padding:14px;
margin-top:6px;
background:linear-gradient(90deg,var(--navy-800),var(--navy-950));
color:#fff;
border:none;
border-radius:10px;
font-family:'Sora',sans-serif;
font-weight:700;
font-size:15px;
letter-spacing:.03em;
cursor:pointer;
transition:transform .15s, box-shadow .15s, background .3s;
box-shadow:0 10px 24px -10px rgba(7,27,46,.5);
}
button.login-btn:hover{
background:linear-gradient(90deg,var(--teal-500),var(--navy-800));
transform:translateY(-1px);
box-shadow:0 14px 28px -10px rgba(0,174,239,.45);
}
button.login-btn:active{
transform:translateY(0);
}
.footer{
text-align:center;
margin-top:22px;
color:var(--ink-500);
font-size:13.5px;
}
.footer a{
color:var(--teal-500);
font-weight:600;
text-decoration:none;
}
.footer a:hover{ text-decoration:underline; }
.error-box{
background:#FDECEA;
color:#C4342A;
border:1px solid #F5C2C7;
padding:12px 14px;
border-radius:10px;
margin-bottom:18px;
text-align:center;
font-weight:600;
font-size:13.5px;
}
@media(max-width:900px){
.left-panel{ display:none; }
.right-panel{ width:100%; }
}
</style>
</head>
<body>
<div class="left-panel">
<div class="left-content">
<span class="eyebrow">MIS PORTAL · SECURE ACCESS</span>
<div class="ticker-row">
<span class="ticker-label">Live focus</span>
<span class="ticker-text" id="tickerText">Production</span>
</div>
<h1>CRI Pumps Pvt Ltd</h1>
<h3>Management Information System</h3>
<p>
Welcome to the CRI Pumps MIS Portal. Access production, quality,
inventory, planning, maintenance and business reports securely
through a single platform.
</p>
<div class="modules" id="moduleChips">
<div class="module-chip active"><span class="dot"></span>PRODUCTION</div>
<div class="module-chip"><span class="dot"></span>QUALITY</div>
<div class="module-chip"><span class="dot"></span>INVENTORY</div>
<div class="module-chip"><span class="dot"></span>PLANNING</div>
<div class="module-chip"><span class="dot"></span>MAINTENANCE</div>
<div class="module-chip"><span class="dot"></span>AI INSIGHTS</div>
</div>
</div>
<!-- signature element: liquid wave, referencing pump/fluid dynamics -->
<div class="wave-wrap">
<div class="wave-layer wave-back"></div>
<div class="wave-layer wave-front"></div>
<div class="wave-crest-dot"></div>
<div class="wave-crest-dot d2"></div>
</div>
</div>
<div class="right-panel">
<div class="login-card">
<div class="logo">
<!-- <div class="logo-mark">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#3FC6F0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"/>
<path d="M12 7v5l3 3"/>
</svg>
</div> -->
<h2>MIS LOGIN</h2>
<p>Sign in to continue</p>
</div>
{% if messages %}
{% for message in messages %}
<div class="error-box">{{ message }}</div>
{% endfor %}
{% endif %}
<form method="POST">
{% csrf_token %}
<div class="input-box">
<label>Username</label>
<div class="field">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
<circle cx="12" cy="7" r="4"/>
</svg>
<input type="text" name="username" placeholder="Enter Username" autocomplete="username">
</div>
</div>
<div class="input-box">
<label>Password</label>
<div class="field">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="11" width="18" height="10" rx="2"/>
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
</svg>
<input id="password" type="password" name="password" placeholder="Enter Password" autocomplete="current-password">
<button type="button" class="toggle-pass" onclick="togglePassword()">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"
viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7"/>
<circle cx="12" cy="12" r="3"/>
</svg>
</button>
</div>
</div>
<button type="submit" class="login-btn">LOGIN</button>
<div class="footer">
Forgot Password? <a href="#">Click Here</a>
</div>
<div class="footer">
&copy; 2026 CRI Pumps Pvt. Ltd. All rights reserved<br>Made by <a href="https://www.cripumps.com" target="_blank">CRI</a>.
</div>
</form>
</div>
<script>
function togglePassword(){
const pw = document.getElementById('password');
pw.type = pw.type == 'password' ? 'text' : 'password';
}
// rotate the "live focus" word in sync with the module chips, every 1.5s
(function(){
const words = ['Production','Quality','Inventory','Planning','Maintenance', 'AI Insights'];
const tickerEl = document.getElementById('tickerText');
const chips = document.querySelectorAll('#moduleChips .module-chip');
let idx = 0;
setInterval(function(){
tickerEl.classList.add('swap');
setTimeout(function(){
idx = (idx + 1) % words.length;
tickerEl.textContent = words[idx];
tickerEl.classList.remove('swap');
chips.forEach(function(c){ c.classList.remove('active'); });
chips[idx].classList.add('active');
}, 220);
}, 1500);
})();
</script>
</body>
</html>

247
venv/bin/Activate.ps1 Normal file
View File

@@ -0,0 +1,247 @@
<#
.Synopsis
Activate a Python virtual environment for the current PowerShell session.
.Description
Pushes the python executable for a virtual environment to the front of the
$Env:PATH environment variable and sets the prompt to signify that you are
in a Python virtual environment. Makes use of the command line switches as
well as the `pyvenv.cfg` file values present in the virtual environment.
.Parameter VenvDir
Path to the directory that contains the virtual environment to activate. The
default value for this is the parent of the directory that the Activate.ps1
script is located within.
.Parameter Prompt
The prompt prefix to display when this virtual environment is activated. By
default, this prompt is the name of the virtual environment folder (VenvDir)
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
.Example
Activate.ps1
Activates the Python virtual environment that contains the Activate.ps1 script.
.Example
Activate.ps1 -Verbose
Activates the Python virtual environment that contains the Activate.ps1 script,
and shows extra information about the activation as it executes.
.Example
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
Activates the Python virtual environment located in the specified location.
.Example
Activate.ps1 -Prompt "MyPython"
Activates the Python virtual environment that contains the Activate.ps1 script,
and prefixes the current prompt with the specified string (surrounded in
parentheses) while the virtual environment is active.
.Notes
On Windows, it may be required to enable this Activate.ps1 script by setting the
execution policy for the user. You can do this by issuing the following PowerShell
command:
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
For more information on Execution Policies:
https://go.microsoft.com/fwlink/?LinkID=135170
#>
Param(
[Parameter(Mandatory = $false)]
[String]
$VenvDir,
[Parameter(Mandatory = $false)]
[String]
$Prompt
)
<# Function declarations --------------------------------------------------- #>
<#
.Synopsis
Remove all shell session elements added by the Activate script, including the
addition of the virtual environment's Python executable from the beginning of
the PATH variable.
.Parameter NonDestructive
If present, do not remove this function from the global namespace for the
session.
#>
function global:deactivate ([switch]$NonDestructive) {
# Revert to original values
# The prior prompt:
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
}
# The prior PYTHONHOME:
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
}
# The prior PATH:
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
}
# Just remove the VIRTUAL_ENV altogether:
if (Test-Path -Path Env:VIRTUAL_ENV) {
Remove-Item -Path env:VIRTUAL_ENV
}
# Just remove VIRTUAL_ENV_PROMPT altogether.
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
}
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
}
# Leave deactivate function in the global namespace if requested:
if (-not $NonDestructive) {
Remove-Item -Path function:deactivate
}
}
<#
.Description
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
given folder, and returns them in a map.
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
two strings separated by `=` (with any amount of whitespace surrounding the =)
then it is considered a `key = value` line. The left hand string is the key,
the right hand is the value.
If the value starts with a `'` or a `"` then the first and last character is
stripped from the value before being captured.
.Parameter ConfigDir
Path to the directory that contains the `pyvenv.cfg` file.
#>
function Get-PyVenvConfig(
[String]
$ConfigDir
) {
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
# An empty map will be returned if no config file is found.
$pyvenvConfig = @{ }
if ($pyvenvConfigPath) {
Write-Verbose "File exists, parse `key = value` lines"
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
$pyvenvConfigContent | ForEach-Object {
$keyval = $PSItem -split "\s*=\s*", 2
if ($keyval[0] -and $keyval[1]) {
$val = $keyval[1]
# Remove extraneous quotations around a string value.
if ("'""".Contains($val.Substring(0, 1))) {
$val = $val.Substring(1, $val.Length - 2)
}
$pyvenvConfig[$keyval[0]] = $val
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
}
}
}
return $pyvenvConfig
}
<# Begin Activate script --------------------------------------------------- #>
# Determine the containing directory of this script
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$VenvExecDir = Get-Item -Path $VenvExecPath
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
# Set values required in priority: CmdLine, ConfigFile, Default
# First, get the location of the virtual environment, it might not be
# VenvExecDir if specified on the command line.
if ($VenvDir) {
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
}
else {
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
Write-Verbose "VenvDir=$VenvDir"
}
# Next, read the `pyvenv.cfg` file to determine any required value such
# as `prompt`.
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
# Next, set the prompt from the command line, or the config file, or
# just use the name of the virtual environment folder.
if ($Prompt) {
Write-Verbose "Prompt specified as argument, using '$Prompt'"
}
else {
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
$Prompt = $pyvenvCfg['prompt'];
}
else {
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
$Prompt = Split-Path -Path $venvDir -Leaf
}
}
Write-Verbose "Prompt = '$Prompt'"
Write-Verbose "VenvDir='$VenvDir'"
# Deactivate any currently active virtual environment, but leave the
# deactivate function in place.
deactivate -nondestructive
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
# that there is an activated venv.
$env:VIRTUAL_ENV = $VenvDir
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
Write-Verbose "Setting prompt to '$Prompt'"
# Set the prompt to include the env name
# Make sure _OLD_VIRTUAL_PROMPT is global
function global:_OLD_VIRTUAL_PROMPT { "" }
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
function global:prompt {
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
_OLD_VIRTUAL_PROMPT
}
$env:VIRTUAL_ENV_PROMPT = $Prompt
}
# Clear PYTHONHOME
if (Test-Path -Path Env:PYTHONHOME) {
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
Remove-Item -Path Env:PYTHONHOME
}
# Add the venv to the PATH
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"

69
venv/bin/activate Normal file
View File

@@ -0,0 +1,69 @@
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
unset VIRTUAL_ENV_PROMPT
if [ ! "${1:-}" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}
# unset irrelevant variables
deactivate nondestructive
VIRTUAL_ENV=/root/projects/pds/oee/venv
export VIRTUAL_ENV
_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/"bin":$PATH"
export PATH
# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
unset PYTHONHOME
fi
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
PS1='(venv) '"${PS1:-}"
export PS1
VIRTUAL_ENV_PROMPT='(venv) '
export VIRTUAL_ENV_PROMPT
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r 2> /dev/null
fi

26
venv/bin/activate.csh Normal file
View File

@@ -0,0 +1,26 @@
# This file must be used with "source bin/activate.csh" *from csh*.
# You cannot run it directly.
# Created by Davide Di Blasi <davidedb@gmail.com>.
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
# Unset irrelevant variables.
deactivate nondestructive
setenv VIRTUAL_ENV /root/projects/pds/oee/venv
set _OLD_VIRTUAL_PATH="$PATH"
setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
set _OLD_VIRTUAL_PROMPT="$prompt"
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
set prompt = '(venv) '"$prompt"
setenv VIRTUAL_ENV_PROMPT '(venv) '
endif
alias pydoc python -m pydoc
rehash

69
venv/bin/activate.fish Normal file
View File

@@ -0,0 +1,69 @@
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
# (https://fishshell.com/); you cannot run it directly.
function deactivate -d "Exit virtual environment and return to normal shell environment"
# reset old environment variables
if test -n "$_OLD_VIRTUAL_PATH"
set -gx PATH $_OLD_VIRTUAL_PATH
set -e _OLD_VIRTUAL_PATH
end
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
set -e _OLD_VIRTUAL_PYTHONHOME
end
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
set -e _OLD_FISH_PROMPT_OVERRIDE
# prevents error when using nested fish instances (Issue #93858)
if functions -q _old_fish_prompt
functions -e fish_prompt
functions -c _old_fish_prompt fish_prompt
functions -e _old_fish_prompt
end
end
set -e VIRTUAL_ENV
set -e VIRTUAL_ENV_PROMPT
if test "$argv[1]" != "nondestructive"
# Self-destruct!
functions -e deactivate
end
end
# Unset irrelevant variables.
deactivate nondestructive
set -gx VIRTUAL_ENV /root/projects/pds/oee/venv
set -gx _OLD_VIRTUAL_PATH $PATH
set -gx PATH "$VIRTUAL_ENV/"bin $PATH
# Unset PYTHONHOME if set.
if set -q PYTHONHOME
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
set -e PYTHONHOME
end
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
# fish uses a function instead of an env var to generate the prompt.
# Save the current fish_prompt function as the function _old_fish_prompt.
functions -c fish_prompt _old_fish_prompt
# With the original prompt function renamed, we can override with our own.
function fish_prompt
# Save the return status of the last command.
set -l old_status $status
# Output the venv prompt; color taken from the blue of the Python logo.
printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal)
# Restore the return status of the previous command.
echo "exit $old_status" | .
# Output the original/"old" prompt.
_old_fish_prompt
end
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
set -gx VIRTUAL_ENV_PROMPT '(venv) '
end

8
venv/bin/django-admin Executable file
View File

@@ -0,0 +1,8 @@
#!/root/projects/pds/oee/venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from django.core.management import execute_from_command_line
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(execute_from_command_line())

8
venv/bin/dotenv Executable file
View File

@@ -0,0 +1,8 @@
#!/root/projects/pds/oee/venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from dotenv.__main__ import cli
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(cli())

8
venv/bin/pip Executable file
View File

@@ -0,0 +1,8 @@
#!/root/projects/pds/oee/venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal.cli.main import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

8
venv/bin/pip3 Executable file
View File

@@ -0,0 +1,8 @@
#!/root/projects/pds/oee/venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal.cli.main import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

8
venv/bin/pip3.10 Executable file
View File

@@ -0,0 +1,8 @@
#!/root/projects/pds/oee/venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal.cli.main import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

1
venv/bin/python Symbolic link
View File

@@ -0,0 +1 @@
python3

1
venv/bin/python3 Symbolic link
View File

@@ -0,0 +1 @@
/usr/bin/python3

1
venv/bin/python3.10 Symbolic link
View File

@@ -0,0 +1 @@
python3

8
venv/bin/sqlformat Executable file
View File

@@ -0,0 +1,8 @@
#!/root/projects/pds/oee/venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from sqlparse.__main__ import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -0,0 +1,132 @@
import sys
import os
import re
import importlib
import warnings
is_pypy = '__pypy__' in sys.builtin_module_names
warnings.filterwarnings('ignore',
r'.+ distutils\b.+ deprecated',
DeprecationWarning)
def warn_distutils_present():
if 'distutils' not in sys.modules:
return
if is_pypy and sys.version_info < (3, 7):
# PyPy for 3.6 unconditionally imports distutils, so bypass the warning
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
return
warnings.warn(
"Distutils was imported before Setuptools, but importing Setuptools "
"also replaces the `distutils` module in `sys.modules`. This may lead "
"to undesirable behaviors or errors. To avoid these issues, avoid "
"using distutils directly, ensure that setuptools is installed in the "
"traditional way (e.g. not an editable install), and/or make sure "
"that setuptools is always imported before distutils.")
def clear_distutils():
if 'distutils' not in sys.modules:
return
warnings.warn("Setuptools is replacing distutils.")
mods = [name for name in sys.modules if re.match(r'distutils\b', name)]
for name in mods:
del sys.modules[name]
def enabled():
"""
Allow selection of distutils by environment variable.
"""
which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib')
return which == 'local'
def ensure_local_distutils():
clear_distutils()
# With the DistutilsMetaFinder in place,
# perform an import to cause distutils to be
# loaded from setuptools._distutils. Ref #2906.
add_shim()
importlib.import_module('distutils')
remove_shim()
# check that submodules load as expected
core = importlib.import_module('distutils.core')
assert '_distutils' in core.__file__, core.__file__
def do_override():
"""
Ensure that the local copy of distutils is preferred over stdlib.
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
for more motivation.
"""
if enabled():
warn_distutils_present()
ensure_local_distutils()
class DistutilsMetaFinder:
def find_spec(self, fullname, path, target=None):
if path is not None:
return
method_name = 'spec_for_{fullname}'.format(**locals())
method = getattr(self, method_name, lambda: None)
return method()
def spec_for_distutils(self):
import importlib.abc
import importlib.util
class DistutilsLoader(importlib.abc.Loader):
def create_module(self, spec):
return importlib.import_module('setuptools._distutils')
def exec_module(self, module):
pass
return importlib.util.spec_from_loader('distutils', DistutilsLoader())
def spec_for_pip(self):
"""
Ensure stdlib distutils when running under pip.
See pypa/pip#8761 for rationale.
"""
if self.pip_imported_during_build():
return
clear_distutils()
self.spec_for_distutils = lambda: None
@staticmethod
def pip_imported_during_build():
"""
Detect if pip is being imported in a build script. Ref #2355.
"""
import traceback
return any(
frame.f_globals['__file__'].endswith('setup.py')
for frame, line in traceback.walk_stack(None)
)
DISTUTILS_FINDER = DistutilsMetaFinder()
def add_shim():
sys.meta_path.insert(0, DISTUTILS_FINDER)
def remove_shim():
try:
sys.meta_path.remove(DISTUTILS_FINDER)
except ValueError:
pass

View File

@@ -0,0 +1 @@
__import__('_distutils_hack').do_override()

View File

@@ -0,0 +1,247 @@
Metadata-Version: 2.4
Name: asgiref
Version: 3.11.1
Summary: ASGI specs, helper code, and adapters
Home-page: https://github.com/django/asgiref/
Author: Django Software Foundation
Author-email: foundation@djangoproject.com
License: BSD-3-Clause
Project-URL: Documentation, https://asgi.readthedocs.io/
Project-URL: Further Documentation, https://docs.djangoproject.com/en/stable/topics/async/#async-adapter-functions
Project-URL: Changelog, https://github.com/django/asgiref/blob/master/CHANGELOG.txt
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.9
License-File: LICENSE
Requires-Dist: typing_extensions>=4; python_version < "3.11"
Provides-Extra: tests
Requires-Dist: pytest; extra == "tests"
Requires-Dist: pytest-asyncio; extra == "tests"
Requires-Dist: mypy>=1.14.0; extra == "tests"
Dynamic: license-file
asgiref
=======
.. image:: https://github.com/django/asgiref/actions/workflows/tests.yml/badge.svg
:target: https://github.com/django/asgiref/actions/workflows/tests.yml
.. image:: https://img.shields.io/pypi/v/asgiref.svg
:target: https://pypi.python.org/pypi/asgiref
ASGI is a standard for Python asynchronous web apps and servers to communicate
with each other, and positioned as an asynchronous successor to WSGI. You can
read more at https://asgi.readthedocs.io/en/latest/
This package includes ASGI base libraries, such as:
* Sync-to-async and async-to-sync function wrappers, ``asgiref.sync``
* Server base classes, ``asgiref.server``
* A WSGI-to-ASGI adapter, in ``asgiref.wsgi``
Function wrappers
-----------------
These allow you to wrap or decorate async or sync functions to call them from
the other style (so you can call async functions from a synchronous thread,
or vice-versa).
In particular:
* AsyncToSync lets a synchronous subthread stop and wait while the async
function is called on the main thread's event loop, and then control is
returned to the thread when the async function is finished.
* SyncToAsync lets async code call a synchronous function, which is run in
a threadpool and control returned to the async coroutine when the synchronous
function completes.
The idea is to make it easier to call synchronous APIs from async code and
asynchronous APIs from synchronous code so it's easier to transition code from
one style to the other. In the case of Channels, we wrap the (synchronous)
Django view system with SyncToAsync to allow it to run inside the (asynchronous)
ASGI server.
Note that exactly what threads things run in is very specific, and aimed to
keep maximum compatibility with old synchronous code. See
"Synchronous code & Threads" below for a full explanation. By default,
``sync_to_async`` will run all synchronous code in the program in the same
thread for safety reasons; you can disable this for more performance with
``@sync_to_async(thread_sensitive=False)``, but make sure that your code does
not rely on anything bound to threads (like database connections) when you do.
Threadlocal replacement
-----------------------
This is a drop-in replacement for ``threading.local`` that works with both
threads and asyncio Tasks. Even better, it will proxy values through from a
task-local context to a thread-local context when you use ``sync_to_async``
to run things in a threadpool, and vice-versa for ``async_to_sync``.
If you instead want true thread- and task-safety, you can set
``thread_critical`` on the Local object to ensure this instead.
Server base classes
-------------------
Includes a ``StatelessServer`` class which provides all the hard work of
writing a stateless server (as in, does not handle direct incoming sockets
but instead consumes external streams or sockets to work out what is happening).
An example of such a server would be a chatbot server that connects out to
a central chat server and provides a "connection scope" per user chatting to
it. There's only one actual connection, but the server has to separate things
into several scopes for easier writing of the code.
You can see an example of this being used in `frequensgi <https://github.com/andrewgodwin/frequensgi>`_.
WSGI-to-ASGI adapter
--------------------
Allows you to wrap a WSGI application so it appears as a valid ASGI application.
Simply wrap it around your WSGI application like so::
asgi_application = WsgiToAsgi(wsgi_application)
The WSGI application will be run in a synchronous threadpool, and the wrapped
ASGI application will be one that accepts ``http`` class messages.
Please note that not all extended features of WSGI may be supported (such as
file handles for incoming POST bodies).
Dependencies
------------
``asgiref`` requires Python 3.9 or higher.
Contributing
------------
Please refer to the
`main Channels contributing docs <https://github.com/django/channels/blob/master/CONTRIBUTING.rst>`_.
Testing
'''''''
To run tests, make sure you have installed the ``tests`` extra with the package::
cd asgiref/
pip install -e .[tests]
pytest
Building the documentation
''''''''''''''''''''''''''
The documentation uses `Sphinx <http://www.sphinx-doc.org>`_::
cd asgiref/docs/
pip install sphinx
To build the docs, you can use the default tools::
sphinx-build -b html . _build/html # or `make html`, if you've got make set up
cd _build/html
python -m http.server
...or you can use ``sphinx-autobuild`` to run a server and rebuild/reload
your documentation changes automatically::
pip install sphinx-autobuild
sphinx-autobuild . _build/html
Releasing
'''''''''
To release, first add details to CHANGELOG.txt and update the version number in ``asgiref/__init__.py``.
Then, build and push the packages::
python -m build
twine upload dist/*
rm -r asgiref.egg-info dist
Implementation Details
----------------------
Synchronous code & threads
''''''''''''''''''''''''''
The ``asgiref.sync`` module provides two wrappers that let you go between
asynchronous and synchronous code at will, while taking care of the rough edges
for you.
Unfortunately, the rough edges are numerous, and the code has to work especially
hard to keep things in the same thread as much as possible. Notably, the
restrictions we are working with are:
* All synchronous code called through ``SyncToAsync`` and marked with
``thread_sensitive`` should run in the same thread as each other (and if the
outer layer of the program is synchronous, the main thread)
* If a thread already has a running async loop, ``AsyncToSync`` can't run things
on that loop if it's blocked on synchronous code that is above you in the
call stack.
The first compromise you get to might be that ``thread_sensitive`` code should
just run in the same thread and not spawn in a sub-thread, fulfilling the first
restriction, but that immediately runs you into the second restriction.
The only real solution is to essentially have a variant of ThreadPoolExecutor
that executes any ``thread_sensitive`` code on the outermost synchronous
thread - either the main thread, or a single spawned subthread.
This means you now have two basic states:
* If the outermost layer of your program is synchronous, then all async code
run through ``AsyncToSync`` will run in a per-call event loop in arbitrary
sub-threads, while all ``thread_sensitive`` code will run in the main thread.
* If the outermost layer of your program is asynchronous, then all async code
runs on the main thread's event loop, and all ``thread_sensitive`` synchronous
code will run in a single shared sub-thread.
Crucially, this means that in both cases there is a thread which is a shared
resource that all ``thread_sensitive`` code must run on, and there is a chance
that this thread is currently blocked on its own ``AsyncToSync`` call. Thus,
``AsyncToSync`` needs to act as an executor for thread code while it's blocking.
The ``CurrentThreadExecutor`` class provides this functionality; rather than
simply waiting on a Future, you can call its ``run_until_future`` method and
it will run submitted code until that Future is done. This means that code
inside the call can then run code on your thread.
Maintenance and Security
------------------------
To report security issues, please contact security@djangoproject.com. For GPG
signatures and more security process information, see
https://docs.djangoproject.com/en/dev/internals/security/.
To report bugs or request new features, please open a new GitHub issue.
This repository is part of the Channels project. For the shepherd and maintenance team, please see the
`main Channels readme <https://github.com/django/channels/blob/master/README.rst>`_.

View File

@@ -0,0 +1,27 @@
asgiref-3.11.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
asgiref-3.11.1.dist-info/METADATA,sha256=aLePcJP6N7HpneCbacTmO_4Gc4b0PRSo2fWPwsWLeYQ,9287
asgiref-3.11.1.dist-info/RECORD,,
asgiref-3.11.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
asgiref-3.11.1.dist-info/licenses/LICENSE,sha256=uEZBXRtRTpwd_xSiLeuQbXlLxUbKYSn5UKGM0JHipmk,1552
asgiref-3.11.1.dist-info/top_level.txt,sha256=bokQjCzwwERhdBiPdvYEZa4cHxT4NCeAffQNUqJ8ssg,8
asgiref/__init__.py,sha256=UuKR3QjWk9sJtn2huDFsPdSgtx1CgiEl2B4BUtVRd58,23
asgiref/__pycache__/__init__.cpython-310.pyc,,
asgiref/__pycache__/compatibility.cpython-310.pyc,,
asgiref/__pycache__/current_thread_executor.cpython-310.pyc,,
asgiref/__pycache__/local.cpython-310.pyc,,
asgiref/__pycache__/server.cpython-310.pyc,,
asgiref/__pycache__/sync.cpython-310.pyc,,
asgiref/__pycache__/testing.cpython-310.pyc,,
asgiref/__pycache__/timeout.cpython-310.pyc,,
asgiref/__pycache__/typing.cpython-310.pyc,,
asgiref/__pycache__/wsgi.cpython-310.pyc,,
asgiref/compatibility.py,sha256=DhY1SOpOvOw0Y1lSEjCqg-znRUQKecG3LTaV48MZi68,1606
asgiref/current_thread_executor.py,sha256=42CU1VODLTk-_PYise-cP1XgyAvI5Djc8f97owFzdrs,4157
asgiref/local.py,sha256=ZZeWWIXptVU4GbNApMMWQ-skuglvodcQA5WpzJDMxh4,4912
asgiref/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
asgiref/server.py,sha256=3A68169Nuh2sTY_2O5JzRd_opKObWvvrEFcrXssq3kA,6311
asgiref/sync.py,sha256=KHjSkYRZKIJ1cmBvxcloGrcnhvAuEfXCWCZAoxN8grg,22929
asgiref/testing.py,sha256=U5wcs_-ZYTO5SIGfl80EqRAGv_T8BHrAhvAKRuuztT4,4421
asgiref/timeout.py,sha256=LtGL-xQpG8JHprdsEUCMErJ0kNWj4qwWZhEHJ3iKu4s,3627
asgiref/typing.py,sha256=Zi72AZlOyF1C7N14LLZnpAdfUH4ljoBqFdQo_bBKMq0,6290
asgiref/wsgi.py,sha256=OSxanm5Qf-VfrOkAx62mYW0mOnh8pwaTz1099HbJzM0,7941

View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: setuptools (80.10.2)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -0,0 +1,27 @@
Copyright (c) Django Software Foundation and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Django nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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