from django.contrib import admin from django.contrib.gis.admin import GISModelAdmin from django.utils.html import format_html from .models.company import Company from .models.rides import Ride, RideModel, RollerCoasterStats from .models.location import RideLocation from .models.reviews import RideReview class ManufacturerAdmin(admin.ModelAdmin): list_display = ("name", "headquarters", "website", "rides_count") search_fields = ("name",) def get_queryset(self, request): return super().get_queryset(request).filter(roles__contains=["MANUFACTURER"]) class DesignerAdmin(admin.ModelAdmin): list_display = ("name", "headquarters", "website") search_fields = ("name",) def get_queryset(self, request): return super().get_queryset(request).filter(roles__contains=["DESIGNER"]) class RideLocationInline(admin.StackedInline): """Inline admin for RideLocation""" model = RideLocation extra = 0 fields = ( "park_area", "point", "entrance_notes", "accessibility_notes", ) class RideLocationAdmin(GISModelAdmin): """Admin for standalone RideLocation management""" list_display = ("ride", "park_area", "has_coordinates", "created_at") list_filter = ("park_area", "created_at") search_fields = ("ride__name", "park_area", "entrance_notes") readonly_fields = ( "latitude", "longitude", "coordinates", "created_at", "updated_at", ) fieldsets = ( ("Ride", {"fields": ("ride",)}), ( "Location Information", { "fields": ( "park_area", "point", "latitude", "longitude", "coordinates", ), "description": "Optional coordinates - not all rides need precise location tracking", }, ), ( "Navigation Notes", { "fields": ("entrance_notes", "accessibility_notes"), }, ), ( "Metadata", {"fields": ("created_at", "updated_at"), "classes": ("collapse",)}, ), ) @admin.display(description="Latitude") def latitude(self, obj): return obj.latitude @admin.display(description="Longitude") def longitude(self, obj): return obj.longitude class RollerCoasterStatsInline(admin.StackedInline): """Inline admin for RollerCoasterStats""" model = RollerCoasterStats extra = 0 fields = ( ("height_ft", "length_ft", "speed_mph"), ("track_material", "roller_coaster_type"), ("launch_type", "inversions"), ("max_drop_height_ft", "ride_time_seconds"), ("train_style", "trains_count"), ("cars_per_train", "seats_per_car"), ) classes = ("collapse",) @admin.register(Ride) class RideAdmin(admin.ModelAdmin): """Enhanced Ride admin with location and coaster stats inlines""" list_display = ( "name", "park", "category_display", "manufacturer", "status", "opening_date", "average_rating", ) list_filter = ( "category", "status", "park", "manufacturer", "designer", "opening_date", ) search_fields = ( "name", "description", "park__name", "manufacturer__name", "designer__name", ) readonly_fields = ("created_at", "updated_at") prepopulated_fields = {"slug": ("name",)} inlines = [RideLocationInline, RollerCoasterStatsInline] date_hierarchy = "opening_date" ordering = ("park", "name") fieldsets = ( ( "Basic Information", { "fields": ( "name", "slug", "description", "park", "park_area", "category", ) }, ), ( "Companies", { "fields": ( "manufacturer", "designer", "ride_model", ) }, ), ( "Status & Dates", { "fields": ( "status", "post_closing_status", "opening_date", "closing_date", "status_since", ) }, ), ( "Ride Specifications", { "fields": ( "min_height_in", "max_height_in", "capacity_per_hour", "ride_duration_seconds", "average_rating", ), "classes": ("collapse",), }, ), ( "Metadata", { "fields": ("created_at", "updated_at"), "classes": ("collapse",), }, ), ) @admin.display(description="Category") def category_display(self, obj): """Display category with full name""" return dict(obj._meta.get_field("category").choices).get( obj.category, obj.category ) @admin.register(RideModel) class RideModelAdmin(admin.ModelAdmin): """Admin interface for ride models""" list_display = ( "name", "manufacturer", "category_display", "ride_count", ) list_filter = ( "manufacturer", "category", ) search_fields = ( "name", "description", "manufacturer__name", ) ordering = ("manufacturer", "name") fieldsets = ( ( "Model Information", { "fields": ( "name", "manufacturer", "category", "description", ) }, ), ) @admin.display(description="Category") def category_display(self, obj): """Display category with full name""" return dict(obj._meta.get_field("category").choices).get( obj.category, obj.category ) @admin.display(description="Installations") def ride_count(self, obj): """Display number of ride installations""" return obj.rides.count() @admin.register(RollerCoasterStats) class RollerCoasterStatsAdmin(admin.ModelAdmin): """Admin interface for roller coaster statistics""" list_display = ( "ride", "height_ft", "speed_mph", "length_ft", "inversions", "track_material", "roller_coaster_type", ) list_filter = ( "track_material", "roller_coaster_type", "launch_type", "inversions", ) search_fields = ( "ride__name", "ride__park__name", "track_type", "train_style", ) readonly_fields = ("calculated_capacity",) fieldsets = ( ( "Basic Stats", { "fields": ( "ride", "height_ft", "length_ft", "speed_mph", "max_drop_height_ft", ) }, ), ( "Track & Design", { "fields": ( "track_material", "track_type", "roller_coaster_type", "launch_type", "inversions", ) }, ), ( "Operation Details", { "fields": ( "ride_time_seconds", "train_style", "trains_count", "cars_per_train", "seats_per_car", "calculated_capacity", ), "classes": ("collapse",), }, ), ) @admin.display(description="Calculated Capacity") def calculated_capacity(self, obj): """Calculate theoretical hourly capacity""" if all( [ obj.trains_count, obj.cars_per_train, obj.seats_per_car, obj.ride_time_seconds, ] ): total_seats = obj.trains_count * obj.cars_per_train * obj.seats_per_car # Add 2 min loading time cycles_per_hour = 3600 / (obj.ride_time_seconds + 120) return f"{int(total_seats * cycles_per_hour)} riders/hour" return "N/A" @admin.register(RideReview) class RideReviewAdmin(admin.ModelAdmin): """Admin interface for ride reviews""" list_display = ( "ride", "user", "rating", "title", "visit_date", "is_published", "created_at", "moderation_status", ) list_filter = ( "rating", "is_published", "visit_date", "created_at", "ride__park", "moderated_by", ) search_fields = ( "title", "content", "user__username", "ride__name", "ride__park__name", ) readonly_fields = ("created_at", "updated_at") date_hierarchy = "created_at" ordering = ("-created_at",) fieldsets = ( ( "Review Details", { "fields": ( "user", "ride", "rating", "title", "content", "visit_date", ) }, ), ( "Publication Status", { "fields": ("is_published",), }, ), ( "Moderation", { "fields": ( "moderated_by", "moderated_at", "moderation_notes", ), "classes": ("collapse",), }, ), ( "Metadata", { "fields": ("created_at", "updated_at"), "classes": ("collapse",), }, ), ) @admin.display(description="Moderation Status", boolean=True) def moderation_status(self, obj): """Display moderation status with color coding""" if obj.moderated_by: return format_html( '{}', "green" if obj.is_published else "red", "Approved" if obj.is_published else "Rejected", ) return format_html('Pending') def save_model(self, request, obj, form, change): """Auto-set moderation info when status changes""" if change and "is_published" in form.changed_data: from django.utils import timezone obj.moderated_by = request.user obj.moderated_at = timezone.now() super().save_model(request, obj, form, change) @admin.register(Company) class CompanyAdmin(admin.ModelAdmin): """Enhanced Company admin for rides app""" list_display = ( "name", "roles_display", "website", "founded_date", "rides_count", "coasters_count", ) list_filter = ("roles", "founded_date") search_fields = ("name", "description") readonly_fields = ("created_at", "updated_at") prepopulated_fields = {"slug": ("name",)} fieldsets = ( ( "Basic Information", { "fields": ( "name", "slug", "roles", "description", "website", ) }, ), ( "Company Details", { "fields": ( "founded_date", "rides_count", "coasters_count", ) }, ), ( "Metadata", { "fields": ("created_at", "updated_at"), "classes": ("collapse",), }, ), ) @admin.display(description="Roles") def roles_display(self, obj): """Display roles as a formatted string""" return ", ".join(obj.roles) if obj.roles else "No roles" admin.site.register(RideLocation, RideLocationAdmin)