datadis_python.utils package

Submodules

Module contents

Módulo de utilidades y constantes para el SDK de Datadis.

Este módulo proporciona funciones y constantes comunes utilizadas en el SDK de Datadis.

author:

TacoronteRiveroCristian

datadis_python.utils.validate_date_range(date_from, date_to, format_type='monthly')[fuente]

Valida un rango de fechas según las restricciones específicas de Datadis.

Esta función implementa todas las reglas de validación temporal específicas de la API de Datadis, incluyendo formatos aceptados, rangos permitidos y limitaciones de acceso a datos históricos.

Restricciones de Datadis implementadas:
  • Rango temporal: Solo últimos 2 años desde la fecha actual

  • No fechas futuras: Las fechas no pueden ser posteriores al día actual

  • Orden lógico: date_from debe ser anterior o igual a date_to

  • Formatos específicos: YYYY/MM para mensual, YYYY/MM/DD para diario

Formatos soportados:
  • Mensual: «YYYY/MM» (ej: «2024/01») - Recomendado para Datadis

  • Diario: «YYYY/MM/DD» (ej: «2024/01/15») - Limitado en algunos endpoints

Validaciones aplicadas:
  1. Formato de entrada: Verificación contra patrones regex específicos

  2. Parseabilidad: Verificación de fechas válidas (no 30 de febrero)

  3. Orden lógico: date_from ≤ date_to

  4. Límite histórico: No más de 2 años hacia atrás

  5. Límite futuro: No fechas posteriores a hoy

Parámetros:
  • date_from (str) – Fecha de inicio del rango en formato string

  • date_to (str) – Fecha de fin del rango en formato string

  • format_type (str) – Tipo de formato («monthly» para YYYY/MM).

Devuelve:

Tupla con las fechas validadas (date_from, date_to)

Tipo del valor devuelto:

Tuple[str, str]

Muestra:

ValidationError – Si cualquier validación falla

Ejemplo

Validaciones exitosas:

# Rango mensual válido
validate_date_range("2024/01", "2024/12", "monthly")
# → ("2024/01", "2024/12")

# Rango diario válido
validate_date_range("2024/01/01", "2024/01/31", "daily")
# → ("2024/01/01", "2024/01/31")

# Rango de un mes
validate_date_range("2024/06", "2024/06", "monthly")
# → ("2024/06", "2024/06")

Casos que fallan (errores esperados):

# ❌ Formato incorrecto
validate_date_range("2024-01", "2024-12", "monthly")
# → ValidationError: "Formato de fecha_desde inválido: 2024-01. Use 2024/01"

# ❌ Rango invertido
validate_date_range("2024/12", "2024/01", "monthly")
# → ValidationError: "fecha_desde no puede ser posterior a fecha_hasta"

# ❌ Demasiado antigua (más de 2 años)
validate_date_range("2020/01", "2020/12", "monthly")
# → ValidationError: "fecha_desde no puede ser anterior a hace 2 años"

# ❌ Fecha futura
validate_date_range("2030/01", "2030/12", "monthly")
# → ValidationError: "fecha_hasta no puede ser futura"

# ❌ Fecha inválida
validate_date_range("2024/02/30", "2024/02/30", "daily")
# → ValidationError: "Fecha inválida: day is out of range for month"

Uso típico en métodos del SDK:

def get_consumption(self, date_from, date_to):
    # Validación robusta antes de petición API
    validated_from, validated_to = validate_date_range(
        date_from, date_to, "monthly"
    )
    # Las fechas están garantizadas como válidas
    params = {"dateFrom": validated_from, "dateTo": validated_to}

Nota

El límite de 2 años se basa en las limitaciones actuales de Datadis para acceso a datos históricos. Este límite puede cambiar en el futuro según las políticas de la plataforma.

Performance:

La validación incluye cálculos de fecha para verificar límites temporales. Para uso intensivo, considere cachear los límites calculados.

Technical Details:
  • Regex patterns: Específicos para cada formato (daily/monthly)

  • Parsing: Usa datetime.strptime() para validación completa

  • Límites dinámicos: Calculados en tiempo real basados en datetime.now()

  • Timezone: Usa timezone local del sistema

Ver también

Added in version 1.0: Validación básica de rangos de fechas

Distinto en la versión 2.0: Añadidas validaciones específicas para limitaciones de Datadis

datadis_python.utils.validate_distributor_code(distributor_code)[fuente]

Valida códigos de distribuidor eléctrico españoles oficiales.

Parámetros:

distributor_code (str) – Código de distribuidor a validar

Devuelve:

Código de distribuidor validado

Tipo del valor devuelto:

str

Muestra:

ValidationError – Si el código no corresponde a un distribuidor válido

Ejemplo

Códigos válidos:

# Distribuidores nacionales principales
validate_distributor_code("2")  # → "2" (E-distribución/Endesa)
validate_distributor_code("5")  # → "5" (UFD/Naturgy)

# Distribuidores regionales
validate_distributor_code("1")  # → "1" (Viesgo - Norte)
validate_distributor_code("3")  # → "3" (E-redes - Galicia)
validate_distributor_code("6")  # → "6" (EOSA - Aragón)

# Distribuidores insulares/locales
validate_distributor_code("8")  # → "8" (IDE - Baleares)
validate_distributor_code("4")  # → "4" (ASEME - Melilla)
validate_distributor_code("7")  # → "7" (CIDE - Ceuta)

Casos que fallan (errores esperados):

# ❌ Código inexistente
validate_distributor_code("9")
# → ValidationError: "Código de distribuidor inválido: 9. Válidos: 1, 2, 3, 4, 5, 6, 7, 8"

# ❌ Código múltiple
validate_distributor_code("12")
# → ValidationError: "Código de distribuidor inválido: 12..."

