Initial commit
This commit is contained in:
5
.env
Normal file
5
.env
Normal 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
0
accounts/__init__.py
Normal file
BIN
accounts/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
accounts/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
accounts/__pycache__/admin.cpython-310.pyc
Normal file
BIN
accounts/__pycache__/admin.cpython-310.pyc
Normal file
Binary file not shown.
BIN
accounts/__pycache__/apps.cpython-310.pyc
Normal file
BIN
accounts/__pycache__/apps.cpython-310.pyc
Normal file
Binary file not shown.
BIN
accounts/__pycache__/models.cpython-310.pyc
Normal file
BIN
accounts/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
accounts/__pycache__/urls.cpython-310.pyc
Normal file
BIN
accounts/__pycache__/urls.cpython-310.pyc
Normal file
Binary file not shown.
BIN
accounts/__pycache__/utils.cpython-310.pyc
Normal file
BIN
accounts/__pycache__/utils.cpython-310.pyc
Normal file
Binary file not shown.
BIN
accounts/__pycache__/views.cpython-310.pyc
Normal file
BIN
accounts/__pycache__/views.cpython-310.pyc
Normal file
Binary file not shown.
3
accounts/admin.py
Normal file
3
accounts/admin.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
accounts/apps.py
Normal file
6
accounts/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AccountsConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'accounts'
|
||||||
0
accounts/migrations/__init__.py
Normal file
0
accounts/migrations/__init__.py
Normal file
BIN
accounts/migrations/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
accounts/migrations/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
110
accounts/models.py
Normal file
110
accounts/models.py
Normal 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
3
accounts/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
9
accounts/urls.py
Normal file
9
accounts/urls.py
Normal 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
27
accounts/utils.py
Normal 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
224
accounts/views.py
Normal 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
0
dashboard/__init__.py
Normal file
BIN
dashboard/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
dashboard/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
dashboard/__pycache__/admin.cpython-310.pyc
Normal file
BIN
dashboard/__pycache__/admin.cpython-310.pyc
Normal file
Binary file not shown.
BIN
dashboard/__pycache__/apps.cpython-310.pyc
Normal file
BIN
dashboard/__pycache__/apps.cpython-310.pyc
Normal file
Binary file not shown.
BIN
dashboard/__pycache__/models.cpython-310.pyc
Normal file
BIN
dashboard/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
dashboard/__pycache__/urls.cpython-310.pyc
Normal file
BIN
dashboard/__pycache__/urls.cpython-310.pyc
Normal file
Binary file not shown.
BIN
dashboard/__pycache__/views.cpython-310.pyc
Normal file
BIN
dashboard/__pycache__/views.cpython-310.pyc
Normal file
Binary file not shown.
3
dashboard/admin.py
Normal file
3
dashboard/admin.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
dashboard/apps.py
Normal file
6
dashboard/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'dashboard'
|
||||||
0
dashboard/migrations/__init__.py
Normal file
0
dashboard/migrations/__init__.py
Normal file
BIN
dashboard/migrations/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
dashboard/migrations/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
3
dashboard/models.py
Normal file
3
dashboard/models.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
3
dashboard/tests.py
Normal file
3
dashboard/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
6
dashboard/urls.py
Normal file
6
dashboard/urls.py
Normal 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
6
dashboard/views.py
Normal 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
0
departments/__init__.py
Normal file
BIN
departments/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
departments/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
departments/__pycache__/admin.cpython-310.pyc
Normal file
BIN
departments/__pycache__/admin.cpython-310.pyc
Normal file
Binary file not shown.
BIN
departments/__pycache__/apps.cpython-310.pyc
Normal file
BIN
departments/__pycache__/apps.cpython-310.pyc
Normal file
Binary file not shown.
BIN
departments/__pycache__/models.cpython-310.pyc
Normal file
BIN
departments/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
departments/__pycache__/urls.cpython-310.pyc
Normal file
BIN
departments/__pycache__/urls.cpython-310.pyc
Normal file
Binary file not shown.
BIN
departments/__pycache__/views.cpython-310.pyc
Normal file
BIN
departments/__pycache__/views.cpython-310.pyc
Normal file
Binary file not shown.
3
departments/admin.py
Normal file
3
departments/admin.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
departments/apps.py
Normal file
6
departments/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class DepartmentsConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'departments'
|
||||||
0
departments/migrations/__init__.py
Normal file
0
departments/migrations/__init__.py
Normal file
BIN
departments/migrations/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
departments/migrations/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
3
departments/models.py
Normal file
3
departments/models.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
3
departments/tests.py
Normal file
3
departments/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
6
departments/urls.py
Normal file
6
departments/urls.py
Normal 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
7
departments/views.py
Normal 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
0
employees/__init__.py
Normal file
BIN
employees/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
employees/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
employees/__pycache__/admin.cpython-310.pyc
Normal file
BIN
employees/__pycache__/admin.cpython-310.pyc
Normal file
Binary file not shown.
BIN
employees/__pycache__/apps.cpython-310.pyc
Normal file
BIN
employees/__pycache__/apps.cpython-310.pyc
Normal file
Binary file not shown.
BIN
employees/__pycache__/models.cpython-310.pyc
Normal file
BIN
employees/__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
employees/__pycache__/urls.cpython-310.pyc
Normal file
BIN
employees/__pycache__/urls.cpython-310.pyc
Normal file
Binary file not shown.
BIN
employees/__pycache__/views.cpython-310.pyc
Normal file
BIN
employees/__pycache__/views.cpython-310.pyc
Normal file
Binary file not shown.
3
employees/admin.py
Normal file
3
employees/admin.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
employees/apps.py
Normal file
6
employees/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeesConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'employees'
|
||||||
0
employees/migrations/__init__.py
Normal file
0
employees/migrations/__init__.py
Normal file
BIN
employees/migrations/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
employees/migrations/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
3
employees/models.py
Normal file
3
employees/models.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
3
employees/tests.py
Normal file
3
employees/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
6
employees/urls.py
Normal file
6
employees/urls.py
Normal 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
7
employees/views.py
Normal 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
22
manage.py
Executable 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
0
oee/__init__.py
Normal file
BIN
oee/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
oee/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
oee/__pycache__/settings.cpython-310.pyc
Normal file
BIN
oee/__pycache__/settings.cpython-310.pyc
Normal file
Binary file not shown.
BIN
oee/__pycache__/urls.cpython-310.pyc
Normal file
BIN
oee/__pycache__/urls.cpython-310.pyc
Normal file
Binary file not shown.
BIN
oee/__pycache__/wsgi.cpython-310.pyc
Normal file
BIN
oee/__pycache__/wsgi.cpython-310.pyc
Normal file
Binary file not shown.
16
oee/asgi.py
Normal file
16
oee/asgi.py
Normal 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
165
oee/settings.py
Normal 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
28
oee/urls.py
Normal 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
16
oee/wsgi.py
Normal 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
5
requirements.txt
Normal 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
BIN
static/images/motor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
1343
templates/dashboard.html
Normal file
1343
templates/dashboard.html
Normal file
File diff suppressed because it is too large
Load Diff
556
templates/login.html
Normal file
556
templates/login.html
Normal 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">
|
||||||
|
© 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
247
venv/bin/Activate.ps1
Normal 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
69
venv/bin/activate
Normal 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
26
venv/bin/activate.csh
Normal 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
69
venv/bin/activate.fish
Normal 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
8
venv/bin/django-admin
Executable 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
8
venv/bin/dotenv
Executable 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
8
venv/bin/pip
Executable 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
8
venv/bin/pip3
Executable 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
8
venv/bin/pip3.10
Executable 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
1
venv/bin/python
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
python3
|
||||||
1
venv/bin/python3
Symbolic link
1
venv/bin/python3
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/usr/bin/python3
|
||||||
1
venv/bin/python3.10
Symbolic link
1
venv/bin/python3.10
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
python3
|
||||||
8
venv/bin/sqlformat
Executable file
8
venv/bin/sqlformat
Executable 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())
|
||||||
Binary file not shown.
132
venv/lib/python3.10/site-packages/_distutils_hack/__init__.py
Normal file
132
venv/lib/python3.10/site-packages/_distutils_hack/__init__.py
Normal 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
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
__import__('_distutils_hack').do_override()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pip
|
||||||
@@ -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>`_.
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: setuptools (80.10.2)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
||||||
@@ -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
Reference in New Issue
Block a user