Pregatire pentru productie si adaugare whitenoise

This commit is contained in:
2026-09-07 14:44:20 +03:00
parent 3649b0126d
commit 13b86106ce
8 changed files with 142 additions and 25 deletions
+4
View File
@@ -46,3 +46,7 @@ docker compose exec web python manage.py migrate
docker compose exec web python manage.py makemigrations equipment docker compose exec web python manage.py makemigrations equipment
docker compose exec web python manage.py migrate docker compose exec web python manage.py migrate
docker compose exec web python manage.py import_access
docker compose exec web python manage.py clear_database
+7 -7
View File
@@ -4,15 +4,15 @@ services:
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - postgres_data:/var/lib/postgresql/data
environment: environment:
- POSTGRES_DB=djangodb - POSTGRES_DB=eumi-db
- POSTGRES_USER=djangouser - POSTGRES_USER=eumi-user
- POSTGRES_PASSWORD=djangopass - POSTGRES_PASSWORD=kBGedy4ScgI7g
pgadmin: pgadmin:
image: dpage/pgadmin4 image: dpage/pgadmin4
environment: environment:
- PGADMIN_DEFAULT_EMAIL=catalin@x3c.ro - PGADMIN_DEFAULT_EMAIL=catalin@x3c.ro
- PGADMIN_DEFAULT_PASSWORD=Parola123 - PGADMIN_DEFAULT_PASSWORD=CFratxlTiJ0TB
ports: ports:
- "5050:80" - "5050:80"
depends_on: depends_on:
@@ -26,9 +26,9 @@ services:
ports: ports:
- "8000:8000" - "8000:8000"
environment: environment:
- DB_NAME=djangodb - DB_NAME=eumi-db
- DB_USER=djangouser - DB_USER=eumi-user
- DB_PASSWORD=djangopass - DB_PASSWORD=kBGedy4ScgI7g
- DB_HOST=db - DB_HOST=db
- DB_PORT=5432 - DB_PORT=5432
depends_on: depends_on:
@@ -0,0 +1,47 @@
# management/commands/clear_database.py
from django.core.management.base import BaseCommand
from django.db import connection
from users.models import Employee
from equipment.models import Computer, Monitor, Peripheral
class Command(BaseCommand):
help = "Clear all data from equipment and users tables"
def add_arguments(self, parser):
parser.add_argument(
"--force",
action="store_true",
help="Skip confirmation prompt",
)
def handle(self, *args, **options):
if not options.get("force"):
confirm = input(
"This will DELETE ALL data from Employee, Computer, Monitor, and Peripheral tables. Are you sure? (y/n): "
)
if confirm.lower() != "y":
self.stdout.write("Operation cancelled.")
return
# Option A: Using Django's delete() - slower but triggers signals
Employee.objects.all().delete()
Computer.objects.all().delete()
Monitor.objects.all().delete()
Peripheral.objects.all().delete()
# Option B: Using raw SQL TRUNCATE - faster, resets sequences
with connection.cursor() as cursor:
# Truncate in correct order to respect foreign key constraints
cursor.execute("TRUNCATE TABLE equipment_peripheral CASCADE;")
cursor.execute("TRUNCATE TABLE equipment_monitor CASCADE;")
cursor.execute("TRUNCATE TABLE equipment_computer CASCADE;")
cursor.execute("TRUNCATE TABLE users_employee CASCADE;")
# Reset auto-increment sequences (if using serial/identity columns)
cursor.execute("ALTER SEQUENCE users_employee_id_seq RESTART WITH 1;")
cursor.execute("ALTER SEQUENCE equipment_computer_id_seq RESTART WITH 1;")
cursor.execute("ALTER SEQUENCE equipment_monitor_id_seq RESTART WITH 1;")
cursor.execute("ALTER SEQUENCE equipment_peripheral_id_seq RESTART WITH 1;")
self.stdout.write(self.style.SUCCESS("Database cleared successfully!"))
+63 -4
View File
@@ -18,6 +18,31 @@ class Command(BaseCommand):
# Funcție ajutătoare pentru a evita erorile la date calendaristice goale # Funcție ajutătoare pentru a evita erorile la date calendaristice goale
# Funcție ajutătoare pentru conversia datelor din DD-MM-YYYY în YYYY-MM-DD # Funcție ajutătoare pentru conversia datelor din DD-MM-YYYY în YYYY-MM-DD
# def parse_date(date_str):
# # Definim data de rezervă dacă rubrică e goală
# fallback_date = "2000-01-01"
# if not date_str or str(date_str).strip() == "":
# return fallback_date
# clean_str = str(date_str).strip()
# try:
# parsed = datetime.strptime(clean_str, "%d-%m-%Y")
# return parsed.strftime("%Y-%m-%d")
# except ValueError:
# try:
# parsed = datetime.strptime(clean_str, "%d/%m/%Y")
# return parsed.strftime("%Y-%m-%d")
# except ValueError:
# # Dacă data există dar formatul e complet invalid, punem tot fallback
# self.stdout.write(
# self.style.WARNING(
# f"Atenție: Data '{clean_str}' e invalidă. Folosim data de azi."
# )
# )
# return fallback_date
def parse_date(date_str): def parse_date(date_str):
# Definim data de rezervă dacă rubrică e goală # Definim data de rezervă dacă rubrică e goală
fallback_date = "2000-01-01" fallback_date = "2000-01-01"
@@ -27,18 +52,52 @@ class Command(BaseCommand):
clean_str = str(date_str).strip() clean_str = str(date_str).strip()
# Elimină partea de timp (0:00:00) dacă există
if " " in clean_str:
clean_str = clean_str.split(" ")[0]
# Încearcă să parseze formatul DD-M-YYYY (cu sau fără leading zeros)
try: try:
# Split după '-'
parts = clean_str.split("-")
if len(parts) == 3:
day = parts[0].zfill(2) # Adaugă zero în față dacă e necesar
month = parts[1].zfill(2) # Adaugă zero în față dacă e necesar
year = parts[2]
# Verifică dacă anul are 4 cifre
if len(year) == 2:
year = "20" + year
clean_str = f"{day}-{month}-{year}"
parsed = datetime.strptime(clean_str, "%d-%m-%Y") parsed = datetime.strptime(clean_str, "%d-%m-%Y")
return parsed.strftime("%Y-%m-%d") return parsed.strftime("%Y-%m-%d")
except ValueError: except (ValueError, IndexError):
pass
# Încearcă și cu slash ca separator
try: try:
parts = clean_str.split("/")
if len(parts) == 3:
day = parts[0].zfill(2)
month = parts[1].zfill(2)
year = parts[2]
if len(year) == 2:
year = "20" + year
clean_str = f"{day}/{month}/{year}"
parsed = datetime.strptime(clean_str, "%d/%m/%Y") parsed = datetime.strptime(clean_str, "%d/%m/%Y")
return parsed.strftime("%Y-%m-%d") return parsed.strftime("%Y-%m-%d")
except ValueError: except (ValueError, IndexError):
# Dacă data există dar formatul e complet invalid, punem tot fallback pass
# Dacă data există dar formatul e complet invalid, punem fallback
self.stdout.write( self.stdout.write(
self.style.WARNING( self.style.WARNING(
f"Atenție: Data '{clean_str}' e invalidă. Folosim data de azi." f"Atenție: Data '{date_str}' e invalidă. Folosim {fallback_date}."
) )
) )
return fallback_date return fallback_date
+3 -1
View File
@@ -17,7 +17,9 @@
</div> </div>
<!-- Am adăugat înălțime fixă (max-height) și overflow-y pentru scroll --> <!-- Am adăugat înălțime fixă (max-height) și overflow-y pentru scroll -->
<div class="card-body" style="max-height: 450px; overflow-y: auto;"> <div class="card-body" style="max-height: 450px; overflow-y: auto;">
<p class="mb-2">Total Calculatoare: <strong>{{ total_computers }}</strong></p> <p class="mb-1">Total Calculatoare: <strong>{{ total_computers }}</strong></p>
<p class="mb-1">Total Monitoare: <strong>{{ total_monitors }}</strong></p>
<p class="mb-0">Total Periferice: <strong>{{ total_peripherals }}</strong></p>
<hr> <hr>
<h6 class="fw-bold text-danger mt-3">Calculatoare Neasignate:</h6> <h6 class="fw-bold text-danger mt-3">Calculatoare Neasignate:</h6>
<ul class="list-unstyled mb-3"> <ul class="list-unstyled mb-3">
+5
View File
@@ -557,6 +557,9 @@ def handover_protocol_form(request, pk=None):
def dashboard_view(request): def dashboard_view(request):
# --- Coloana 1: Sumar Inventar (Înlocuitor temporar pt. Tichete) --- # --- Coloana 1: Sumar Inventar (Înlocuitor temporar pt. Tichete) ---
total_computers = Computer.objects.count() total_computers = Computer.objects.count()
total_monitors = Monitor.objects.count()
total_peripherals = Peripheral.objects.count()
unassigned_computers = Computer.objects.filter(assigned_to__isnull=True) unassigned_computers = Computer.objects.filter(assigned_to__isnull=True)
unassigned_monitors = Monitor.objects.filter(assigned_to__isnull=True) unassigned_monitors = Monitor.objects.filter(assigned_to__isnull=True)
@@ -578,6 +581,8 @@ def dashboard_view(request):
context = { context = {
"total_computers": total_computers, "total_computers": total_computers,
"total_monitors": total_monitors,
"total_peripherals": total_peripherals,
"unassigned_computers": unassigned_computers, "unassigned_computers": unassigned_computers,
"unassigned_monitors": unassigned_monitors, "unassigned_monitors": unassigned_monitors,
"activity_log": activity_log, "activity_log": activity_log,
+4 -5
View File
@@ -24,9 +24,9 @@ BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = "django-insecure-z(p$%wh6x^_9!1cgt%!s$w8k39(fox-^!iujxai42_wncja4%s" SECRET_KEY = "django-insecure-z(p$%wh6x^_9!1cgt%!s$w8k39(fox-^!iujxai42_wncja4%s"
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = False
ALLOWED_HOSTS = ["localhost", "127.0.0.1", "0.0.0.0"] ALLOWED_HOSTS = ["192.168.100.142", "eumi.local", "localhost", "127.0.0.1", "0.0.0.0"]
# Application definition # Application definition
@@ -48,6 +48,7 @@ INSTALLED_APPS = [
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware", "django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware",
@@ -128,9 +129,7 @@ USE_TZ = True
# https://docs.djangoproject.com/en/6.1/howto/static-files/ # https://docs.djangoproject.com/en/6.1/howto/static-files/
STATIC_URL = "static/" STATIC_URL = "static/"
STATICFILES_DIRS = [ STATIC_ROOT = BASE_DIR / "staticfiles"
BASE_DIR / "static",
]
# Email # Email
+1
View File
@@ -4,3 +4,4 @@ psycopg2-binary==2.9.12
sqlparse==0.6.0 sqlparse==0.6.0
django-simple-history django-simple-history
xhtml2pdf>=0.2.11 xhtml2pdf>=0.2.11
whitenoise