diff --git a/mpr/media_assets/models.py b/mpr/media_assets/models.py index 971768b..6d6f505 100644 --- a/mpr/media_assets/models.py +++ b/mpr/media_assets/models.py @@ -1,7 +1,8 @@ """ Django ORM Models - GENERATED FILE -Do not edit directly. Regenerate using modelgen. +Do not edit directly. Modify schema/models/*.py and run: + python schema/generate.py --django """ import uuid @@ -12,6 +13,7 @@ class AssetStatus(models.TextChoices): READY = "ready", "Ready" ERROR = "error", "Error" + class JobStatus(models.TextChoices): PENDING = "pending", "Pending" PROCESSING = "processing", "Processing" @@ -19,13 +21,14 @@ class JobStatus(models.TextChoices): FAILED = "failed", "Failed" CANCELLED = "cancelled", "Cancelled" + class MediaAsset(models.Model): """A video/audio file registered in the system.""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) filename = models.CharField(max_length=500) file_path = models.CharField(max_length=1000) - status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING) + status = models.CharField(max_length=20, choices=AssetStatus.choices, default=AssetStatus.PENDING) error_message = models.TextField(blank=True, default='') file_size = models.BigIntegerField(null=True, blank=True) duration = models.FloatField(null=True, blank=True, default=None) @@ -89,7 +92,7 @@ class TranscodeJob(models.Model): output_filename = models.CharField(max_length=500) output_path = models.CharField(max_length=1000, null=True, blank=True) output_asset_id = models.UUIDField(null=True, blank=True) - status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING) + status = models.CharField(max_length=20, choices=JobStatus.choices, default=JobStatus.PENDING) progress = models.FloatField(default=0.0) current_frame = models.IntegerField(null=True, blank=True, default=None) current_time = models.FloatField(null=True, blank=True, default=None) diff --git a/schema/generate.py b/schema/generate.py index dee2ce1..9449abe 100755 --- a/schema/generate.py +++ b/schema/generate.py @@ -40,7 +40,7 @@ DJANGO_TYPES: dict[Any, str] = { "list": "models.JSONField(default=list, blank=True)", "text": "models.TextField(blank=True, default='')", "bigint": "models.BigIntegerField({opts})", - "enum": "models.CharField(max_length=20, choices=Status.choices{opts})", + "enum": "models.CharField(max_length=20, choices={enum_name}.choices{opts})", } DJANGO_SPECIAL: dict[str, str] = { @@ -69,9 +69,11 @@ TS_RESOLVERS: dict[Any, Callable[[Any], str]] = { "UUID": lambda _: "string", "datetime": lambda _: "string", "dict": lambda _: "Record", - "list": lambda base: f"{TS_RESOLVERS.get(get_args(base)[0], lambda _: 'string')(None)}[]" - if get_args(base) - else "string[]", + "list": lambda base: ( + f"{TS_RESOLVERS.get(get_args(base)[0], lambda _: 'string')(None)}[]" + if get_args(base) + else "string[]" + ), "enum": lambda base: base.__name__, } @@ -80,9 +82,11 @@ PROTO_RESOLVERS: dict[Any, Callable[[Any], str]] = { int: lambda _: "int32", float: lambda _: "float", bool: lambda _: "bool", - "list": lambda base: f"repeated {PROTO_RESOLVERS.get(get_args(base)[0], lambda _: 'string')(None)}" - if get_args(base) - else "repeated string", + "list": lambda base: ( + f"repeated {PROTO_RESOLVERS.get(get_args(base)[0], lambda _: 'string')(None)}" + if get_args(base) + else "repeated string" + ), } @@ -172,13 +176,14 @@ def resolve_django_type(name: str, type_hint: Any, default: Any) -> str: # Enum if isinstance(base, type) and issubclass(base, Enum): + enum_name = base.__name__ extra = [] if optional: extra.append("null=True, blank=True") if default is not dc.MISSING and isinstance(default, Enum): - extra.append(f"default=Status.{default.name}") + extra.append(f"default={enum_name}.{default.name}") return DJANGO_TYPES["enum"].format( - opts=", " + ", ".join(extra) if extra else "" + enum_name=enum_name, opts=", " + ", ".join(extra) if extra else "" ) # Text fields @@ -216,6 +221,15 @@ def resolve_django_type(name: str, type_hint: Any, default: Any) -> str: return DJANGO_TYPES[str].format(max_length=255, opts=", " + opts if opts else "") +def generate_django_enum(enum_cls: type) -> list[str]: + """Generate Django TextChoices enum.""" + lines = [f"class {enum_cls.__name__}(models.TextChoices):"] + for member in enum_cls: + label = member.name.replace("_", " ").title() + lines.append(f' {member.name} = "{member.value}", "{label}"') + return lines + + def generate_django_model(cls: type) -> list[str]: """Generate Django model lines from dataclass.""" lines = [ @@ -227,17 +241,6 @@ def generate_django_model(cls: type) -> list[str]: hints = get_type_hints(cls) fields = {f.name: f for f in dc.fields(cls)} - # Add Status inner class for enum fields - for type_hint in hints.values(): - base, _ = unwrap_optional(type_hint) - if isinstance(base, type) and issubclass(base, Enum): - lines.append(" class Status(models.TextChoices):") - for member in base: - label = member.name.replace("_", " ").title() - lines.append(f' {member.name} = "{member.value}", "{label}"') - lines.append("") - break - # Fields for name, type_hint in hints.items(): if name.startswith("_"): @@ -283,7 +286,13 @@ def generate_django() -> str: "", ] + # Generate enums first body = [] + for enum_cls in ENUMS: + body.extend(generate_django_enum(enum_cls)) + body.extend(["", ""]) + + # Generate models for cls in DATACLASSES: body.extend(generate_django_model(cls)) body.extend(["", ""])