Files
NightSpire/home/views/account.py
T
Inna b3184fbdfe Divide home/views.py en el paquete home/views/ por secciones
El módulo monolítico (~3200 líneas) se convierte en un paquete cuyo
__init__.py re-exporta todas las vistas, de modo que `views.X`,
`from home.views import X` y la cadena 'home.views.not_found' (handler404)
siguen funcionando sin tocar urls.py.

Módulos por sección: _base (imports comunes + require_paid_stripe), auth,
pages, account, recruit, characters, store, points, guild, history,
players y payments. Cada submódulo hereda los imports con
`from ._base import *`; las dependencias entre secciones se resuelven con
imports explícitos (guild->auth, recruit/history->account).

Se eliminan dos definiciones duplicadas que quedaban muertas al cargar:
- get_character_image (primera copia, idéntica a la efectiva)
- store_novawow_success_view (stub sin SOAP, pisado por la versión real)

Validado con `manage.py check` (0 issues), resolución de las 100 rutas y
un chequeo estático de nombres no definidos por módulo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:09:40 +00:00

269 lines
9.7 KiB
Python

from ._base import *
dias_semana = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo']
meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']
def get_class_css(class_id):
class_map = {
1: 'warrior',
2: 'paladin',
3: 'hunter',
4: 'rogue',
5: 'priest',
6: 'death-knight',
7: 'shaman',
8: 'mage',
9: 'warlock',
11: 'druid'
}
return class_map.get(class_id, 'unknown')
def get_class_text_css(class_id):
text_class_map = {
1: 'warrior',
2: 'paladin',
3: 'hunter',
4: 'rogue',
5: 'priest',
6: 'death-knight',
7: 'shaman',
8: 'mage',
9: 'warlock',
11: 'druid'
}
return text_class_map.get(class_id, 'unknown')
def formatear_fecha(fecha):
"""Formatea la fecha al estilo solicitado."""
dia_semana = dias_semana[fecha.weekday()]
mes = meses[fecha.month - 1]
return f"{dia_semana} {fecha.day:02d} de {mes} del {fecha.year}"
def my_account(request):
# Verificar si la sesión contiene la cuenta de juego seleccionada
username = request.session.get('username')
# Si aún no se ha elegido cuenta de juego pero hay login Battle.net,
# enviar al selector; si no, al inicio de sesión.
if not username:
if request.session.get('bnet_id'):
return redirect('select-account')
return redirect('login')
is_logged_in = True # Si el usuario está en la sesión, asumimos que está conectado
# Obtener información básica de la cuenta
with connections['acore_auth'].cursor() as cursor:
cursor.execute("""
SELECT id, username, reg_mail, email, last_ip, last_attempt_ip, joindate
FROM account
WHERE username = %s
""", [username])
account_data = cursor.fetchone()
if not account_data:
return redirect('index')
account_id = account_data[0]
# Obtener los personajes asociados a la cuenta
characters = get_account_characters(account_id)
has_characters = bool(characters)
# Obtener el estado de la cuenta
account_status = get_account_status(account_id)
# Obtener los puntos (DP y VP) del usuario desde la tabla `home_api_points`
user_points = HomeApiPoints.objects.filter(accountID=account_id).first()
dp = user_points.dp if user_points else 0
vp = user_points.vp if user_points else 0
# Obtener el estado del token de seguridad
security_token = SecurityToken.objects.filter(user_id=account_id).first()
if security_token:
token_status = "Solicitado"
token_date = security_token.created_at.strftime('%H:%M:%S %d-%m-%Y')
else:
token_status = "Sin solicitar"
token_date = None
# Créditos Battlepay de la cuenta Battle.net
with connections['acore_auth'].cursor() as cursor:
battlepay_credits = bnet.get_battlepay_credits(cursor, request.session.get('bnet_id'))
return render(request, 'my-account/my-account.html', {
'is_logged_in': is_logged_in,
'battlepay_credits': battlepay_credits,
'user_info': {
'username': account_data[1],
'reg_mail': account_data[2],
'email': account_data[3],
'last_ip': account_data[4],
'last_attempt_ip': account_data[5],
'joindate': formatear_fecha(account_data[6]),
},
'account_status': account_status,
'characters': characters,
'has_characters': has_characters,
'dp': dp,
'vp': vp,
'token_status': token_status,
'token_date': token_date
})
def get_account_status(account_id):
with connections['acore_auth'].cursor() as cursor:
# Obtener información de la tabla 'account'
cursor.execute("""
SELECT username, reg_mail, email, last_ip, last_attempt_ip, joindate, recruiter
FROM account
WHERE id = %s
""", [account_id])
account_data = cursor.fetchone()
if not account_data:
return None
# Verificar si la cuenta está baneada
cursor.execute("""
SELECT unbandate, UNIX_TIMESTAMP()
FROM account_banned
WHERE id = %s AND active = 1
""", [account_id])
ban_data = cursor.fetchone()
# Obtener el número de amigos reclutados
cursor.execute("""
SELECT id
FROM account
WHERE recruiter = %s
""", [account_id])
recruited_accounts = cursor.fetchall()
recruited_count = len(recruited_accounts)
# Contar cuántos personajes de amigos reclutados han alcanzado el nivel 80
if recruited_count > 0:
recruited_ids = [acc[0] for acc in recruited_accounts]
with connections['acore_characters'].cursor() as char_cursor:
char_cursor.execute("""
SELECT COUNT(*)
FROM characters
WHERE account IN %s AND level = 80
""", [tuple(recruited_ids)])
level_80_count = char_cursor.fetchone()[0]
else:
level_80_count = 0
# Asignar los datos a variables
user_info = {
'username': account_data[0] if account_data[0] else 'No disponible',
'reg_mail': account_data[1] if account_data[1] else 'No disponible',
'email': account_data[2] if account_data[2] else 'No disponible',
'last_ip': account_data[3] if account_data[3] else 'Nunca',
'last_attempt_ip': account_data[4] if account_data[4] else 'Nunca',
'joindate': account_data[5].strftime("%A %d de %B del %Y a las %H:%M:%S Horas") if account_data[5] else 'No disponible',
'is_recruited': bool(account_data[6]),
'recruited_count': recruited_count,
'recruited_level_80_count': level_80_count
}
# Información sobre la suspensión, si existe
if ban_data:
unban_timestamp, current_timestamp = ban_data
remaining_time = unban_timestamp - current_timestamp
user_info['is_banned'] = True
user_info['unban_date'] = datetime.fromtimestamp(unban_timestamp).strftime("%A %d de %B del %Y a las %H:%M:%S Horas")
user_info['remaining_time'] = remaining_time
else:
user_info['is_banned'] = False
return user_info
def get_character_image(class_id, race, gender):
"""
Devuelve la imagen del personaje basada en la raza, clase y género.
`gender`: 0 = Hombre, 1 = Mujer
"""
# Definir los nombres de archivos según el género
gender_suffix = 'male' if gender == 0 else 'female'
# Raza y clases compatibles
race_class_map = {
10: [2, 3, 4, 5, 6, 8, 9], # Elfo de Sangre
8: [1, 3, 4, 5, 6, 7, 8], # Trol
6: [1, 3, 6, 7, 11], # Tauren
5: [1, 4, 5, 6, 8, 9], # No-Muerto
2: [1, 3, 4, 6, 7, 9], # Orco
11: [1, 2, 3, 5, 6, 7, 8], # Draenei
7: [1, 4, 6, 8, 9], # Gnomo
4: [1, 3, 4, 5, 6, 11], # Elfo de la Noche
3: [1, 2, 3, 4, 5, 6], # Enano
1: [1, 2, 4, 5, 6, 8, 9] # Humano
}
# Verificar si la clase es compatible con la raza
if class_id not in race_class_map.get(race, []):
return 'nw-themes/nw-ryu/nw-images/nw-classes/unknown.webp' # Imagen por defecto si no es compatible
# Construir el nombre del archivo basado en la raza y el género
race_image_map = {
10: f"big-blood-elf-{gender_suffix}.webp",
8: f"big-troll-{gender_suffix}.webp",
6: f"big-tauren-{gender_suffix}.webp",
5: f"big-undead-{gender_suffix}.webp",
2: f"big-orc-{gender_suffix}.webp",
11: f"big-draenei-{gender_suffix}.webp",
7: f"big-gnome-{gender_suffix}.webp",
4: f"big-night-elf-{gender_suffix}.webp",
3: f"big-dwarf-{gender_suffix}.webp",
1: f"big-human-{gender_suffix}.webp"
}
# Devolver la imagen según la raza y género, o una imagen por defecto
return f"nw-themes/nw-ryu/nw-images/nw-races/{race_image_map.get(race, 'unknown.webp')}"
def get_account_characters(account_id):
"""Obtiene los personajes de la cuenta dada."""
with connections['acore_characters'].cursor() as cursor:
cursor.execute("""
SELECT name, level, money, race, class, zone, gender
FROM characters
WHERE account = %s
""", [account_id])
characters = cursor.fetchall()
character_list = []
for char in characters:
name, level, money, race, class_id, zone, gender = char
gold = money // 10000
silver = (money % 10000) // 100
copper = money % 100
# Obtener la clase CSS y la imagen antes de pasarla a la plantilla
class_css = get_class_css(class_id)
image_url = get_character_image(class_id, race, gender)
# Obtener el nombre de la zona usando la función get_zone_name
zone_name = get_zone_name(zone)
character_list.append({
'name': name,
'level': level,
'gold': gold,
'silver': silver,
'copper': copper,
'race': race,
'class_id': class_id,
'zone': zone_name,
'class_css': class_css,
'image_url': image_url
})
return character_list
def get_account_id(username):
# Consulta para obtener el account_id a partir del username
with connections['acore_auth'].cursor() as cursor:
cursor.execute("""
SELECT id
FROM account
WHERE username = %s
""", [username])
result = cursor.fetchone()
return result[0] if result else None