# ❌ Código negativo
validate_distributor_code("-1")
# → ValidationError: "Código de distribuidor inválido: -1..."

Uso con constantes descriptivas:

from datadis_python.utils.constants import DISTRIBUTOR_CODES

# Usar nombres descriptivos en lugar de números
endesa_code = DISTRIBUTOR_CODES["E_DISTRIBUCION"]  # "2"
validated = validate_distributor_code(endesa_code)  # "2"

# Iterar sobre todos los distribuidores válidos
for name, code in DISTRIBUTOR_CODES.items():
    validated_code = validate_distributor_code(code)
    print(f"{name}: {validated_code}")

Nota

Los códigos de distribuidor son siempre strings en la API de Datadis, aunque representen números. Use convert_distributor_code_parameter() si necesita conversión automática desde enteros.

Technical Details:
  • Lista estática: Los 8 códigos están hardcodeados (no cambian frecuentemente)

  • Validación rápida: Lookup en lista pequeña, muy eficiente

  • Case sensitive: Los códigos deben ser exactamente como se especifican

  • No conversión: Esta función no convierte tipos, solo valida

Referencias

  • CNE: Comisión Nacional de Energía - Listado oficial de distribuidores

  • CNMC: Comisión Nacional de los Mercados y la Competencia

  • Datadis: Documentación oficial de códigos soportados

Ver también

Added in version 1.0: Validación de códigos de distribuidor

Distinto en la versión 2.0: Actualizada lista con información geográfica detallada

datadis_python.utils.convert_cups_parameter(cups)[fuente]

Procesa y valida un código CUPS para la API de Datadis.

Los códigos CUPS (Código Universal del Punto de Suministro) son identificadores únicos para puntos de suministro eléctrico en España. Esta función valida que el código tenga el formato correcto antes de enviarlo a la API.

Formato CUPS español:
  • Prefijo: Siempre «ES» (código país)

  • Longitud: 20-22 caracteres alfanuméricos después del prefijo

  • Caracteres: Solo letras mayúsculas y números

  • Ejemplo: "ES0031607515707001RC0F"

Validaciones aplicadas:
  • Tipo correcto: Debe ser string

  • Formato válido: Cumplir patrón regex de CUPS españoles

  • Longitud correcta: Entre 22-24 caracteres totales (ES + 20-22)

Parámetros:

cups (str) – Código CUPS a validar. Debe ser un string con formato válido

Devuelve:

Código CUPS validado y limpio (sin espacios extra)

Tipo del valor devuelto:

str

Muestra:

ValidationError – Si el CUPS no cumple con el formato esperado o no es string

Ejemplo

Códigos CUPS válidos:

# Formato típico (20 caracteres después de ES)
convert_cups_parameter("ES0031607515707001RC0F")  # → "ES0031607515707001RC0F"

# Formato largo (22 caracteres después de ES)
convert_cups_parameter("ES1234567890123456789012")  # → "ES1234567890123456789012"

# Con espacios (se limpian automáticamente)
convert_cups_parameter(" ES0031607515707001RC0F ")  # → "ES0031607515707001RC0F"

Casos que fallan (errores esperados):

# ❌ Tipo incorrecto
convert_cups_parameter(123456)
# → ValidationError: "CUPS debe ser string, recibido: <class 'int'>"

# ❌ Formato inválido (sin prefijo ES)
convert_cups_parameter("0031607515707001RC0F")
# → ValidationError: "Formato CUPS inválido. Debe ser: ES + 20-22 caracteres alfanuméricos"

# ❌ Demasiado corto
convert_cups_parameter("ES12345")
# → ValidationError: "Formato CUPS inválido..."

# ❌ Caracteres inválidos
convert_cups_parameter("ES003160751570700@RC0F")
# → ValidationError: "Formato CUPS inválido..."

Uso típico en consultas:

def get_consumption(self, cups, ...):
    # Validar CUPS antes de enviar a API
    validated_cups = convert_cups_parameter(cups)

    # validated_cups está garantizado como válido
    params = {"cups": validated_cups}

Nota

Esta función realiza validación del formato pero no verifica que el CUPS exista realmente o esté asociado al usuario. Esa validación la realiza la API de Datadis en el servidor.

Technical Details:
  • Regex pattern: ^ES[A-Z0-9]{20,22}$

  • Case sensitivity: Solo acepta mayúsculas (conversión automática)

  • Limpieza: Elimina espacios en blanco automáticamente

  • Delegación: Usa datadis_python.utils.validators.validate_cups() internamente

Ver también

Added in version 1.0: Validación de formato CUPS integrada en el SDK

datadis_python.utils.convert_date_range_to_api_format(date_from, date_to, format_type='daily')[fuente]

Convierte y valida un rango de fechas para la API de Datadis.

Esta función es un wrapper de alto nivel que combina conversión de tipos y validación de rangos para asegurar que tanto las fechas individuales como el rango completo cumplan con los requisitos de la API de Datadis.

Proceso de validación en dos etapas:
  1. Conversión individual: Cada fecha se convierte usando convert_date_to_api_format()

  2. Validación de rango: El rango se valida usando validate_date_range

Validaciones aplicadas:
  • Formato individual: Cada fecha debe tener formato válido

  • Consistencia de rango: date_from no puede ser posterior a date_to

  • Límites temporales: Cumplir restricciones de Datadis (últimos 2 años)

  • Restricciones de modo: Solo formatos mensuales si format_type="monthly"

Parámetros:
  • date_from (Union[str, datetime, date]) – Fecha de inicio del rango. Acepta múltiples formatos

  • date_to (Union[str, datetime, date]) – Fecha de fin del rango. Acepta múltiples formatos

  • format_type (str) –

    Tipo de formato de salida:

    • »monthly»: YYYY/MM (recomendado para Datadis)

    • »daily»: YYYY/MM/DD (limitado en Datadis)

