b3184fbdfe
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>
269 lines
9.7 KiB
Python
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
|