Registro / app.py
Yofran23's picture
Update app.py
b37a693 verified
Raw
History Blame Contribute Delete
13.1 kB
# --- Para la manipulación de datos ---
import pandas as pd
from datetime import datetime
from share_com import tips, regist, app_dir # Importa tus DataFrames
import faicons as fa
import plotly.express as px
from shiny.ui import page_navbar
from shiny import reactive, render
from shiny.express import input, ui
from shinywidgets import render_plotly
import share_com
from shiny import express as sx
from io import BytesIO
# --- Para gráficos e iconos ---
import share_com
print(share_com.tips.head())
bill_rng = tips['assocName'].unique()
asocc = regist['parentOrganization'].unique()
generos_unicos = regist['gender'].unique().tolist()[0:2]
discipline = regist['discipline'].unique().tolist()
discipline_compe = tips['discipline'].unique().tolist()
active_tips = tips['status'].unique().tolist()
value = [0,1,3]
discipline = [discipline[i] for i in value]
tips.info()
# Add page title and sidebar
ui.page_opts(
title="App with Data",
)
with ui.nav_panel("Competiciones"):
with ui.layout_sidebar():
with ui.sidebar(open="desktop"):
ui.img(src="FVF.png", width="250px")
ui.input_select(
"tourment",
"Selecciona una Opción Asociaciones",
["TODAS"] + list(bill_rng) ,
selected=["FVF"]
)
ui.input_checkbox_group(
"active", # Un ID diferente
"Estatus",
choices=active_tips,
selected=active_tips, # Por defecto, todas están seleccionadas
inline=True
)
ui.input_checkbox_group(
"disci_comp", # Un ID diferente
"Disciplina",
choices=discipline_compe,
selected=discipline_compe, # Por defecto, todas están seleccionadas
inline=True
)
ui.input_dark_mode()
ICONS = {
"user": fa.icon_svg("user", "regular"),
"wallet": fa.icon_svg("wallet"),
"currency-dollar": fa.icon_svg("dollar-sign"),
"ellipsis": fa.icon_svg("ellipsis"),
"gavel" : fa.icon_svg("gavel"),
"clipboard-list" : fa.icon_svg("clipboard-list")
}
with ui.layout_columns(fill=False):
with ui.value_box(showcase=ICONS["user"]):
"Total Competiciones"
@render.express
def total_competiciones():
# CORREGIDO: Nombre de variable simplificado
seleccion = input.tourment()
disciplina = input.disci_comp()
status = input.active()
d = tips.copy()
if seleccion != "TODAS":
d = d[d['assocName'] == seleccion ]
if status: # Nos aseguramos de que la lista de géneros no esté vacía
d = d[d['status'].isin(status)]
if disciplina:
d = d[d['discipline'].isin(disciplina)]
number = len(d['competitionTypeId'].unique())
number
with ui.value_box(showcase = ICONS['clipboard-list']):
"Cantidad de Asociaciones"
@render.express
def total_asociaciones():
# CORREGIDO: Nombre de variable simplificado
seleccion = input.tourment()
disciplina = input.disci_comp()
status = input.active()
d = tips.copy()
if seleccion != "TODAS":
d = d[d['assocName'] == seleccion]
if seleccion != "TODAS":
d = d[d['assocName'] == seleccion ]
if status: # Nos aseguramos de que la lista de géneros no esté vacía
d = d[d['status'].isin(status)]
if disciplina:
d = d[d['discipline'].isin(disciplina)]
number = len(d['assocId'].unique())
number
with ui.layout_columns(col_widths=[12,6,6]):
with ui.card(full_screen=True):
with ui.card_header(class_="d-flex justify-content-between align-items-center"):
"Cantidad de Competiciones por Asociación"
@render_plotly
def col_plot():
seleccion = input.tourment()
disciplina = input.disci_comp()
status = input.active()
d = tips.copy()
if seleccion != "TODAS":
d = d[d['assocName'] == seleccion ]
if status: # Nos aseguramos de que la lista de géneros no esté vacía
d = d[d['status'].isin(status)]
if disciplina:
d = d[d['discipline'].isin(disciplina)]
conteo_asociaciones = d['assocName'].value_counts().reset_index()
# Renombramos las columnas para mayor claridad en el gráfico
conteo_asociaciones.columns = ['Asociacion', 'Cantidad de Competiciones']
# Ordenamos los datos para que el gráfico se vea más limpio (de mayor a menor)
conteo_asociaciones = conteo_asociaciones.sort_values(
by='Cantidad de Competiciones',
ascending=False
)
fig = px.bar(
conteo_asociaciones,
x='Cantidad de Competiciones',
y='Asociacion',
title='Número de Competiciones por Asociación',
text_auto=True, # Pone los números en las barras
template='plotly_white', # Un tema limpio y profesional
color='Cantidad de Competiciones', # Colorea las barras según la cantidad
color_continuous_scale= px.colors.sequential.Blues
)
fig.update_layout(title_x=0.5) # Centra el título
fig.update_xaxes(visible=False) # Oculta el eje X (es redundante)
fig.update_yaxes(title=None, automargin=True)
fig.update_xaxes(tickangle=45)
return fig
with ui.nav_panel("Registro"):
with ui.layout_sidebar():
with ui.sidebar(open="desktop"):
ui.img(src="FVF.png", width="250px")
ui.input_select(
"select",
"Selecciona una Opción Asociaciones",
["TODAS"] + list(asocc) ,
selected=["TODAS"]
)
ui.input_checkbox_group(
"genero_multi", # Un ID diferente
"Selecciona Género(s)",
choices=generos_unicos,
selected=generos_unicos, # Por defecto, todas están seleccionadas
inline=True
)
ui.input_checkbox_group(
"disci", # Un ID diferente
"Disciplina",
choices=discipline,
selected=discipline, # Por defecto, todas están seleccionadas
inline=True
)
ui.input_dark_mode()
ICONS = {
"user": fa.icon_svg("user", "regular"),
"wallet": fa.icon_svg("wallet"),
"currency-dollar": fa.icon_svg("dollar-sign"),
"ellipsis": fa.icon_svg("ellipsis"),
"gavel" : fa.icon_svg("gavel"),
"clipboard-list" : fa.icon_svg("clipboard-list")
}
with ui.layout_columns(fill=False):
with ui.value_box(showcase=ICONS["user"]):
"Total Jugadores"
@render.express
def total_registro_():
# CORREGIDO: Nombre de variable simplificado
seleccion = input.select()
genre = input.genero_multi()
disci = input.disci()
c = regist.copy()
if seleccion != "TODAS":
c = c[c['parentOrganization'] == seleccion]
# 2. CORRECCIÓN: Filtramos por género usando .isin()
if genre: # Nos aseguramos de que la lista de géneros no esté vacía
c = c[c['gender'].isin(genre)]
if disci:
c = c[c['discipline'].isin(disci)]
# 3. Filtramos por categoría "Jugador" y status "ACTIVO"
jugadores_filtrados = c[
(c['registrationCategory'] == "Jugador") &
(c['status'] == "ACTIVO")
]
# Elimina duplicados de personas y cuenta el resultado
count = jugadores_filtrados['personId'].nunique()
count
with ui.value_box(showcase=ICONS["gavel"]):
"Total de Árbitros"
@render.express
def total_registro_ar():
# CORREGIDO: Nombre de variable simplificado
seleccion = input.select()
c = regist.copy()
def contar_registros_activos(df, categoria):
# Filtra por categoría y status en un solo paso
filtrado = df[
(df['registrationCategory'] == categoria) &
(df['status'] == "ACTIVO")
]
# Elimina duplicados de personas y cuenta el resultado
cantidad = filtrado.drop_duplicates(subset='personId')['personId'].nunique()
return cantidad
if seleccion != "TODAS":
c = c[c['parentOrganization'] == seleccion]
arbitro = contar_registros_activos(c, "Árbitro")
arbitro
with ui.value_box(showcase=ICONS["clipboard-list"]):
"Total de Entrenadores"
@render.express
def total_registro_entre():
seleccion = input.select()
seleccion_disc = input.disci()
seleccion_genero = input.genero_multi()
c = regist.copy()
if seleccion != "TODAS":
c = c[c['parentOrganization'] == seleccion ]
if seleccion_genero: # Nos aseguramos de que la lista de géneros no esté vacía
c = c[c['gender'].isin(seleccion_genero)]
if seleccion_disc:
c = c[c['discipline'].isin(seleccion_disc)]
def contar_registros_activos(df, categoria):
# Filtra por categoría y status en un solo paso
filtrado = df[
(df['registrationCategory'] == categoria) &
(df['status'] == "ACTIVO")
]
# Elimina duplicados de personas y cuenta el resultado
cantidad = filtrado.drop_duplicates(subset='personId')['personId'].nunique()
return cantidad
if seleccion != "TODAS":
c = c[c['parentOrganization'] == seleccion]
entre = contar_registros_activos(c, "Entrenador")
entre
with ui.value_box(showcase=ICONS["user"]):
"Edad Promedio"
@render.express
def edad_promedio():
# CORREGIDO: Nombre de variable simplificado
seleccion = input.select()
seleccion_disc = input.disci()
seleccion_genero = input.genero_multi()
c = regist.copy()
if seleccion != "TODAS":
c = c[c['parentOrganization'] == seleccion ]
if seleccion_genero: # Nos aseguramos de que la lista de géneros no esté vacía
c = c[c['gender'].isin(seleccion_genero)]
if seleccion_disc:
c = c[c['discipline'].isin(seleccion_disc)]
edad_df = c.drop_duplicates(subset=['personId'])
edad_df = edad_df[edad_df['status'] == 'ACTIVO']
edad_df = edad_df.dropna(subset=['dateOfBirth'])
edad_df['dateOfBirth'] = pd.to_datetime(edad_df['dateOfBirth'])
hoy = datetime.now()
edad_df['edad'] = (hoy - edad_df['dateOfBirth']).dt.days / 365.25
edad_df['edad'] = edad_df['edad'].astype(int)
edad_promedio = edad_df['edad'].mean()
round(edad_promedio, 2)
with ui.layout_columns(col_widths=[12, 6, 6]):
with ui.card(full_screen=True):
with ui.card_header("Datos Evaluados"):
@render.data_frame
def tabla_registros():
# IMPORTANTE: @render.data_frame SÍ necesita un 'return'.
return render.DataGrid(datos_filtrados()[['personId', 'firstName', 'lastName','orgName',
'status','registrationCategory', 'parentOrganization','discipline']].head())
@reactive.calc
def datos_filtrados():
seleccion_disc = input.disci()
seleccion_asoc = input.select()
seleccion_genero = input.genero_multi()
c = regist.copy()
if seleccion_asoc!= "TODAS":
c = c[c['parentOrganization'] == seleccion_asoc]
# 2. CORRECCIÓN: Filtramos por género usando .isin()
if seleccion_genero: # Nos aseguramos de que la lista de géneros no esté vacía
c = c[c['gender'].isin(seleccion_genero)]
if seleccion_disc:
c = c[c['discipline'].isin(seleccion_disc)]
jugadores_filtrados = c[c['status'] == "ACTIVO"]
return jugadores_filtrados