Devuelve:

Tupla con fechas convertidas y validadas (date_from_str, date_to_str)

Tipo del valor devuelto:

tuple[str, str]

Muestra:

ValidationError – Si cualquier fecha es inválida o el rango no cumple restricciones

Ejemplo

Rangos típicos para consultas mensuales:

from datetime import date

# Objetos date → strings de API
start = date(2024, 1, 1)
end = date(2024, 12, 31)
converted = convert_date_range_to_api_format(start, end, "monthly")
# Resultado: ("2024/01", "2024/12")

# Strings en formatos mixtos
converted = convert_date_range_to_api_format(
    "2024-01-01",  # Formato ISO
    "2024/12",     # Formato API
    "monthly"
)
# Resultado: ("2024/01", "2024/12")

Rangos para consultas diarias (limitado):

# Solo algunos endpoints aceptan fechas específicas
converted = convert_date_range_to_api_format(
    "2024/01/01",
    "2024/01/31",
    "daily"
)
# Resultado: ("2024/01/01", "2024/01/31")

Casos que fallan (errores esperados):

# ❌ Rango invertido
convert_date_range_to_api_format("2024/12", "2024/01", "monthly")
# → ValidationError: "date_from no puede ser posterior a date_to"

# ❌ Fechas específicas en modo mensual
convert_date_range_to_api_format("2024/01/15", "2024/01/20", "monthly")
# → ValidationError: "API solo acepta fechas mensuales"

Uso típico en métodos del SDK:

def get_consumption(self, date_from, date_to):
    # Conversión flexible + validación automática
    api_from, api_to = convert_date_range_to_api_format(
        date_from, date_to, "monthly"
    )

    # api_from y api_to están garantizados como válidos
    params = {"dateFrom": api_from, "dateTo": api_to}

Nota

Esta función es la forma recomendada de procesar todos los rangos de fechas en el SDK, ya que combina flexibilidad de entrada con validación robusta.

Performance:

La función realiza validación completa en cada llamada. Para uso intensivo con fechas ya validadas, considere llamar directamente las funciones de conversión individual.

Ver también

Distinto en la versión 2.0: Añadida validación estricta para prevenir fechas específicas en modo mensual

datadis_python.utils.convert_date_to_api_format(date_value, format_type='daily')[fuente]

Convierte fechas de múltiples formatos al formato requerido por la API de Datadis.

Esta función es el núcleo del sistema de conversión de fechas del SDK. Acepta fechas en múltiples formatos naturales de Python y las convierte al formato específico requerido por la API de Datadis, aplicando validaciones estrictas para prevenir errores comunes.

Limitaciones específicas de Datadis:
  • Solo fechas mensuales: Para la mayoría de endpoints, solo se acepta YYYY/MM

  • No fechas específicas: Fechas como YYYY/MM/DD se rechazan en modo mensual

  • Rango temporal limitado: Solo últimos 2 años (validado por otros componentes)

Formatos de entrada soportados:
  • Strings: «2024/01», «2024-01-15», «20240115», «15/01/2024»

  • datetime objects: datetime(2024, 1, 15) → «2024/01» (modo mensual)

  • date objects: date(2024, 1, 15) → «2024/01» (modo mensual)

Validaciones aplicadas:
  • Detección de días específicos: Rechaza YYYY/MM/DD en modo mensual

  • Parseo de múltiples formatos: Intenta varios formatos comunes automáticamente

  • Consistencia de modo: Asegura que el output coincida con el format_type

Parámetros:
  • date_value (Union[str, datetime, date]) – Fecha a convertir. Acepta strings en múltiples formatos, objetos datetime, o objetos date de Python

  • format_type (str) –

    Tipo de formato de salida requerido:

    • »monthly»: Formato YYYY/MM (recomendado para Datadis)

    • »daily»: Formato YYYY/MM/DD (limitado en Datadis)

Devuelve:

Fecha formateada según el format_type especificado y validada para cumplir con las restricciones de la API de Datadis

Tipo del valor devuelto:

str

Muestra:

ValidationError

En los siguientes casos:

  • Fecha contiene día específico en modo mensual

  • Formato de fecha no reconocible

  • Tipo de entrada no soportado

  • format_type no válido

Ejemplo

Conversiones típicas en modo mensual (recomendado):

# Strings en formato correcto (pasan sin cambios)
convert_date_to_api_format("2024/01", "monthly")  # → "2024/01"

# Objetos datetime (día se ignora en modo mensual)
from datetime import datetime
dt = datetime(2024, 1, 15)
convert_date_to_api_format(dt, "monthly")  # → "2024/01"

# Strings en formatos alternativos (se convierten)
convert_date_to_api_format("2024-01-15", "monthly")  # → "2024/01"
convert_date_to_api_format("15/01/2024", "monthly")  # → "2024/01"

Validaciones que fallan (errores esperados):

# ❌ Fecha específica en modo mensual
convert_date_to_api_format("2024/01/15", "monthly")
# → ValidationError: "API solo acepta fechas mensuales"

# ❌ datetime con día específico
dt = datetime(2024, 1, 15)  # día 15, no día 1
convert_date_to_api_format(dt, "monthly")
# → ValidationError: "Fecha contiene día específico"

Conversiones en modo daily:

# Para endpoints que sí aceptan fechas específicas
convert_date_to_api_format("2024/01/15", "daily")   # → "2024/01/15"
dt = datetime(2024, 1, 15)
convert_date_to_api_format(dt, "daily")             # → "2024/01/15"

Nota

La mayoría de endpoints de Datadis solo aceptan fechas mensuales (YYYY/MM). Use format_type="monthly" a menos que esté seguro de que el endpoint específico acepta fechas diarias.

