This commit is contained in:
Faheed 2025-09-15 17:39:24 +03:00
commit c90f8edc5a
4 changed files with 509 additions and 248 deletions

View File

@ -25,6 +25,7 @@ from api import routing
from inventory.notifications.sse import NotificationSSEApp
from django.urls import re_path
from django.core.asgi import get_asgi_application
from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler
# application = ProtocolTypeRouter(
# {
@ -32,6 +33,9 @@ from django.core.asgi import get_asgi_application
# # "websocket": AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns)),
# }
# )
app = get_asgi_application()
application = ProtocolTypeRouter(
{
"http": AuthMiddlewareStack(
@ -39,10 +43,13 @@ application = ProtocolTypeRouter(
[
path("sse/notifications/", NotificationSSEApp()),
re_path(
r"", get_asgi_application()
r"", app
), # All other routes go to Django
]
)
),
}
)
if django.conf.settings.DEBUG:
application = ASGIStaticFilesHandler(app)

View File

@ -1220,11 +1220,11 @@ urlpatterns = [
views.permenant_delete_account,
name="permenant_delete_account",
),
path(
"<slug:dealer_slug>/management/audit_log_dashboard/",
views.AuditLogDashboardView,
name="audit_log_dashboard",
),
# path(
# "<slug:dealer_slug>/management/audit_log_dashboard/",
# views.AuditLogDashboardView,
# name="audit_log_dashboard",
# ),
#########
# Purchase Order
path(

View File

@ -1909,23 +1909,22 @@ def create_make_accounts(dealer):
)
def handle_payment(request, order):
def handle_payment(request, dealer):
url = "https://api.moyasar.com/v1/payments"
callback_url = request.build_absolute_uri(
reverse("payment_callback", kwargs={"dealer_slug": request.dealer.slug})
reverse("payment_callback", kwargs={"dealer_slug": dealer.slug})
)
if request.user.is_authenticated:
email = request.POST["email"]
first_name = request.POST["first_name"]
last_name = request.POST["last_name"]
phone = request.POST["phone"]
card_name = request.POST["card_name"]
card_number = str(request.POST["card_number"]).replace(" ", "").strip()
month = int(request.POST["card_expiry"].split("/")[0].strip())
year = int(request.POST["card_expiry"].split("/")[1].strip())
cvv = request.POST["card_cvv"]
email = request.POST.get("email")
first_name = request.POST.get("first_name")
last_name = request.POST.get("last_name")
phone = request.POST.get("phone")
card_name = request.POST.get("card_name")
card_number = str(request.POST.get("card_number", "")).replace(" ", "").strip()
expiry = request.POST.get("card_expiry", "").split("/")
month = int(expiry[0].strip()) if len(expiry) > 0 else 0
year = int(expiry[1].strip()) if len(expiry) > 1 else 0
cvv = request.POST.get("card_cvv")
user_data = {
"email": email,
@ -1933,15 +1932,30 @@ def handle_payment(request, order):
"last_name": last_name,
"phone": phone,
}
try:
total = int((order.total() + order.tax * order.total() / 100) * 100)
except (AttributeError, TypeError):
raise ValueError("Order total or tax is invalid")
payload = json.dumps(
{
# Get selected plan from session
selected_plan_id = request.session.get('pending_plan_id')
if not selected_plan_id:
raise ValueError("No pending plan found in session")
from plans.models import PlanPricing
pp = PlanPricing.objects.get(pk=selected_plan_id)
# Calculate amount without creating order
amount_sar = pp.price # assuming price is in SAR
tax_amount = amount_sar * 15 / 100
total = int((amount_sar + tax_amount) * 100) # convert to halalas
# Pass plan & dealer info via metadata
metadata = {
**user_data,
"plan_pricing_id": selected_plan_id,
"dealer_slug": dealer.slug,
}
payload = json.dumps({
"amount": total,
"currency": "SAR",
"description": f"payment issued for {email}",
"description": f"Payment for plan {pp.plan.name}",
"callback_url": callback_url,
"source": {
"type": "creditcard",
@ -1955,43 +1969,129 @@ def handle_payment(request, order):
"manual": False,
"save_card": False,
},
"metadata": user_data,
}
)
"metadata": metadata,
})
headers = {"Content-Type": "application/json", "Accept": "application/json"}
auth = (settings.MOYASAR_SECRET_KEY, "")
response = requests.request("POST", url, auth=auth, headers=headers, data=payload)
if response.status_code == 400:
data = response.json()
if data["type"] == "validation_error":
errors = data.get("errors", {})
if "source.year" in errors:
raise Exception("Invalid expiry year")
else:
raise Exception("Validation Error: ", errors)
else:
print("Failed to process payment:", data)
#
response = requests.post(url, auth=auth, headers=headers, data=payload)
if response.status_code != 201:
logger.error(f"Payment initiation failed: {response.text}")
return None, response.get("message")
data = response.json()
# order.status = AbstractOrder.STATUS.NEW
order.save()
#
amount = Decimal("{0:.2f}".format(Decimal(total) / Decimal(100)))
# Create PaymentHistory WITHOUT linking to order (since order doesn't exist yet)
amount_decimal = Decimal("{0:.2f}".format(Decimal(total) / Decimal(100)))
models.PaymentHistory.objects.create(
user=request.user,
user_data=user_data,
amount=amount,
user_data=json.dumps(metadata),
amount=amount_decimal,
currency=data["currency"],
status=data["status"],
transaction_id=data["id"],
payment_date=data["created_at"],
gateway_response=data,
)
transaction_url = data["source"]["transaction_url"]
return transaction_url
logger.info(f"Payment initiated: {data}")
return data["source"]["transaction_url"],None
# def handle_payment(request, order):
# logger.info(f"Handling payment for order {order}")
# url = "https://api.moyasar.com/v1/payments"
# callback_url = request.build_absolute_uri(
# reverse("payment_callback", kwargs={"dealer_slug": request.dealer.slug})
# )
# logger.info(f"Got callback_url: {callback_url}")
# if request.user.is_authenticated:
# email = request.POST["email"]
# first_name = request.POST["first_name"]
# last_name = request.POST["last_name"]
# phone = request.POST["phone"]
# logger.info(f"Got user data: {email}, {first_name}, {last_name}, {phone}")
# card_name = request.POST["card_name"]
# card_number = str(request.POST["card_number"]).replace(" ", "").strip()
# month = int(request.POST["card_expiry"].split("/")[0].strip())
# year = int(request.POST["card_expiry"].split("/")[1].strip())
# cvv = request.POST["card_cvv"]
# logger.info(f"Got card data: {card_name}, {card_number[:4]}****, {month}, {year}, {cvv}")
# user_data = {
# "email": email,
# "first_name": first_name,
# "last_name": last_name,
# "phone": phone,
# }
# try:
# total = int((order.total() + order.tax * order.total() / 100) * 100)
# except (AttributeError, TypeError):
# raise ValueError("Order total or tax is invalid")
# logger.info(f"Calculated total: {total}")
# payload = json.dumps(
# {
# "amount": total,
# "currency": "SAR",
# "description": f"payment issued for {email}",
# "callback_url": callback_url,
# "source": {
# "type": "creditcard",
# "name": card_name,
# "number": card_number,
# "month": month,
# "year": year,
# "cvc": cvv,
# "statement_descriptor": "Century Store",
# "3ds": False,
# "manual": False,
# "save_card": False,
# },
# "metadata": user_data,
# }
# )
# logger.info(f"Generated payload: {payload}")
# headers = {"Content-Type": "application/json", "Accept": "application/json"}
# auth = (settings.MOYASAR_SECRET_KEY, "")
# response = requests.request("POST", url, auth=auth, headers=headers, data=payload)
# logger.info(f"Sent request to Moyasar API: {response.status_code}")
# if response.status_code == 400:
# data = response.json()
# if data["type"] == "validation_error":
# errors = data.get("errors", {})
# if "source.year" in errors:
# raise Exception("Invalid expiry year")
# else:
# raise Exception("Validation Error: ", errors)
# else:
# logger.error(f"Failed to process payment: {data}")
# #
# data = response.json()
# # order.status = AbstractOrder.STATUS.NEW
# order.save()
# #
# amount = Decimal("{0:.2f}".format(Decimal(total) / Decimal(100)))
# models.PaymentHistory.objects.create(
# user=request.user,
# user_data=user_data,
# amount=amount,
# currency=data["currency"],
# status=data["status"],
# transaction_id=data["id"],
# payment_date=data["created_at"],
# gateway_response=data,
# )
# transaction_url = data["source"]["transaction_url"]
# logger.info(f"Created payment history and got transaction_url: {transaction_url}")
# return transaction_url
# def get_user_quota(user):

