Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/BrandonCVale/SISTEMA-HABITOS/llms.txt

Use this file to discover all available pages before exploring further.

Hábito. includes a per-habit progress visualization that gives users a clear picture of their consistency over the last 30 days. The chart is built entirely on the server using Plotly Express, serialised to a self-contained HTML snippet, and injected into the Jinja2 template without any client-side charting code. The approach keeps the frontend simple — no JavaScript build step, no extra API calls — while still delivering a fully interactive chart that users can hover, zoom, and inspect.

Accessing the Chart

Route: GET /habitos/visualizar_progreso/<id> The route first fetches the habit by ID, then enforces ownership before generating anything:
habito = HabitoRepository.obtener_habito_por_id(id_habito)
if not habito or habito.usuario_id != current_user.id:
    flash("Hábito no encontrado o no tienes permiso para ver el progreso", "error")
    return redirect(url_for('habitos.mis_habitos'))
If the habit belongs to current_user, the route calls EstadisticasService.generar_grafica_ultimos_dias, passing the habit’s full registros relationship list:
from app.services.estadisticas_grafica import EstadisticasService
grafica_html = EstadisticasService.generar_grafica_ultimos_dias(habito.registros)
The resulting HTML string is forwarded to the template:
return render_template('visualizar_progreso.html',
                       usuario=current_user,
                       habito=habito,
                       grafica_html=grafica_html)

How the Chart is Built

EstadisticasService.generar_grafica_ultimos_dias takes the list of RegistroHabito objects for one habit and produces a Plotly line chart. Here is the complete function as it appears in the source:
@staticmethod
def generar_grafica_ultimos_dias(registros, dias=30):
    """Genera una gráfica de barras mostrando el progreso de los últimos 'X' o '15' días."""
    hoy = date.today()  # ej. yyyy-mm-dd

    # 1. Crear lista de los últimos 14 días (del más antiguo al de hoy)
    # range(inicio, fin, salto) - timedelta es una función matemática para fechas.
    ultimos_dias = [hoy - timedelta(days=i) for i in range(dias - 1, -1, -1)]

    # 2. Extraer solo las fechas en las que el hábito sí se completó
    fechas_completadas = {registro.fecha for registro in registros if registro.completado}

    # 3. Construir Eje X (Días) y Eje Y (1 si completó, 0 si falló)
    eje_x = [d.strftime("%d/%m") for d in ultimos_dias]
    eje_y = [1 if d in fechas_completadas else 0 for d in ultimos_dias]

    # 4. Crear la gráfica interactiva con Plotly Express
    fig = px.line(
        x=eje_x,
        y=eje_y,
        title=f"Progreso de los últimos {dias} días",
        markers=True,
        color_discrete_sequence=['#235336']
    )

    # 5. Personalizar el diseño para que luzca limpio y combine con tu CSS
    fig.update_layout(
        yaxis=dict(tickvals=[0, 1], ticktext=['Fallado', 'Completado']),
        coloraxis_showscale=False,  # Ocultar la barra lateral de colores
        plot_bgcolor='rgba(0,0,0,0)',  # Fondo transparente
        paper_bgcolor='rgba(0,0,0,0)',
        margin=dict(l=20, r=20, t=40, b=20)
    )

    # 6. Convertir a HTML crudo (incluyendo el código JS de Plotly automáticamente)
    return fig.to_html(full_html=False, include_plotlyjs='cdn')
Step-by-step breakdown:
1

Build the date window

ultimos_dias is a list of date objects covering the last dias days (default 30), ordered from oldest to most recent. The list comprehension counts down from dias - 1 to 0, subtracting timedelta(days=i) from today each iteration.
2

Extract completed dates

fechas_completadas is a Python set built from the RegistroHabito records where completado == True. Using a set gives O(1) membership testing in the next step.
3

Construct the axes

  • eje_x — each date formatted as "DD/MM" (e.g. "04/07") for a readable x-axis label.
  • eje_y1 if the corresponding date is in fechas_completadas, 0 otherwise. This binary representation maps to the y-axis tick labels 'Completado' and 'Fallado'.
4

Create the Plotly figure

px.line draws a line chart with dot markers. The line colour is #235336 (deep forest green), matching the app’s brand palette. markers=True adds a visible dot at each data point so individual days are easy to read.
5

Customise the layout

Several layout properties are applied to integrate the chart cleanly with the app’s CSS:
PropertyValueEffect
yaxis.tickvals[0, 1]Only show two ticks on the y-axis
yaxis.ticktext['Fallado', 'Completado']Human-readable labels instead of numbers
plot_bgcolorrgba(0,0,0,0)Transparent plot area
paper_bgcolorrgba(0,0,0,0)Transparent outer background
marginl=20, r=20, t=40, b=20Compact margins
6

Serialise to HTML

fig.to_html(full_html=False, include_plotlyjs='cdn') produces a partial HTML string — just the <div> and the inline <script> for this chart, without a full <!DOCTYPE html> wrapper. Plotly’s JavaScript library is loaded from a CDN rather than bundled inline, keeping the response payload small.

Rendering in the Template

The grafica_html string is injected directly into the Jinja2 template using the safe filter, which tells Jinja2 to render the raw HTML without escaping it:
{{ grafica_html | safe }}
Because the chart carries a transparent background, it inherits whatever background colour the surrounding <div> provides, blending seamlessly with the page’s CSS without any extra styling.
The chart defaults to the last 30 days, but the dias parameter in generar_grafica_ultimos_dias can be changed programmatically when calling the service. For example, to show a 7-day or 90-day window, pass the desired value directly:
grafica_html = EstadisticasService.generar_grafica_ultimos_dias(habito.registros, dias=7)
include_plotlyjs='cdn' instructs Plotly to load its JavaScript library from Plotly’s CDN (https://cdn.plot.ly/plotly-*.min.js) rather than embedding the full library (~3 MB) inline in every response. An active internet connection is required to view the interactive chart. In offline or air-gapped environments, switch to include_plotlyjs=True to bundle Plotly’s JS inline, at the cost of a larger HTML payload.

Build docs developers (and LLMs) love