Technical Details:
  • Detección inteligente: Analiza el formato de entrada automáticamente

  • Múltiples intentos: Prueba varios formatos comunes antes de fallar

  • Validación estricta: Previene errores sutiles por formato incorrecto

  • Retrocompatibilidad: Strings correctos pasan sin modificación

Ver también

Distinto en la versión 2.0: Añadida validación estricta para fechas específicas en modo mensual

datadis_python.utils.convert_distributor_code_parameter(distributor_code)[fuente]

Convierte y valida códigos de distribuidor para la API de Datadis.

Los códigos de distribuidor identifican las diferentes compañías distribuidoras eléctricas en España. Esta función acepta tanto enteros como strings para flexibilidad del usuario, y valida que el código sea uno de los oficialmente reconocidos por Datadis.

Distribuidores válidos (códigos oficiales):
  • «1»: Viesgo (Cantabria, Asturias)

  • «2»: E-distribución/Endesa (Nacional)

  • «3»: E-redes (Galicia)

  • «4»: ASEME (Melilla)

  • «5»: UFD/Naturgy (Nacional)

  • «6»: EOSA (Aragón)

  • «7»: CIDE (Ceuta)

  • «8»: IDE (Baleares)

Conversión automática:
  • Enteros: Se convierten a string (2"2")

  • Strings: Se validan y retornan si son correctos

  • Otros tipos: Se rechazan con error claro

Parámetros:

distributor_code (Union[str, int]) – Código del distribuidor. Acepta entero (ej: 2) o string (ej: "2") con código válido

Devuelve:

Código de distribuidor como string validado

Tipo del valor devuelto:

str

Muestra:

ValidationError – Si el código no es válido o es de tipo no soportado

Ejemplo

Conversiones exitosas:

# Enteros (forma natural para usuarios)
convert_distributor_code_parameter(2)      # → "2" (Endesa)
convert_distributor_code_parameter(5)      # → "5" (Naturgy)
convert_distributor_code_parameter(1)      # → "1" (Viesgo)

# Strings válidos (pasan sin cambios)
convert_distributor_code_parameter("2")    # → "2"
convert_distributor_code_parameter("8")    # → "8" (IDE Baleares)

Casos que fallan (errores esperados):

# ❌ Código inválido (no existe distribuidor 9)
convert_distributor_code_parameter(9)
# → ValidationError: "Código de distribuidor inválido: 9. Válidos: 1, 2, 3, 4, 5, 6, 7, 8"

# ❌ Código inválido como string
convert_distributor_code_parameter("99")
# → ValidationError: "Código de distribuidor inválido: 99..."

# ❌ Tipo no soportado
convert_distributor_code_parameter([1, 2])
# → ValidationError: "Código de distribuidor debe ser string o int, recibido: <class 'list'>"

Uso típico con constantes:

from datadis_python.utils.constants import DISTRIBUTOR_CODES

# Usuario puede usar nombres descriptivos
endesa_code = DISTRIBUTOR_CODES["E_DISTRIBUCION"]  # "2"
validated = convert_distributor_code_parameter(endesa_code)  # "2"

# O directamente números naturales
validated = convert_distributor_code_parameter(2)  # "2"

En métodos del SDK:

def get_supplies(self, distributor_code=None):
    if distributor_code is not None:
        # Conversión flexible + validación
        api_code = convert_distributor_code_parameter(distributor_code)
        params["distributorCode"] = api_code

Nota

Aunque la función acepta enteros para comodidad, la API de Datadis internamente espera todos los códigos como strings, por eso se hace la conversión automática.

Technical Details:

Ver también

Added in version 1.0: Soporte para conversión automática int → string

datadis_python.utils.convert_number_to_string(value)[fuente]

Convierte números de múltiples tipos a strings validados para la API de Datadis.

La API de Datadis espera parámetros numéricos como strings, pero los usuarios naturalmente quieren pasar enteros o floats. Esta función acepta cualquier tipo numérico y lo convierte a string después de validar que sea un número válido.

Validaciones aplicadas:
  • Strings: Verifica que representen números válidos antes de retornarlos

  • Enteros: Convierte directamente a string

  • Floats: Convierte a string con representación completa

  • Tipos inválidos: Rechaza con mensaje de error claro

Casos de uso típicos:
  • Códigos de distribuidor: 2"2"

  • Tipos de medida: 0"0" (consumo vs generación)

  • Tipos de punto: 1"1" (frontera, consumo, etc.)

  • IDs numéricos: Para cualquier identificador numérico de la API

Parámetros:

value (Union[str, int, float]) – Valor numérico a convertir. Acepta enteros, floats, o strings que representen números válidos

Devuelve:

Representación string del número, validada para ser numérica

Tipo del valor devuelto:

str

Muestra:

ValidationError – Si el valor no es numérico válido o es de tipo no soportado

Ejemplo

Conversiones exitosas:

# Enteros (caso más común)
convert_number_to_string(2)        # → "2"
convert_number_to_string(0)        # → "0"
convert_number_to_string(-1)       # → "-1"

# Floats
convert_number_to_string(2.5)      # → "2.5"
convert_number_to_string(0.0)      # → "0.0"

# Strings numéricos válidos (pasan sin cambios)
convert_number_to_string("2")      # → "2"
convert_number_to_string("2.5")    # → "2.5"

Casos que fallan (errores esperados):

# ❌ String no numérico
convert_number_to_string("abc")
# → ValidationError: "String no numérico: abc"

# ❌ Tipos no soportados
convert_number_to_string(None)
# → ValidationError: "Tipo numérico no soportado: <class 'NoneType'>"

convert_number_to_string([1, 2, 3])
# → ValidationError: "Tipo numérico no soportado: <class 'list'>"

Uso en contexto de Datadis:

# Flexibilidad para el usuario
distributor_code = 2  # int natural
api_param = convert_number_to_string(distributor_code)  # "2" para API