View File

@ -218,7 +218,7 @@ from .utils import (
from .tasks import create_accounts_for_make, create_user_dealer, send_email
# djago easy audit log
from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent
# from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
@ -504,6 +504,7 @@ def general_dashboard(request,dealer_slug):
total_vat_collected = total_vat_collected_from_cars + total_vat_collected_from_services
total_revenue_generated = total_revenue_from_cars + total_revenue_from_services
# total_expenses=sum([x.amount_paid for x in dealer.entity.get_bills().filter(bill_items__item_role="expense")])
total_expenses=dealer.entity.get_bills().filter(bill_items__item_role="expense").aggregate(total=Sum('amount_paid'))['total'] or 0
gross_profit = net_profit_from_cars+total_revenue_from_services - total_expenses
@ -9889,50 +9890,123 @@ def pricing_page(request, dealer_slug):
@login_required
@permission_required("inventory.change_dealer", raise_exception=True)
@require_POST
# @login_required
# @permission_required("inventory.change_dealer", raise_exception=True)
# @require_POST
# def submit_plan(request, dealer_slug):
# logger.info(f"dealer slug : {dealer_slug}")
# if request.method == "GET":
# logger.info("method is GET, redirecting to pricing page")
# return redirect("pricing_page", dealer_slug=dealer_slug)
# dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
# logger.info(f"Selected dealer : {dealer}")
# selected_plan_id = request.POST.get("selected_plan")
# logger.info(f"Selected plan id : {selected_plan_id}")
# pp = PlanPricing.objects.get(pk=selected_plan_id)
# logger.info(f"Selected plan pricing : {pp}")
# order = None
# try:
# order = Order.objects.create(
# user=dealer.user,
# plan=pp.plan,
# pricing=pp.pricing,
# amount=pp.price,
# currency="SA",
# tax=15,
# status=1,
# )
# logger.info(f"order created {order}")
# except Exception as e:
# logger.error(e)
# if not order:
# messages.error(request, _("Error creating order"))
# logger.error("unable to create order")
# return redirect("pricing_page", dealer_slug=dealer_slug)
# transaction_url = handle_payment(request, order)
# logger.info(f"Redirecting to : {transaction_url}")
# return redirect(transaction_url)
def submit_plan(request, dealer_slug):
if request.method == "GET":
return redirect("pricing_page", dealer_slug=dealer_slug)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
selected_plan_id = request.POST.get("selected_plan")
pp = PlanPricing.objects.get(pk=selected_plan_id)
order = None
if not selected_plan_id:
messages.error(request, _("No plan selected."))
return redirect("pricing_page", dealer_slug=dealer_slug)
# Store plan & dealer info in session for use in callback
request.session['pending_plan_id'] = selected_plan_id
request.session['pending_dealer_slug'] = dealer_slug
# Initiate payment WITHOUT creating order
transaction_url,error = handle_payment(request, dealer)
if not transaction_url:
messages.error(request, _(f"Payment initiation failed. {error}"))
return redirect("pricing_page", dealer_slug=dealer_slug)
return redirect(transaction_url)
# @login_required
def payment_callback(request, dealer_slug):
payment_id = request.GET.get("id")
payment_status = request.GET.get("status")
message = request.GET.get("message", "")
logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}")
history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first()
if not history:
logger.error(f"No PaymentHistory found for transaction_id: {payment_id}")
return render(request, "payment_failed.html", {"message": "Invalid transaction"})
if history.status == "paid":
logger.info("Payment already processed. Redirecting to home.")
return redirect('home')
if payment_status == "paid":
logger.info(f"Payment successful for transaction ID {payment_id}. Creating order...")
# Get metadata from PaymentHistory (passed during handle_payment)
metadata = history.user_data
if isinstance(metadata, str):
try:
metadata = json.loads(metadata)
except json.JSONDecodeError:
logger.error(f"Failed to decode metadata JSON: {metadata}")
metadata = {}
plan_pricing_id = metadata.get("plan_pricing_id")
dealer_slug_from_meta = metadata.get("dealer_slug")
if not plan_pricing_id or dealer_slug_from_meta != dealer_slug:
logger.error("Invalid metadata in payment callback")
history.status = "failed"
history.save()
return render(request, "payment_failed.html", {"message": "Invalid payment data"})
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
pp = get_object_or_404(PlanPricing, pk=plan_pricing_id)
# ✅ CREATE ORDER HERE
try:
order = Order.objects.create(
user=dealer.user,
plan=pp.plan,
pricing=pp.pricing,
amount=pp.price,
currency="SA",
currency="SAR", # Fixed typo: was "SA"
tax=15,
status=1,
status=Order.STATUS.NEW, # Use constant if available
)
logger.info(f"order created {order}")
logger.info(f"Order {order.id} created for user {dealer.user}")
except Exception as e:
logger.error(e)
if not order:
messages.error(request, _("Error creating order"))
logger.error("unable to create order")
return redirect("pricing_page", dealer_slug=dealer_slug)
transaction_url = handle_payment(request, order)
return redirect(transaction_url)
@login_required
@permission_required("inventory.change_dealer", raise_exception=True)
def payment_callback(request, dealer_slug):
message = request.GET.get("message")
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
payment_id = request.GET.get("id")
history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first()
payment_status = request.GET.get("status")
logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}")
order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW
if history.status == "paid":
return redirect('home')
if payment_status == "paid":
logger.info(f"Payment successful for transaction ID {payment_id}. Processing order completion.")
logger.exception(f"Failed to create order: {e}")
history.status = "failed"
history.save()
return render(request, "payment_failed.html", {"message": "Order creation failed"})
# Create or get BillingInfo
billing_info, created = BillingInfo.objects.get_or_create(
user=dealer.user,
defaults={
@ -9949,8 +10023,8 @@ def payment_callback(request, dealer_slug):
else:
logger.debug(f"Billing info already exists for user {dealer.user}.")
# Create or update UserPlan
if not hasattr(order.user, 'userplan'):
print(order.get_plan_pricing().pricing.period)
UserPlan.objects.create(
user=order.user,
plan=order.plan,
@ -9958,26 +10032,22 @@ def payment_callback(request, dealer_slug):
)
logger.info(f"Created new UserPlan for user {order.user} with plan {order.plan}.")
else:
# Optional: upgrade existing plan
# user_plan = order.user.userplan
# user_plan.plan = order.plan
# user_plan.save()
logger.info(f"UserPlan already exists for user {order.user}.")
try:
# if order.user.userplan:
# user = order.user
# pricing = order.get_plan_pricing().pricing
# logger.info(f"Processing order completion for {user} - upgrading to {order.plan}")
# user.userplan.plan = order.plan
# user.userplan.expire = datetime.now() + timedelta(days=pricing.period)
# user.userplan.save()
# user.save()
# logger.info(f"User {user} upgraded to {order.plan} plan successfully")
# Complete the order (this may generate invoice, etc.)
order.complete_order()
history.status = "paid"
history.order = order # Link payment to order
history.save()
logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.")
invoice = order.get_invoices().first()
logger.info(f"Order {order.id} completed. Rendering success page.")
return render(
request,
"payment_success.html",
@ -9985,12 +10055,13 @@ def payment_callback(request, dealer_slug):
)
except Exception as e:
logger.exception(f"Error completing order {order.id} for user {order.user}: {e}")
logger.error(f"Plan activation failed: {str(e)}")
logger.exception(f"Error completing order {order.id}: {e}")
history.status = "failed"
history.save()
return render(request, "payment_failed.html", {"message": "Plan activation error"})
finally:
# Activate dealer & staff if needed
if dealer := getattr(order.user, "dealer", None):
if not dealer.user.is_active:
dealer.user.is_active = True
@ -9999,7 +10070,6 @@ def payment_callback(request, dealer_slug):
if not staff.user.is_active:
staff.activate_account()
logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.")
elif payment_status == "failed":
logger.warning(f"Payment failed for transaction ID {payment_id}. Message: {message}")
history.status = "failed"
@ -10007,6 +10077,90 @@ def payment_callback(request, dealer_slug):
return render(request, "payment_failed.html", {"message": message})
return render(request, "payment_failed.html", {"message": "Unknown payment status"})
# @login_required
# @permission_required("inventory.change_dealer", raise_exception=True)
# def payment_callback(request, dealer_slug):
# message = request.GET.get("message")
# dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
# payment_id = request.GET.get("id")
# history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first()
# payment_status = request.GET.get("status")
# logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}")
# order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW
# logger.info(f"Retrieved order {order.id} for user {order.user}.")
# if history.status == "paid":
# logger.info(f"Payment history already marked as paid for transaction ID {payment_id}. Redirecting to home.")
# return redirect('home')
# if payment_status == "paid":
# logger.info(f"Payment successful for transaction ID {payment_id}. Processing order completion.")
# billing_info, created = BillingInfo.objects.get_or_create(
# user=dealer.user,
# defaults={
# 'tax_number': dealer.vrn,
# 'name': dealer.arabic_name,
# 'street': dealer.address,
# 'zipcode': dealer.entity.zip_code or " ",
# 'city': dealer.entity.city or " ",
# 'country': dealer.entity.country or " ",
# }
# )
# if created:
# logger.info(f"Created new billing info for user {dealer.user}.")
# else:
# logger.debug(f"Billing info already exists for user {dealer.user}.")
# if not hasattr(order.user, 'userplan'):
# logger.info(f"Creating new UserPlan for user {order.user} with plan {order.plan}.")
# UserPlan.objects.create(
# user=order.user,
# plan=order.plan,
# # expire=datetime.now().date() + timedelta(days=order.get_plan_pricing().pricing.period)
# )
# else:
# logger.info(f"UserPlan already exists for user {order.user}.")
# try:
# logger.info(f"Processing order completion for {order.user} - upgrading to {order.plan}")
# order.complete_order()
# history.status = "paid"
# history.save()
# logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.")
# invoice = order.get_invoices().first()
# logger.info(f"Redirecting to payment success page with invoice {invoice.id}.")
# return render(
# request,
# "payment_success.html",
# {"order": order, "invoice": invoice}
# )
# except Exception as e:
# logger.exception(f"Error completing order {order.id} for user {order.user}: {e}")
# logger.error(f"Plan activation failed: {str(e)}")
# history.status = "failed"
# history.save()
# logger.info(f"Redirecting to payment failed page with message {message}.")
# return render(request, "payment_failed.html", {"message": "Plan activation error"})
# finally:
# if dealer := getattr(order.user,"dealer", None):
# logger.info(f"Activating dealer {dealer} and its staff.")
# if not dealer.user.is_active:
# logger.info(f"Activating dealer {dealer}.")
# dealer.user.is_active = True
# dealer.user.save()
# for staff in dealer.get_staff():
# logger.info(f"Activating staff {staff}.")
# if not staff.user.is_active:
# staff.activate_account()
# logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.")
# elif payment_status == "failed":
# logger.warning(f"Payment failed for transaction ID {payment_id}. Message: {message}")
# history.status = "failed"
# history.save()
# logger.info(f"Redirecting to payment failed page with message {message}.")
# return render(request, "payment_failed.html", {"message": message})
# logger.info(f"Redirecting to payment failed page with message {message}.")
# return render(request, "payment_failed.html", {"message": "Unknown payment status"})
# def payment_callback(request, dealer_slug):
# message = request.GET.get("message")
# dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
@ -10350,140 +10504,140 @@ def user_management(request, dealer_slug):
return render(request, "admin_management/user_management.html", context)
@login_required
@permission_required("inventory.change_dealer", raise_exception=True)
def AuditLogDashboardView(request, dealer_slug):
"""
Displays audit logs (User Actions, Login Events, Request Events) with pagination.
Log type is determined by the 'q' query parameter (e.g., ?q=userActions).
Pagination page number is passed as a query parameter (e.g., ?page=2).
"""
get_object_or_404(models.Dealer, slug=dealer_slug)
q = request.GET.get("q") # Get the log type from the 'q' query parameter
current_pagination_page = request.GET.get("page", 1)
context = {}
template_name = None
logs_per_page = 30 # Define logs per page once
# @login_required
# @permission_required("inventory.change_dealer", raise_exception=True)
# def AuditLogDashboardView(request, dealer_slug):
# """
# Displays audit logs (User Actions, Login Events, Request Events) with pagination.
# Log type is determined by the 'q' query parameter (e.g., ?q=userActions).
# Pagination page number is passed as a query parameter (e.g., ?page=2).
# """
# get_object_or_404(models.Dealer, slug=dealer_slug)
# q = request.GET.get("q") # Get the log type from the 'q' query parameter
# current_pagination_page = request.GET.get("page", 1)
# context = {}
# template_name = None
# logs_per_page = 30 # Define logs per page once
# --- Determine Data Source and Template based on 'q' parameter ---
if (
q == "userRequests"
): # This block handles cases where 'q' is 'requestEvents', None, or any other invalid value.
# It defaults to Request Logs if 'q' is not 'userActions' or 'loginEvents'.
template_name = "admin_management/request_logs.html"
context["title"] = "Request Logs Dashboard"
request_events = RequestEvent.objects.all().order_by("-datetime")
paginator = Paginator(request_events, logs_per_page)
try:
page_obj = paginator.page(current_pagination_page)
except PageNotAnInteger:
page_obj = paginator.page(1)
except EmptyPage:
page_obj = paginator.page(paginator.num_pages)
# # --- Determine Data Source and Template based on 'q' parameter ---
# if (
# q == "userRequests"
# ): # This block handles cases where 'q' is 'requestEvents', None, or any other invalid value.
# # It defaults to Request Logs if 'q' is not 'userActions' or 'loginEvents'.
# template_name = "admin_management/request_logs.html"
# context["title"] = "Request Logs Dashboard"
# request_events = RequestEvent.objects.all().order_by("-datetime")
# paginator = Paginator(request_events, logs_per_page)
# try:
# page_obj = paginator.page(current_pagination_page)
# except PageNotAnInteger:
# page_obj = paginator.page(1)
# except EmptyPage:
# page_obj = paginator.page(paginator.num_pages)
elif q == "loginEvents":
template_name = "admin_management/auth_logs.html"
context["title"] = "Login Events Dashboard"
auth_events = LoginEvent.objects.all().order_by("-datetime")
paginator = Paginator(auth_events, logs_per_page)
try:
page_obj = paginator.page(current_pagination_page)
except PageNotAnInteger:
page_obj = paginator.page(1)
except EmptyPage:
page_obj = paginator.page(paginator.num_pages)
# elif q == "loginEvents":
# template_name = "admin_management/auth_logs.html"
# context["title"] = "Login Events Dashboard"
# auth_events = LoginEvent.objects.all().order_by("-datetime")
# paginator = Paginator(auth_events, logs_per_page)
# try:
# page_obj = paginator.page(current_pagination_page)
# except PageNotAnInteger:
# page_obj = paginator.page(1)
# except EmptyPage:
# page_obj = paginator.page(paginator.num_pages)
else:
template_name = "admin_management/model_logs.html"
context["title"] = "User Actions Dashboard"
# else:
# template_name = "admin_management/model_logs.html"
# context["title"] = "User Actions Dashboard"
# OPTIMIZATION: Get the QuerySet but don't evaluate it yet
model_events_queryset = CRUDEvent.objects.all().order_by("-datetime")
# # OPTIMIZATION: Get the QuerySet but don't evaluate it yet
# model_events_queryset = CRUDEvent.objects.all().order_by("-datetime")
# 1. Paginate the raw QuerySet FIRST
paginator = Paginator(model_events_queryset, logs_per_page)
# # 1. Paginate the raw QuerySet FIRST
# paginator = Paginator(model_events_queryset, logs_per_page)
try:
# Get the page object, which contains only the raw QuerySet objects for the current page
page_obj_raw = paginator.page(current_pagination_page)
except PageNotAnInteger:
page_obj_raw = paginator.page(1)
except EmptyPage:
page_obj_raw = paginator.page(paginator.num_pages)
# try:
# # Get the page object, which contains only the raw QuerySet objects for the current page
# page_obj_raw = paginator.page(current_pagination_page)
# except PageNotAnInteger:
# page_obj_raw = paginator.page(1)
# except EmptyPage:
# page_obj_raw = paginator.page(paginator.num_pages)
# 2. Now, process 'field_changes' ONLY for the events on the current page
processed_model_events_for_page = []
for (
event
) in page_obj_raw.object_list: # Loop only through the current page's items
event_data = {
"datetime": event.datetime,
"user": event.user,
"event_type_display": event.get_event_type_display(),
"model_name": event.content_type.model,
"object_id": event.object_id,
"object_repr": event.object_repr,
"field_changes": [],
}
# # 2. Now, process 'field_changes' ONLY for the events on the current page
# processed_model_events_for_page = []
# for (
# event
# ) in page_obj_raw.object_list: # Loop only through the current page's items
# event_data = {
# "datetime": event.datetime,
# "user": event.user,
# "event_type_display": event.get_event_type_display(),
# "model_name": event.content_type.model,
# "object_id": event.object_id,
# "object_repr": event.object_repr,
# "field_changes": [],
# }
if event.changed_fields:
try:
changes = json.loads(event.changed_fields)
if isinstance(changes, dict):
for field_name, values in changes.items():
old_value = (
values[0]
if isinstance(values, list) and len(values) > 0
else None
)
new_value = (
values[1]
if isinstance(values, list) and len(values) > 1
else None
)
event_data["field_changes"].append(
{
"field": field_name,
"old": old_value,
"new": new_value,
}
)
elif changes is None:
event_data["field_changes"].append(
{
"field": "Info",
"old": "",
"new": "No specific field changes recorded (JSON was null)",
}
)
else: # Handle valid JSON but not a dictionary (e.g., "[]", 123)
event_data["field_changes"].append(
{
"field": "Error",
"old": "",
"new": f"Unexpected JSON format: {type(changes).__name__}",
}
)
except json.JSONDecodeError:
# Handle invalid JSON; you might log this error
event_data["field_changes"].append(
{
"field": "Error",
"old": "",
"new": "Invalid JSON in changed_fields",
}
)
processed_model_events_for_page.append(event_data)
# if event.changed_fields:
# try:
# changes = json.loads(event.changed_fields)
# if isinstance(changes, dict):
# for field_name, values in changes.items():
# old_value = (
# values[0]
# if isinstance(values, list) and len(values) > 0
# else None
# )
# new_value = (
# values[1]
# if isinstance(values, list) and len(values) > 1
# else None
# )
# event_data["field_changes"].append(
# {
# "field": field_name,
# "old": old_value,
# "new": new_value,
# }
# )
# elif changes is None:
# event_data["field_changes"].append(
# {
# "field": "Info",
# "old": "",
# "new": "No specific field changes recorded (JSON was null)",
# }
# )
# else: # Handle valid JSON but not a dictionary (e.g., "[]", 123)
# event_data["field_changes"].append(
# {
# "field": "Error",
# "old": "",
# "new": f"Unexpected JSON format: {type(changes).__name__}",
# }
# )
# except json.JSONDecodeError:
# # Handle invalid JSON; you might log this error
# event_data["field_changes"].append(
# {
# "field": "Error",
# "old": "",
# "new": "Invalid JSON in changed_fields",
# }
# )
# processed_model_events_for_page.append(event_data)
# 3. Replace the object_list of the original page_obj with the processed data
# This keeps all pagination properties (has_next, number, etc.) intact.
page_obj_raw.object_list = processed_model_events_for_page
page_obj = page_obj_raw # This will be passed to the context
# # 3. Replace the object_list of the original page_obj with the processed data
# # This keeps all pagination properties (has_next, number, etc.) intact.
# page_obj_raw.object_list = processed_model_events_for_page
# page_obj = page_obj_raw # This will be passed to the context
# Pass the final page object to the context
context["page_obj"] = page_obj
# # Pass the final page object to the context
# context["page_obj"] = page_obj
return render(request, template_name, context)
# return render(request, template_name, context)
@login_required