Files
thrillwiki_django_no_react/backend/apps/parks/models/companies.py
pacnpal fe960e8b62 w
2026-01-08 13:44:37 -05:00

237 lines
7.8 KiB
Python

import pghistory
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils.text import slugify
from apps.core.choices.fields import RichChoiceField
from apps.core.models import TrackedModel
@pghistory.track()
class Company(TrackedModel):
# Import managers
from ..managers import CompanyManager
objects = CompanyManager()
# Core Fields
name = models.CharField(max_length=255, help_text="Company name")
slug = models.SlugField(max_length=255, unique=True, help_text="URL-friendly identifier")
roles = ArrayField(
RichChoiceField(choice_group="company_roles", domain="parks", max_length=20),
default=list,
blank=True,
help_text="Company roles (operator, manufacturer, etc.)",
)
description = models.TextField(blank=True, help_text="Detailed company description")
website = models.URLField(blank=True, help_text="Company website URL")
# Person/Entity Type (ported from legacy thrillwiki-87)
PERSON_TYPES = [
("INDIVIDUAL", "Individual"),
("FIRM", "Firm"),
("ORGANIZATION", "Organization"),
("CORPORATION", "Corporation"),
("PARTNERSHIP", "Partnership"),
("GOVERNMENT", "Government Entity"),
]
person_type = models.CharField(
max_length=20,
choices=PERSON_TYPES,
blank=True,
help_text="Type of entity (individual, firm, organization, etc.)",
)
# Company Status (ported from legacy)
COMPANY_STATUSES = [
("ACTIVE", "Active"),
("DEFUNCT", "Defunct"),
("MERGED", "Merged"),
("ACQUIRED", "Acquired"),
("RENAMED", "Renamed"),
("DORMANT", "Dormant"),
]
status = models.CharField(
max_length=20,
choices=COMPANY_STATUSES,
default="ACTIVE",
help_text="Current operational status of the company",
)
# Founding Information (enhanced from just founded_year)
founded_year = models.PositiveIntegerField(blank=True, null=True, help_text="Year the company was founded")
founded_date = models.DateField(blank=True, null=True, help_text="Full founding date if known")
DATE_PRECISION_CHOICES = [
("exact", "Exact Date"),
("month", "Month and Year"),
("year", "Year Only"),
("decade", "Decade"),
("century", "Century"),
("approximate", "Approximate"),
]
founded_date_precision = models.CharField(
max_length=20,
choices=DATE_PRECISION_CHOICES,
blank=True,
help_text="Precision of the founding date",
)
# Image URLs (ported from legacy)
logo_url = models.URLField(blank=True, help_text="Company logo image URL")
banner_image_url = models.URLField(blank=True, help_text="Banner image for company page header")
card_image_url = models.URLField(blank=True, help_text="Card/thumbnail image for listings")
# Image ID fields (for frontend submissions - Cloudflare image IDs)
banner_image_id = models.CharField(
max_length=255,
blank=True,
help_text="Cloudflare image ID for banner image",
)
card_image_id = models.CharField(
max_length=255,
blank=True,
help_text="Cloudflare image ID for card image",
)
# Location relationship (for headquarters coordinates)
location = models.ForeignKey(
"ParkLocation",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="companies_hq",
help_text="Linked location record for headquarters",
)
# Text-based headquarters location (matches frontend schema)
headquarters_location = models.CharField(
max_length=200,
blank=True,
help_text="Headquarters location description (e.g., 'Los Angeles, CA, USA')",
)
# Rating & Review Aggregates (computed fields, updated by triggers/signals)
average_rating = models.DecimalField(
max_digits=3,
decimal_places=2,
blank=True,
null=True,
help_text="Average rating from reviews (auto-calculated)",
)
review_count = models.PositiveIntegerField(
default=0,
help_text="Total number of reviews (auto-calculated)",
)
# Counts (auto-calculated)
parks_count = models.IntegerField(default=0, help_text="Number of parks operated (auto-calculated)")
rides_count = models.IntegerField(default=0, help_text="Number of rides manufactured (auto-calculated)")
# Submission metadata fields (from frontend schema)
source_url = models.URLField(
blank=True,
help_text="Source URL for the data (e.g., official website, Wikipedia)",
)
is_test_data = models.BooleanField(
default=False,
help_text="Whether this is test/development data",
)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def __str__(self):
return self.name
class Meta(TrackedModel.Meta):
app_label = "parks"
verbose_name = "Company"
verbose_name_plural = "Companies"
ordering = ["name"]
@pghistory.track()
class CompanyHeadquarters(models.Model):
"""
Simple address storage for company headquarters without coordinate tracking.
Focus on human-readable location information for display purposes.
"""
# Relationships
company = models.OneToOneField(
"Company",
on_delete=models.CASCADE,
related_name="headquarters",
help_text="Company this headquarters belongs to",
)
# Address Fields (No coordinates needed)
street_address = models.CharField(
max_length=255,
blank=True,
help_text="Mailing address if publicly available",
)
city = models.CharField(max_length=100, db_index=True, help_text="Headquarters city")
state_province = models.CharField(
max_length=100,
blank=True,
db_index=True,
help_text="State/Province/Region",
)
country = models.CharField(
max_length=100,
default="USA",
db_index=True,
help_text="Country where headquarters is located",
)
postal_code = models.CharField(max_length=20, blank=True, help_text="ZIP or postal code")
# Optional mailing address if different or more complete
mailing_address = models.TextField(
blank=True,
help_text="Complete mailing address if different from basic address",
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@property
def formatted_location(self):
"""Returns a formatted address string for display."""
components = []
if self.street_address:
components.append(self.street_address)
if self.city:
components.append(self.city)
if self.state_province:
components.append(self.state_province)
if self.postal_code:
components.append(self.postal_code)
if self.country and self.country != "USA":
components.append(self.country)
return ", ".join(components) if components else f"{self.city}, {self.country}"
@property
def location_display(self):
"""Simple city, state/country display for compact views."""
parts = [self.city]
if self.state_province:
parts.append(self.state_province)
elif self.country != "USA":
parts.append(self.country)
return ", ".join(parts) if parts else "Unknown Location"
def __str__(self):
return f"{self.company.name} Headquarters - {self.location_display}"
class Meta:
verbose_name = "Company Headquarters"
verbose_name_plural = "Company Headquarters"
ordering = ["company__name"]
indexes = [
models.Index(fields=["city", "country"]),
]