# Validación de entrada
user_input = "2.5"  # string del usuario
validated = convert_number_to_string(user_input)  # validado

Nota

La función preserva la precisión completa de los floats. Para casos donde necesite controlar el número de decimales, aplique redondeo antes de llamar esta función.

Technical Details:
  • Validación de strings: Usa float() para verificar que sean numéricos

  • Preservación de formato: Strings válidos se retornan sin modificación

  • Conversión directa: Enteros y floats se convierten con str()

  • Mensajes específicos: Errores incluyen el valor problemático para debugging

Ver también

datadis_python.utils.convert_optional_number_to_string(value)[fuente]

Convierte números opcionales a strings para la API de Datadis.

Esta función extiende convert_number_to_string() para manejar valores que pueden ser None, lo que es común en parámetros opcionales de la API. Mantiene la semántica de None (parámetro no especificado) mientras valida valores no-None.

Comportamiento:
  • None: Se retorna como None (parámetro opcional no especificado)

  • Valores válidos: Se procesan con convert_number_to_string()

  • Valores inválidos: Se rechazan con ValidationError

Casos de uso típicos:
  • point_type: Parámetro opcional en consultas de consumo

  • measurement_type: Parámetro opcional con valor por defecto

  • Filtros numéricos: Cualquier filtro numérico opcional

Parámetros:

value (Optional[Union[str, int, float]]) – Valor numérico opcional a convertir. Puede ser None, entero, float, o string que represente un número

Devuelve:

String representando el número si no es None, o None si es None

Tipo del valor devuelto:

Optional[str]

Muestra:

ValidationError – Si el valor no es None y no es numérico válido

Ejemplo

Manejo de parámetros opcionales:

# Valor None (parámetro no especificado)
convert_optional_number_to_string(None)       # → None

# Valores numéricos válidos
convert_optional_number_to_string(2)          # → "2"
convert_optional_number_to_string(2.5)        # → "2.5"
convert_optional_number_to_string("3")        # → "3"

# Valores inválidos (solo si no son None)
convert_optional_number_to_string("abc")      # → ValidationError

Uso en consultas de API:

# point_type es opcional
point_type = None  # Usuario no especifica
api_point_type = convert_optional_number_to_string(point_type)  # → None

# measurement_type especificado por usuario
measurement_type = 1  # Usuario especifica generación
api_measurement = convert_optional_number_to_string(measurement_type)  # → "1"

En métodos del cliente:

def get_consumption(self, point_type=None, measurement_type=0):
    # Convertir parámetros opcionales
    api_point_type = convert_optional_number_to_string(point_type)
    api_measurement = convert_optional_number_to_string(measurement_type)

    # Solo añadir a params si no son None
    params = {}
    if api_point_type is not None:
        params["pointType"] = api_point_type
    if api_measurement is not None:
        params["measurementType"] = api_measurement

Nota

Esta función es la forma recomendada de manejar todos los parámetros numéricos opcionales en el SDK, ya que mantiene la semántica clara entre «no especificado» (None) y «especificado con valor» (string).

Ver también

  • convert_number_to_string() para valores numéricos obligatorios

  • Documentación de la API de Datadis sobre parámetros opcionales

class datadis_python.utils.HTTPClient(timeout=60, retries=3)[fuente]

Bases: object

Cliente HTTP robusto especializado para la API de Datadis.

Esta clase proporciona una interfaz de alto nivel para realizar peticiones HTTP con características específicamente optimizadas para interactuar con la API de Datadis. Incluye manejo automático de reintentos, timeouts largos, gestión de autenticación y procesamiento especializado de respuestas.

Optimizaciones para Datadis:
  • Timeouts largos: Por defecto 60s, recomendado 90-120s para Datadis

  • Reintentos robustos: Backoff exponencial con hasta 5 reintentos

  • Rate limiting: Delays automáticos para evitar sobrecarga del servidor

  • Manejo de encoding: Desactiva compresión gzip para evitar problemas

  • Procesamiento de texto: Normalización automática de caracteres especiales

Estrategia de reintentos:
  • Errores de red y timeouts: Se reintentan automáticamente

  • Errores HTTP 4xx/5xx: Se propagan inmediatamente (no reintentos)

  • Backoff exponencial: 2s → 4s → 8s → 16s → 32s (máximo 10s para timeouts)

  • Error 401: Se propaga para permitir renovación de token

Ejemplo

Configuración típica para Datadis:

# Configuración robusta para API lenta de Datadis
client = HTTPClient(timeout=120, retries=5)

# Autenticación Bearer Token
client.set_auth_header("jwt_token_from_datadis")

# Petición con gestión automática de errores
response = client.make_request(
    method="GET",
    url="https://datadis.es/api-private/api/get-supplies",
    params={"distributor_code": "2"}
)

Diferentes tipos de contenido:

# Para autenticación (form-data)
auth_response = client.make_request(
    method="POST",
    url="https://datadis.es/nikola-auth/tokens/login",
    data={"username": "12345678A", "password": "password"},
    use_form_data=True
)

# Para endpoints de datos (JSON, por defecto)
data_response = client.make_request(
    method="GET",
    url="https://datadis.es/api-private/api/get-consumption-data",
    params={"cups": "ES001234567890123456AB"}
)
Parámetros:
  • timeout (int) – Timeout para peticiones HTTP en segundos. Recomendado: 90-120s para Datadis

  • retries (int) – Número máximo de reintentos automáticos para errores de red/timeouts

Nota

La API de Datadis puede ser muy lenta (60-90 segundos) al procesar consultas complejas que requieren agregar datos de múltiples distribuidoras eléctricas.

Ver también

  • SimpleDatadisClientV1 y SimpleDatadisClientV2 para uso de alto nivel

  • Documentación oficial de Datadis para límites de rate limiting

