From 585285640d35c615e3f76e955b36b209f4f78f6a Mon Sep 17 00:00:00 2001 From: adevopg Date: Sun, 12 Jul 2026 19:15:12 +0000 Subject: [PATCH] Divide home/models.py en el paquete home/models/ por secciones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El módulo (~310 líneas, 30 modelos) se convierte en un paquete cuyo __init__.py re-exporta todos los modelos, de modo que `from home.models import X`, el admin y el resto de consumidores siguen funcionando igual. Módulos por sección: pricing (precios/costes de servicios + GuildRenameSettings), store (Category, Item, StripeLog, Pedido), site (contenido/config del sitio), recruit, accounts (activación, token de seguridad, LoginAttempt), voting (VoteSite, VoteLog, HomeApiPoints) e history. Las claves foráneas por clase directa quedan en el mismo módulo; cada submódulo importa solo lo que usa. Se eliminan imports de cabecera muertos (ValidationError, uuid, secrets) y las líneas de import duplicadas del original. Migración-neutral: `makemigrations home --dry-run` -> "No changes detected" (app_label=home y nombres/tablas de modelo intactos). Definición de cada modelo idéntica (extracción por AST). `manage.py check` sin incidencias. Co-Authored-By: Claude Opus 4.8 (1M context) --- home/models.py | 313 ---------------------------------------- home/models/__init__.py | 13 ++ home/models/accounts.py | 48 ++++++ home/models/history.py | 13 ++ home/models/pricing.py | 51 +++++++ home/models/recruit.py | 34 +++++ home/models/site.py | 68 +++++++++ home/models/store.py | 44 ++++++ home/models/voting.py | 35 +++++ 9 files changed, 306 insertions(+), 313 deletions(-) delete mode 100644 home/models.py create mode 100644 home/models/__init__.py create mode 100644 home/models/accounts.py create mode 100644 home/models/history.py create mode 100644 home/models/pricing.py create mode 100644 home/models/recruit.py create mode 100644 home/models/site.py create mode 100644 home/models/store.py create mode 100644 home/models/voting.py diff --git a/home/models.py b/home/models.py deleted file mode 100644 index 3ccbdba..0000000 --- a/home/models.py +++ /dev/null @@ -1,313 +0,0 @@ -# home/models.py -from django.db import models -from django_ckeditor_5.fields import CKEditor5Field -from django.core.exceptions import ValidationError -from django.utils import timezone -import uuid -from django.contrib.auth.models import User -import secrets - -class Noticia(models.Model): - titulo = models.CharField(max_length=200) - contenido = CKEditor5Field('Contenido', config_name='default') - fecha_publicacion = models.DateTimeField(auto_now=True) # Cambiado a DateTimeField - fecha_fin_evento = models.DateField(null=True, blank=True) - enlace = models.URLField(max_length=200, blank=True, null=True) - - def __str__(self): - return self.titulo - -class MaintenanceMode(models.Model): - is_active = models.BooleanField(default=False) - - def __str__(self): - return "Maintenance Mode" - -class UnstuckHistory(models.Model): - character_name = models.CharField(max_length=50) - used_at = models.DateTimeField() - -class ReviveHistory(models.Model): - character_name = models.CharField(max_length=50) - used_at = models.DateTimeField() - - def __str__(self): - return f"{self.character_name} - {self.used_at}" - -class ChangeFactionPrice(models.Model): - price = models.DecimalField(max_digits=6, decimal_places=2, default=10.00) - - def __str__(self): - return f"Precio para cambiar facción: {self.price} EUR" - - -class LevelUpPrice(models.Model): - price = models.DecimalField(max_digits=10, decimal_places=2, default=10.00) - - def __str__(self): - return f"Subir a nivel 80: {self.price} EUR" - - -class GoldPrice(models.Model): - gold_amount = models.IntegerField() # Cantidad de oro - price = models.DecimalField(max_digits=10, decimal_places=2) # Precio en EUR - - def __str__(self): - return f"{self.gold_amount} oro - {self.price} EUR" - - - -class TransferPrice(models.Model): - price = models.DecimalField(max_digits=5, decimal_places=2) - - def __str__(self): - return f"Precio de transferencia: {self.price} EUR" - - -class Category(models.Model): - name = models.CharField(max_length=100, unique=True) - - def __str__(self): - return self.name - - -class Item(models.Model): - category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="items") - name = models.CharField(max_length=200) - price = models.DecimalField(max_digits=10, decimal_places=2) # Precio en euros - item_id = models.IntegerField(unique=True) # ID del ítem en el juego - image_url = models.URLField() # URL de la imagen del ítem - - def __str__(self): - return self.name - -class RenamePrice(models.Model): - price = models.DecimalField(max_digits=5, decimal_places=2, default=2.00) - - def __str__(self): - return f"{self.price} EUR" - - -class CustomizePrice(models.Model): - price = models.DecimalField(max_digits=5, decimal_places=2, default=1.00) - - def __str__(self): - return f"{self.price} EUR" - - -class ChangeRacePrice(models.Model): - price = models.DecimalField(max_digits=5, decimal_places=2) - - def __str__(self): - return f"Cambiar raza: {self.price} EUR" - - -class ClienteCategoria(models.Model): - nombre = models.CharField(max_length=100) - descripcion = models.CharField(max_length=200, blank=True, null=True) - - def __str__(self): - return self.nombre - - -class SistemaOperativo(models.Model): - categoria = models.ForeignKey(ClienteCategoria, related_name='sistemas_operativos', on_delete=models.CASCADE) - nombre = models.CharField(max_length=100) - url_descarga = models.URLField() - - def __str__(self): - return f"{self.nombre} - {self.categoria.nombre}" - - -class ServerSelection(models.Model): - server_id = models.IntegerField() - name = models.CharField(max_length=100) - address = models.CharField(max_length=100) - port = models.IntegerField() - gamebuild = models.IntegerField() - - def __str__(self): - expansion = 'WotLK' if self.gamebuild == 12340 else 'Cataclysm' if self.gamebuild == 15595 else 'Unknown' - return f"{self.name} ({expansion})" - - -class RecruitAFriend(models.Model): - title = models.CharField(max_length=255, default="Recluta a un amigo") - information = models.CharField(max_length=255, default="Información") - content = CKEditor5Field('Contenido', config_name='default') - updated_at = models.DateTimeField(auto_now=True) - - def __str__(self): - return self.title - - -class DownloadClientPage(models.Model): - title = models.CharField(max_length=255, default="Descarga del cliente") - content = CKEditor5Field('Contenido', config_name='default') - mac_instructions = CKEditor5Field('Instrucciones para macOS', blank=True, null=True, config_name='default') - updated_at = models.DateTimeField(auto_now=True) - - def __str__(self): - return self.title - - -class ContentCreator(models.Model): - title = models.CharField(max_length=200) - description = CKEditor5Field('Descripción', config_name='default') - content_type = models.CharField(max_length=60, default="Contenido general") - subtitle = models.CharField(max_length=60, default="Nova WoW") - youtube_video_url = models.URLField(max_length=300, blank=True, null=True) - avatar_image_url = models.URLField(max_length=300, blank=True, null=True) - youtube_channel_url = models.URLField(max_length=300, blank=True, null=True) - facebook_page_url = models.URLField(max_length=300, blank=True, null=True) - updated_at = models.DateTimeField(auto_now=True) - - def __str__(self): - return self.titles - -class RecruitReward(models.Model): - required_friends = models.IntegerField() - reward_name = models.CharField(max_length=100) - item_id = models.IntegerField() - item_quantity = models.IntegerField(default=1) - item_link = models.URLField() - icon_class = models.CharField(max_length=50, help_text="Clase CSS para el icono (ej. 'icontinyl q3')") - - def __str__(self): - return f"{self.required_friends} amigos - {self.reward_name}" - -class ClaimedReward(models.Model): - recruit_reward = models.ForeignKey(RecruitReward, on_delete=models.CASCADE, related_name='claimed_rewards') - account_id = models.IntegerField() - character_name = models.CharField(max_length=100) - username = models.CharField(max_length=100) - claimed_at = models.DateTimeField(auto_now_add=True) - ip_address = models.CharField(max_length=45, default='127.0.0.1') # Añadir un valor predeterminado - - def __str__(self): - return f"{self.username} - {self.recruit_reward.reward_name} - {self.claimed_at}" - -from django.db import models -from django.utils import timezone - -from django.db import models -from django.utils import timezone - -class AccountActivation(models.Model): - # En 3.4.3 (Battle.net) la identidad es el email; el username de la cuenta - # de juego se genera al activar (#1), por eso es opcional aquí. - username = models.CharField(max_length=17, null=True, blank=True) - email = models.EmailField() - old_email = models.EmailField(null=True, blank=True) - password = models.CharField(max_length=64) - # salt/verifier ya no se precalculan en el registro (se calculan al activar, - # tanto el de bnet como el de la cuenta de juego). Se conservan opcionales - # por compatibilidad con el flujo de cambio de email. - salt = models.BinaryField(max_length=32, null=True, blank=True) - verifier = models.BinaryField(max_length=256, null=True, blank=True) - recruiter_id = models.IntegerField(null=True, blank=True) - hash = models.CharField(max_length=32, unique=True) - old_email_hash = models.CharField(max_length=32, null=True, blank=True) - is_used = models.BooleanField(default=False) # Indica si el old_email_hash ha sido usado - is_new_email_used = models.BooleanField(default=False) # Indica si el hash del nuevo correo ha sido usado - created_at = models.DateTimeField(default=timezone.now) - - def is_expired(self): - return timezone.now() > self.created_at + timezone.timedelta(hours=1) - - def __str__(self): - return f"Activation for {self.username}" - - - -class SecurityToken(models.Model): - user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True) - token = models.CharField(max_length=6) - created_at = models.DateTimeField(auto_now_add=True) - expires_at = models.DateTimeField() - ip_address = models.GenericIPAddressField() - - def __str__(self): - return f"Token for {self.user.username if self.user else 'Desconocido'}" - - - -class GuildRenameSettings(models.Model): - cost = models.DecimalField(max_digits=10, decimal_places=2, default=10, help_text="Costo en euros para renombrar una hermandad.") - - def __str__(self): - return f"Configuración de renombrar hermandad - Costo: {self.cost} €" - - -class VoteSite(models.Model): - name = models.CharField(max_length=100, unique=True) - url = models.URLField(max_length=200, help_text="URL del sitio de votación") - image_url = models.URLField(max_length=300, help_text="URL completa de la imagen") - points = models.IntegerField(default=1, help_text="Puntos otorgados por votar") - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) - - def __str__(self): - return self.name - -class VoteLog(models.Model): - account_id = models.IntegerField() - vote_site = models.ForeignKey('VoteSite', on_delete=models.CASCADE) - created_at = models.DateTimeField(auto_now_add=True) - last_vote_time = models.DateTimeField(null=True, blank=True) - processed = models.BooleanField(default=False) - processed_at = models.DateTimeField(null=True, blank=True) - - def __str__(self): - return f"Voto para {self.vote_site.name} por cuenta {self.account_id}" - - -class HomeApiPoints(models.Model): - accountID = models.IntegerField(unique=True) - vp = models.IntegerField(default=0) - dp = models.IntegerField(default=0) - - class Meta: - db_table = 'home_api_points' - - def __str__(self): - return f"Cuenta {self.accountID} - PV: {self.vp}, DP: {self.dp}" - -class LoginAttempt(models.Model): - username = models.CharField(max_length=100) - status = models.CharField(max_length=50) # Ejemplo: 'Éxito' o 'Fracaso' - ip_address = models.GenericIPAddressField() # IP del intento de inicio de sesión - timestamp = models.DateTimeField(auto_now_add=True) - - def __str__(self): - return f"{self.username} - {self.status} - {self.timestamp}" - - - -class StripeLog(models.Model): - account_id = models.IntegerField() # ID de la cuenta en acore_auth - username = models.CharField(max_length=100) # Nombre de la cuenta - email = models.EmailField() # Correo electrónico - acore_ip = models.GenericIPAddressField() # IP de acore_auth - stripe_ip = models.GenericIPAddressField(blank=True, null=True) # IP detectada por Stripe - product_name = models.TextField() # Producto comprado - amount = models.DecimalField(max_digits=10, decimal_places=2) # Precio total - session_id = models.CharField(max_length=255) # ID de la sesión de Stripe - mode = models.CharField(max_length=10, choices=[("test", "Test"), ("live", "Live")]) # Modo de Stripe - character_name = models.CharField(max_length=100) # Nombre del personaje que compró - timestamp = models.DateTimeField(auto_now_add=True) # Fecha y hora - fulfilled = models.BooleanField(default=False) # Si la entrega ya se procesó (evita re-entregas) - - def __str__(self): - return f"{self.username} compró {self.product_name} por {self.amount}€ ({self.mode})" - - -class Pedido(models.Model): - transaction_id = models.CharField(max_length=100, unique=True, null=True, blank=True) - email_cliente = models.EmailField() - estado = models.CharField(max_length=20, default="Pendiente") - monto = models.DecimalField(max_digits=10, decimal_places=2) - - def __str__(self): - return f"Pedido {self.transaction_id} - {self.estado}" - \ No newline at end of file diff --git a/home/models/__init__.py b/home/models/__init__.py new file mode 100644 index 0000000..e10e522 --- /dev/null +++ b/home/models/__init__.py @@ -0,0 +1,13 @@ +"""Modelos de la app `home`, divididos por secciones. + +Antes un único home/models.py. El `import *` de cada submódulo re-exporta +los modelos para que `from home.models import X`, el admin y las migraciones +(app_label=`home`, nombres de modelo intactos) sigan funcionando igual. +""" +from .pricing import * # noqa: F401,F403 +from .store import * # noqa: F401,F403 +from .site import * # noqa: F401,F403 +from .recruit import * # noqa: F401,F403 +from .accounts import * # noqa: F401,F403 +from .voting import * # noqa: F401,F403 +from .history import * # noqa: F401,F403 diff --git a/home/models/accounts.py b/home/models/accounts.py new file mode 100644 index 0000000..6c1fb81 --- /dev/null +++ b/home/models/accounts.py @@ -0,0 +1,48 @@ +from django.db import models +from django.contrib.auth.models import User +from django.utils import timezone + + +class AccountActivation(models.Model): + # En 3.4.3 (Battle.net) la identidad es el email; el username de la cuenta + # de juego se genera al activar (#1), por eso es opcional aquí. + username = models.CharField(max_length=17, null=True, blank=True) + email = models.EmailField() + old_email = models.EmailField(null=True, blank=True) + password = models.CharField(max_length=64) + # salt/verifier ya no se precalculan en el registro (se calculan al activar, + # tanto el de bnet como el de la cuenta de juego). Se conservan opcionales + # por compatibilidad con el flujo de cambio de email. + salt = models.BinaryField(max_length=32, null=True, blank=True) + verifier = models.BinaryField(max_length=256, null=True, blank=True) + recruiter_id = models.IntegerField(null=True, blank=True) + hash = models.CharField(max_length=32, unique=True) + old_email_hash = models.CharField(max_length=32, null=True, blank=True) + is_used = models.BooleanField(default=False) # Indica si el old_email_hash ha sido usado + is_new_email_used = models.BooleanField(default=False) # Indica si el hash del nuevo correo ha sido usado + created_at = models.DateTimeField(default=timezone.now) + + def is_expired(self): + return timezone.now() > self.created_at + timezone.timedelta(hours=1) + + def __str__(self): + return f"Activation for {self.username}" + +class SecurityToken(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True) + token = models.CharField(max_length=6) + created_at = models.DateTimeField(auto_now_add=True) + expires_at = models.DateTimeField() + ip_address = models.GenericIPAddressField() + + def __str__(self): + return f"Token for {self.user.username if self.user else 'Desconocido'}" + +class LoginAttempt(models.Model): + username = models.CharField(max_length=100) + status = models.CharField(max_length=50) # Ejemplo: 'Éxito' o 'Fracaso' + ip_address = models.GenericIPAddressField() # IP del intento de inicio de sesión + timestamp = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"{self.username} - {self.status} - {self.timestamp}" diff --git a/home/models/history.py b/home/models/history.py new file mode 100644 index 0000000..daabf88 --- /dev/null +++ b/home/models/history.py @@ -0,0 +1,13 @@ +from django.db import models + + +class UnstuckHistory(models.Model): + character_name = models.CharField(max_length=50) + used_at = models.DateTimeField() + +class ReviveHistory(models.Model): + character_name = models.CharField(max_length=50) + used_at = models.DateTimeField() + + def __str__(self): + return f"{self.character_name} - {self.used_at}" diff --git a/home/models/pricing.py b/home/models/pricing.py new file mode 100644 index 0000000..73dbac8 --- /dev/null +++ b/home/models/pricing.py @@ -0,0 +1,51 @@ +from django.db import models + + +class ChangeFactionPrice(models.Model): + price = models.DecimalField(max_digits=6, decimal_places=2, default=10.00) + + def __str__(self): + return f"Precio para cambiar facción: {self.price} EUR" + +class LevelUpPrice(models.Model): + price = models.DecimalField(max_digits=10, decimal_places=2, default=10.00) + + def __str__(self): + return f"Subir a nivel 80: {self.price} EUR" + +class GoldPrice(models.Model): + gold_amount = models.IntegerField() # Cantidad de oro + price = models.DecimalField(max_digits=10, decimal_places=2) # Precio en EUR + + def __str__(self): + return f"{self.gold_amount} oro - {self.price} EUR" + +class TransferPrice(models.Model): + price = models.DecimalField(max_digits=5, decimal_places=2) + + def __str__(self): + return f"Precio de transferencia: {self.price} EUR" + +class RenamePrice(models.Model): + price = models.DecimalField(max_digits=5, decimal_places=2, default=2.00) + + def __str__(self): + return f"{self.price} EUR" + +class CustomizePrice(models.Model): + price = models.DecimalField(max_digits=5, decimal_places=2, default=1.00) + + def __str__(self): + return f"{self.price} EUR" + +class ChangeRacePrice(models.Model): + price = models.DecimalField(max_digits=5, decimal_places=2) + + def __str__(self): + return f"Cambiar raza: {self.price} EUR" + +class GuildRenameSettings(models.Model): + cost = models.DecimalField(max_digits=10, decimal_places=2, default=10, help_text="Costo en euros para renombrar una hermandad.") + + def __str__(self): + return f"Configuración de renombrar hermandad - Costo: {self.cost} €" diff --git a/home/models/recruit.py b/home/models/recruit.py new file mode 100644 index 0000000..21e2588 --- /dev/null +++ b/home/models/recruit.py @@ -0,0 +1,34 @@ +from django.db import models +from django_ckeditor_5.fields import CKEditor5Field + + +class RecruitAFriend(models.Model): + title = models.CharField(max_length=255, default="Recluta a un amigo") + information = models.CharField(max_length=255, default="Información") + content = CKEditor5Field('Contenido', config_name='default') + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + +class RecruitReward(models.Model): + required_friends = models.IntegerField() + reward_name = models.CharField(max_length=100) + item_id = models.IntegerField() + item_quantity = models.IntegerField(default=1) + item_link = models.URLField() + icon_class = models.CharField(max_length=50, help_text="Clase CSS para el icono (ej. 'icontinyl q3')") + + def __str__(self): + return f"{self.required_friends} amigos - {self.reward_name}" + +class ClaimedReward(models.Model): + recruit_reward = models.ForeignKey(RecruitReward, on_delete=models.CASCADE, related_name='claimed_rewards') + account_id = models.IntegerField() + character_name = models.CharField(max_length=100) + username = models.CharField(max_length=100) + claimed_at = models.DateTimeField(auto_now_add=True) + ip_address = models.CharField(max_length=45, default='127.0.0.1') # Añadir un valor predeterminado + + def __str__(self): + return f"{self.username} - {self.recruit_reward.reward_name} - {self.claimed_at}" diff --git a/home/models/site.py b/home/models/site.py new file mode 100644 index 0000000..a8fdf51 --- /dev/null +++ b/home/models/site.py @@ -0,0 +1,68 @@ +from django.db import models +from django_ckeditor_5.fields import CKEditor5Field + + +class Noticia(models.Model): + titulo = models.CharField(max_length=200) + contenido = CKEditor5Field('Contenido', config_name='default') + fecha_publicacion = models.DateTimeField(auto_now=True) # Cambiado a DateTimeField + fecha_fin_evento = models.DateField(null=True, blank=True) + enlace = models.URLField(max_length=200, blank=True, null=True) + + def __str__(self): + return self.titulo + +class MaintenanceMode(models.Model): + is_active = models.BooleanField(default=False) + + def __str__(self): + return "Maintenance Mode" + +class ClienteCategoria(models.Model): + nombre = models.CharField(max_length=100) + descripcion = models.CharField(max_length=200, blank=True, null=True) + + def __str__(self): + return self.nombre + +class SistemaOperativo(models.Model): + categoria = models.ForeignKey(ClienteCategoria, related_name='sistemas_operativos', on_delete=models.CASCADE) + nombre = models.CharField(max_length=100) + url_descarga = models.URLField() + + def __str__(self): + return f"{self.nombre} - {self.categoria.nombre}" + +class ServerSelection(models.Model): + server_id = models.IntegerField() + name = models.CharField(max_length=100) + address = models.CharField(max_length=100) + port = models.IntegerField() + gamebuild = models.IntegerField() + + def __str__(self): + expansion = 'WotLK' if self.gamebuild == 12340 else 'Cataclysm' if self.gamebuild == 15595 else 'Unknown' + return f"{self.name} ({expansion})" + +class DownloadClientPage(models.Model): + title = models.CharField(max_length=255, default="Descarga del cliente") + content = CKEditor5Field('Contenido', config_name='default') + mac_instructions = CKEditor5Field('Instrucciones para macOS', blank=True, null=True, config_name='default') + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + +class ContentCreator(models.Model): + title = models.CharField(max_length=200) + description = CKEditor5Field('Descripción', config_name='default') + content_type = models.CharField(max_length=60, default="Contenido general") + subtitle = models.CharField(max_length=60, default="Nova WoW") + youtube_video_url = models.URLField(max_length=300, blank=True, null=True) + avatar_image_url = models.URLField(max_length=300, blank=True, null=True) + youtube_channel_url = models.URLField(max_length=300, blank=True, null=True) + facebook_page_url = models.URLField(max_length=300, blank=True, null=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.titles diff --git a/home/models/store.py b/home/models/store.py new file mode 100644 index 0000000..113f2c6 --- /dev/null +++ b/home/models/store.py @@ -0,0 +1,44 @@ +from django.db import models + + +class Category(models.Model): + name = models.CharField(max_length=100, unique=True) + + def __str__(self): + return self.name + +class Item(models.Model): + category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="items") + name = models.CharField(max_length=200) + price = models.DecimalField(max_digits=10, decimal_places=2) # Precio en euros + item_id = models.IntegerField(unique=True) # ID del ítem en el juego + image_url = models.URLField() # URL de la imagen del ítem + + def __str__(self): + return self.name + +class StripeLog(models.Model): + account_id = models.IntegerField() # ID de la cuenta en acore_auth + username = models.CharField(max_length=100) # Nombre de la cuenta + email = models.EmailField() # Correo electrónico + acore_ip = models.GenericIPAddressField() # IP de acore_auth + stripe_ip = models.GenericIPAddressField(blank=True, null=True) # IP detectada por Stripe + product_name = models.TextField() # Producto comprado + amount = models.DecimalField(max_digits=10, decimal_places=2) # Precio total + session_id = models.CharField(max_length=255) # ID de la sesión de Stripe + mode = models.CharField(max_length=10, choices=[("test", "Test"), ("live", "Live")]) # Modo de Stripe + character_name = models.CharField(max_length=100) # Nombre del personaje que compró + timestamp = models.DateTimeField(auto_now_add=True) # Fecha y hora + fulfilled = models.BooleanField(default=False) # Si la entrega ya se procesó (evita re-entregas) + + def __str__(self): + return f"{self.username} compró {self.product_name} por {self.amount}€ ({self.mode})" + +class Pedido(models.Model): + transaction_id = models.CharField(max_length=100, unique=True, null=True, blank=True) + email_cliente = models.EmailField() + estado = models.CharField(max_length=20, default="Pendiente") + monto = models.DecimalField(max_digits=10, decimal_places=2) + + def __str__(self): + return f"Pedido {self.transaction_id} - {self.estado}" diff --git a/home/models/voting.py b/home/models/voting.py new file mode 100644 index 0000000..d65ca06 --- /dev/null +++ b/home/models/voting.py @@ -0,0 +1,35 @@ +from django.db import models + + +class VoteSite(models.Model): + name = models.CharField(max_length=100, unique=True) + url = models.URLField(max_length=200, help_text="URL del sitio de votación") + image_url = models.URLField(max_length=300, help_text="URL completa de la imagen") + points = models.IntegerField(default=1, help_text="Puntos otorgados por votar") + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.name + +class VoteLog(models.Model): + account_id = models.IntegerField() + vote_site = models.ForeignKey('VoteSite', on_delete=models.CASCADE) + created_at = models.DateTimeField(auto_now_add=True) + last_vote_time = models.DateTimeField(null=True, blank=True) + processed = models.BooleanField(default=False) + processed_at = models.DateTimeField(null=True, blank=True) + + def __str__(self): + return f"Voto para {self.vote_site.name} por cuenta {self.account_id}" + +class HomeApiPoints(models.Model): + accountID = models.IntegerField(unique=True) + vp = models.IntegerField(default=0) + dp = models.IntegerField(default=0) + + class Meta: + db_table = 'home_api_points' + + def __str__(self): + return f"Cuenta {self.accountID} - PV: {self.vp}, DP: {self.dp}"