__enter__()[fuente]

Método de entrada para context manager.

Permite usar el HTTPClient con la declaración with de Python para gestión automática de recursos. Al entrar en el bloque with, retorna la instancia del cliente lista para usar.

Devuelve:

La instancia del cliente HTTP configurada

Tipo del valor devuelto:

HTTPClient

Ejemplo

Uso como context manager:

with HTTPClient(timeout=120, retries=5) as client:
    # Configurar autenticación
    token = client.make_request(
        "POST", "/auth",
        data={"user": "12345678A", "pass": "password"},
        use_form_data=True
    )
    client.set_auth_header(token)

    # Realizar peticiones
    data = client.make_request("GET", "/api/data")

# client.close() se llama automáticamente aquí

Nota

El uso como context manager es la forma recomendada de usar HTTPClient ya que garantiza la liberación adecuada de recursos de red.

__exit__(exc_type, exc_val, exc_tb)[fuente]

Método de salida para context manager.

Se llama automáticamente al salir del bloque with, garantizando que los recursos del cliente HTTP se liberen adecuadamente, independientemente de si el bloque se completó exitosamente o se produjo una excepción.

Parámetros:
  • exc_type – Tipo de excepción si ocurrió una excepción, None en caso contrario

  • exc_val – Valor de la excepción si ocurrió una excepción, None en caso contrario

  • exc_tb – Traceback de la excepción si ocurrió una excepción, None en caso contrario

Nota

Este método siempre retorna None, lo que significa que no suprime ninguna excepción que pueda haber ocurrido dentro del bloque with. Las excepciones se propagan normalmente después de la limpieza de recursos.

__init__(timeout=60, retries=3)[fuente]

Inicializa el cliente HTTP con configuración optimizada para Datadis.

Configura una sesión HTTP persistente con headers optimizados y timeouts apropiados para la API de Datadis. La configuración por defecto está pensada para un uso general, pero se recomienda ajustar los valores para Datadis.

Configuración recomendada para Datadis:
  • timeout: 90-120 segundos (la API puede ser muy lenta)

  • retries: 3-5 reintentos (para manejar inestabilidad ocasional)

Parámetros:
  • timeout (int) – Timeout para peticiones HTTP en segundos. Para Datadis se recomienda 90-120s debido a la lentitud del servicio

  • retries (int) – Número máximo de reintentos automáticos para errores de red. 3-5 reintentos recomendados para Datadis por su inestabilidad ocasional

Ejemplo

Configuraciones típicas:

# Configuración conservadora (rápida, pocos reintentos)
client = HTTPClient(timeout=60, retries=2)

# Configuración robusta para Datadis (recomendada)
client = HTTPClient(timeout=120, retries=5)

# Configuración para desarrollo/testing (muy paciente)
client = HTTPClient(timeout=180, retries=3)
close()[fuente]

Cierra la sesión HTTP y libera recursos asociados.

Cierra explícitamente la sesión HTTP subyacente, liberando conexiones de red y otros recursos. Es una buena práctica llamar este método cuando se termina de usar el cliente, especialmente en aplicaciones de larga duración.

Este método es seguro de llamar múltiples veces y no genera errores si la sesión ya está cerrada.

Ejemplo

Uso manual de cierre:

client = HTTPClient(timeout=120, retries=5)
try:
    # Usar cliente para peticiones...
    response = client.make_request("GET", "https://example.com")
finally:
    # Asegurar limpieza de recursos
    client.close()

Uso como context manager (recomendado):

with HTTPClient(timeout=120, retries=5) as client:
    # Usar cliente...
    response = client.make_request("GET", "https://example.com")
    # client.close() se llama automáticamente

Nota

Cuando se usa el cliente como context manager (with statement), este método se llama automáticamente al salir del bloque, por lo que no es necesario llamarlo manualmente.

Tipo del valor devuelto:

None

make_request(method, url, data=None, params=None, headers=None, use_form_data=False)[fuente]

Realiza una petición HTTP robusta con reintentos automáticos y manejo de errores.

Este método es el núcleo del cliente HTTP y maneja toda la lógica de peticiones incluyendo reintentos con backoff exponencial, manejo de diferentes tipos de contenido, rate limiting automático y procesamiento especializado de respuestas.

Flujo de operación:
  1. Rate limiting: Delay automático de 0.1s (excepto para autenticación)

  2. Configuración de headers: Combina headers por defecto con personalizados

  3. Selección de formato: JSON o form-data según use_form_data

  4. Ejecución con reintentos: Hasta self.retries intentos con backoff exponencial

  5. Procesamiento de respuesta: Manejo especializado según tipo de contenido

Estrategia de reintentos:
  • Errores de red/timeout: Reintentos con backoff: 2s → 4s → 8s → 16s…

  • Errores HTTP: Propagación inmediata sin reintentos

  • Máximo wait: 10 segundos entre reintentos para evitar timeouts excesivos

Tipos de contenido soportados:
  • JSON (por defecto): Para la mayoría de endpoints de datos

  • Form-data: Para autenticación y algunos endpoints legacy

  • Detección automática: Basada en el parámetro use_form_data

Parámetros:
  • method (str) – Método HTTP a usar (GET, POST, PUT, DELETE, etc.)

  • url (str) – URL completa del endpoint a consultar

  • data (Optional[Dict[str, Any]]) – Datos a enviar en el cuerpo de la petición. Para GET se ignora, para POST se usa según use_form_data

  • params (Optional[Dict[str, Any]]) – Parámetros de query string a añadir a la URL

  • headers (Optional[Dict[str, str]]) – Headers HTTP adicionales a combinar con los por defecto. Los headers personalizados tienen prioridad sobre los por defecto

  • use_form_data (bool) – Si True, envía datos como application/x-www-form-urlencoded. Si False (por defecto), envía como application/json

Devuelve:

Respuesta procesada del servidor. El tipo depende del endpoint:

  • JWT tokens: str (para endpoints de autenticación)

  • Datos JSON: Dict[str, Any] o List[Any] (para endpoints de datos)

  • Respuestas de texto: str (para endpoints que no devuelven JSON)

Tipo del valor devuelto:

Union[Dict[str, Any], str, list]

Muestra:
  • DatadisError – Si se agotan todos los reintentos por errores de red/timeouts

  • AuthenticationError – Si hay errores de autenticación (401)

  • APIError – Si la API devuelve errores HTTP (400, 403, 404, 500, etc.)

Ejemplo

Diferentes tipos de peticiones:

# GET con parámetros de query
response = client.make_request(
    method="GET",
    url="https://datadis.es/api-private/api/get-supplies",
    params={"distributor_code": "2", "authorized_nif": "12345678A"}
)

# POST con autenticación (form-data)
token = client.make_request(
    method="POST",
    url="https://datadis.es/nikola-auth/tokens/login",
    data={"username": "12345678A", "password": "mi_password"},
    use_form_data=True
)

# GET con headers personalizados
response = client.make_request(
    method="GET",
    url="https://datadis.es/api-private/api/get-consumption-data",
    params={"cups": "ES001234567890123456AB"},
    headers={"Authorization": f"Bearer {token}"}
)

Nota

El rate limiting automático (delay de 0.1s) se aplica a todas las peticiones excepto las de autenticación para evitar sobrecargar los servidores de Datadis que pueden tener límites de rate restrictivos.

Ver también

  • _handle_response() para detalles del procesamiento de respuestas

  • La normalización de texto se realiza automáticamente en respuestas JSON

remove_auth_header()[fuente]

Elimina el header de autorización de todas las peticiones futuras.

Remueve el token de autenticación de los headers de la sesión, haciendo que las peticiones subsecuentes se realicen sin autenticación. Esto es útil para limpiar credenciales cuando se cambia de usuario o cuando se quiere realizar peticiones no autenticadas.

Esta operación es segura y no genera errores si no existe un header de autorización configurado.

Ejemplo

Limpiar autenticación para cambio de usuario:

# Usuario 1 autenticado
client.set_auth_header(token_user1)
data_user1 = client.make_request("GET", "/get-supplies")

# Cambiar a usuario 2
client.remove_auth_header()  # Limpiar credenciales anteriores
token_user2 = client.make_request(
    "POST", "/nikola-auth/tokens/login",
    data={"username": "87654321B", "password": "other_password"},
    use_form_data=True
)
client.set_auth_header(token_user2)
data_user2 = client.make_request("GET", "/get-supplies")

Peticiones sin autenticación:

client.remove_auth_header()
# Ahora las peticiones no incluirán Authorization header
public_data = client.make_request("GET", "/public-endpoint")

Nota

Después de llamar este método, las peticiones a endpoints que requieren autenticación fallarán con error 401 hasta que se establezca un nuevo token con set_auth_header().

Ver también

Tipo del valor devuelto:

None

set_auth_header(token)[fuente]

Establece el header de autorización Bearer Token para todas las peticiones futuras.

Configura el token de autenticación que se añadirá automáticamente a todas las peticiones HTTP subsecuentes. Este método es típicamente llamado después de una autenticación exitosa para configurar el cliente para peticiones autenticadas.

El token se almacena en los headers de la sesión persistent, por lo que se aplicará automáticamente a todas las peticiones realizadas con este cliente hasta que se reemplace con un nuevo token o se elimine.

Parámetros:

token (str) – Token JWT obtenido del endpoint de autenticación de Datadis. Debe ser un token válido sin el prefijo «Bearer « (se añade automáticamente)

Tipo del valor devuelto:

None

Ejemplo

Configurar autenticación después del login:

client = HTTPClient(timeout=120, retries=5)

# Autenticar y obtener token
token = client.make_request(
    method="POST",
    url="https://datadis.es/nikola-auth/tokens/login",
    data={"username": "12345678A", "password": "mi_password"},
    use_form_data=True
)

# Configurar token para peticiones futuras
client.set_auth_header(token)

# Ahora todas las peticiones incluirán el header Authorization
response = client.make_request(
    method="GET",
    url="https://datadis.es/api-private/api/get-supplies"
)

Nota

El token se prefija automáticamente con «Bearer « según el estándar RFC 6750. No incluya este prefijo en el parámetro token.

Ver también

  • remove_auth_header() para eliminar la autenticación

  • RFC 6750 para especificación completa de Bearer Token

datadis_python.utils.normalize_text(text)[fuente]

Normaliza texto removiendo tildes, caracteres especiales y corrigiendo problemas de encoding.

Esta función es el núcleo del sistema de normalización de texto del SDK. Aplica múltiples estrategias para corregir problemas comunes de encoding que aparecen frecuentemente en las respuestas de la API de Datadis, especialmente con caracteres españoles.

Proceso de normalización:
  1. Detección de doble codificación: Identifica secuencias como «Ãx93» (debe ser «Ó»)

  2. Corrección de encoding: Recodifica usando latin-1 → UTF-8 cuando es necesario

  3. Normalización Unicode: Aplica descomposición NFD para separar caracteres base de acentos

  4. Eliminación de acentos: Convierte caracteres acentuados a su equivalente ASCII

  5. Reemplazos específicos: Maneja caracteres especiales españoles (ñ, ç, etc.)

Casos de uso típicos:
  • Nombres de distribuidores: «EDISTRIBUCIÃx93N» → «EDISTRIBUCION»

  • Direcciones: «CALLE JOSÃπ MARTÃxadNEZ» → «CALLE JOSE MARTINEZ»

  • Nombres de municipios: «Málaga», «Cádiz» → «Malaga», «Cadiz»

  • Códigos postales: Normalmente no necesitan normalización pero se procesan igual

Parámetros:

text (str) – Texto a normalizar. Puede contener caracteres especiales, acentos, o problemas de doble codificación típicos de Datadis

Devuelve:

Texto normalizado sin tildes, acentos ni caracteres especiales. Convertido completamente a caracteres ASCII seguros

Tipo del valor devuelto:

str

Ejemplo

Problemas típicos de encoding de Datadis:

# Doble codificación UTF-8
normalize_text("EDISTRIBUCIÃ\x93N")     # → "EDISTRIBUCION"
normalize_text("Málaga")               # → "Malaga"

# Caracteres españoles normales
normalize_text("Málaga")                # → "Malaga"
normalize_text("Coruña")                # → "Coruna"
normalize_text("Cáceres")               # → "Caceres"

# Caracteres especiales
normalize_text("Peñíscola")             # → "Peniscola"
normalize_text("François")              # → "Francois"

Casos extremos manejados:

# Texto ya normalizado - sin cambios
normalize_text("MADRID")                # → "MADRID"

# Mezcla de problemas
normalize_text("Cádiz - AndalucÃ\xada") # → "Cadiz - Andalucia"

Nota

La función es segura con datos de entrada inválidos - si recibe algo que no es string, retorna el valor original sin modificar. Esto evita errores en cadenas de procesamiento de datos.

Advertencia

La normalización es irreversible. Si necesita preservar el texto original con acentos, haga una copia antes de llamar esta función.

Technical details:
  • Usa unicodedata.normalize('NFD', text) para descomposición Unicode

  • Aplica encode('ascii', 'ignore') para eliminar caracteres no-ASCII

  • Maneja específicamente problemas de doble codificación latin-1/UTF-8

  • Incluye tabla de reemplazos para caracteres que unicodedata no maneja

Ver también

  • normalize_dict_strings() para normalizar diccionarios completos

  • normalize_api_response() para procesar respuestas completas de API

  • Documentación de unicodedata para detalles sobre normalización Unicode

datadis_python.utils.normalize_api_response(response)[fuente]

Función principal para normalizar respuestas completas de la API de Datadis.

Esta es la función de entrada principal del sistema de normalización del SDK. Detecta automáticamente el tipo de respuesta (diccionario o lista) y aplica la estrategia de normalización correspondiente para limpiar todos los strings de problemas de encoding y caracteres especiales.

Función de router inteligente:
  • Dict response: Usa normalize_dict_strings() para procesamiento recursivo

  • List response: Usa normalize_list_strings() para procesamiento recursivo

  • Otros tipos: Retorna sin modificar (para compatibilidad futura)

Esta función es llamada automáticamente por el HTTPClient del SDK, por lo que los usuarios normalmente no necesitan llamarla manualmente. Sin embargo, puede ser útil para procesar datos cacheados o respuestas guardadas localmente.

Casos de uso típicos:
  • Auto-procesamiento: Llamada automática por HTTPClient en todas las respuestas JSON

  • Procesamiento manual: Para limpiar datos guardados localmente o cacheados

  • Testing: Para normalizar respuestas mock en tests unitarios

  • Debugging: Para limpiar respuestas antes de análisis manual

Parámetros:

response (Union[Dict[str, Any], List[Any]]) – Respuesta completa de la API de Datadis en formato JSON deserializado. Puede ser un diccionario (respuesta de objeto) o lista (respuesta de array)

Devuelve:

Respuesta normalizada con la misma estructura pero con todos los strings limpios de problemas de encoding y caracteres especiales

Tipo del valor devuelto:

Union[Dict[str, Any], List[Any]]

Ejemplo

Respuestas típicas de diferentes endpoints:

# Respuesta de get_supplies (lista de objetos)
supplies_response = [
    {"cups": "ES001...", "distributor": "EDISTRIBUCIÃ\x93N"},
    {"cups": "ES002...", "distributor": "Málaga Eléctrica"}
]
clean_supplies = normalize_api_response(supplies_response)
# Resultado: [{"cups": "ES001...", "distributor": "EDISTRIBUCION"}, ...]

# Respuesta de get_consumption (objeto con arrays)
consumption_response = {
    "consumption": [
        {"date": "2024/01/01", "distributor": "EDISTRIBUCIÃ\x93N"}
    ],
    "distributor_error": []
}
clean_consumption = normalize_api_response(consumption_response)
# Resultado: {"consumption": [{"date": "2024/01/01", "distributor": "EDISTRIBUCION"}], ...}

Procesamiento manual de datos guardados:

# Datos cacheados que pueden tener problemas de encoding
cached_data = load_from_cache("datadis_supplies.json")
clean_data = normalize_api_response(cached_data)
# Ahora clean_data está libre de problemas de encoding

Uso en testing:

# Mock response con problemas simulados de Datadis
mock_response = {"distributor": "EDISTRIBUCIÃ\x93N"}
normalized_mock = normalize_api_response(mock_response)
# Para tests con datos normalizados

Nota

Esta función es completamente segura con cualquier tipo de entrada. Si recibe datos que no puede procesar, los retorna sin modificar, garantizando que nunca cause errores en el pipeline de datos.

Performance:

La función crea nuevas estructuras de datos en lugar de modificar las originales, lo que la hace segura para uso concurrente pero puede consumir más memoria con respuestas muy grandes (miles de registros).

Integration:

Esta función está integrada automáticamente en la cadena de procesamiento de respuestas del HTTPClient, por lo que todos los datos que llegan a los usuarios finales ya están normalizados.

Ver también

Added in version 1.0: Normalización automática integrada en todo el SDK

Distinto en la versión 2.0: Mejorada detección de problemas de doble codificación UTF-8