import pandera as pa
import pandas as pd
from pydantic import BaseModel, field_validator, Field
from pandera.typing import Series, DataFrame
from typing import List, Tuple
from datetime import datetime
import re
# Schema per la Series: valori float positivi
class PositiveFloatSeries(pa.SeriesModel):
class Config:
dtype = pa.Float
checks = pa.Check.ge(0)
index = pa.Index(pa.DateTime)
# Schema per il DataFrame: indice di tipo data e una colonna di serie
class DatedDataFrame(pa.DataFrameModel):
valore_di_interesse: pa.Series[float] = pa.Field(nullable=True)
eq_line: pa.Series[Series[PositiveFloatSeries]]
class Config:
index = pa.Index(datetime, name="data_ora", coerce=True)
# La colonna 'valore_di_interesse' non deve contenere valori negativi
checks = pa.Check.ge(0, column='valore_di_interesse')
# Modello per la validazione di ogni singolo dizionario
class ExtraDataModel(BaseModel):
index: int = Field(gt=0)
t_0: int = Field(gt=0)
t_1: int
date_0: datetime
date_1: datetime
@field_validator('t_1')
@classmethod
def validate_t_1(cls, v, info):
if v <= info.data['t_0']:
raise ValueError('t_1 deve essere maggiore di t_0')
return v
@field_validator('date_1')
@classmethod
def validate_date_1(cls, v, info):
if v <= info.data['date_0']:
raise ValueError('date_1 deve essere maggiore di date_0')
return v
# Modello Pydantic che integra tutti gli schemi e le regole
class InputModel(BaseModel):
my_int: int = Field(gt=0)
my_float: float = Field(gt=0, lt=1)
my_str: str
my_list: List[int] = Field(min_length=17, max_length=17)
my_tuple: Tuple[str, ...]
my_series: Series[PositiveFloatSeries]
my_dataframe: DataFrame[DatedDataFrame]
# Lista di dizionari con le regole di validazione
extra_data: List[ExtraDataModel]
# Validatore per la stringa
@field_validator('my_str')
@classmethod
def validate_my_str(cls, v):
if not v:
raise ValueError('La stringa non può essere vuota')
if not re.match(r'^[a-zA-Z0-9.]+$', v):
raise ValueError('La stringa può contenere solo lettere, numeri e il punto')
return v
# Validatore per la lista
@field_validator('my_list')
@classmethod
def validate_my_list(cls, v):
for item in v:
if not (5 <= item <= 27):
raise ValueError('Tutti i valori nella lista devono essere compresi tra 5 e 27')
return v
def process_data(inputs: InputModel):
print("Dati ricevuti e validati con successo.")
print("---------------------------------")
print("Informazioni sugli input:")
print(f"Intero: {inputs.my_int}")
print(f"Stringa: {inputs.my_str}")
print(f"Serie Pandas:\n{inputs.my_series}")
print(f"DataFrame Pandas:\n{inputs.my_dataframe}")
print("\nInformazioni aggiuntive (lista di dizionari):")
for i, data in enumerate(inputs.extra_data):
print(f" Dizionario {i+1}:")
print(f" Index: {data.index}")
print(f" t_0: {data.t_0}")
print(f" t_1: {data.t_1}")
print(f" date_0: {data.date_0}")
print(f" date_1: {data.date_1}")
# Esempio 1: Dati validi
valid_series = pd.Series([1.5, 2.0, 5.8], name='serie_valori')
valid_series_list = [
pd.Series([10.1, 10.2], name='sub_series_1'),
pd.Series([20.1, 20.2], name='sub_series_2')
]
valid_df = pd.DataFrame(
{'valore_di_interesse': [10.5, 20.8],
'eq_line': valid_series_list},
index=pd.to_datetime(['2025-09-01', '2025-09-02'])
)
valid_extra_data_list = [
{"index": 1, "t_0": 5, "t_1": 10, "date_0": datetime(2025, 1, 1), "date_1": datetime(2025, 1, 10)},
{"index": 2, "t_0": 10, "t_1": 20, "date_0": datetime(2025, 2, 1), "date_1": datetime(2025, 2, 20)}
]
valid_data = {
"my_int": 1,
"my_float": 0.5,
"my_str": 'testo.prova.123',
"my_list": [10, 15, 20, 25, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
"my_tuple": ('a', 'b'),
"my_series": valid_series,
"my_dataframe": valid_df,
"extra_data": valid_extra_data_list
}
try:
print("--- Tentativo con dati validi ---")
valid_inputs = InputModel(**valid_data)
process_data(valid_inputs)
except Exception as e:
print(f"\nErrore inatteso: {e}")
print("\n\n" + "="*40 + "\n\n")
# Esempio 2: Dati non validi (valore negativo nella serie)
invalid_series_1 = pd.Series([1.5, -2.0, 5.8], name='serie_valori')
invalid_data_1 = {**valid_data, 'my_series': invalid_series_1}
try:
print("--- Tentativo con dati non validi (serie) ---")
InputModel(**invalid_data_1)
except Exception as e:
print(f"\nErrore di validazione previsto:\n{e}")
print("\n\n" + "="*40 + "\n\n")
# Esempio 3: Dati non validi (t_1 non è maggiore di t_0 in uno dei dizionari)
invalid_extra_data_list = [
{"index": 1, "t_0": 5, "t_1": 10, "date_0": datetime(2025, 1, 1), "date_1": datetime(2025, 1, 10)},
{"index": 2, "t_0": 20, "t_1": 10, "date_0": datetime(2025, 2, 1), "date_1": datetime(2025, 2, 20)} # t_1 < t_0
]
invalid_data_2 = {**valid_data, 'extra_data': invalid_extra_data_list}
try:
print("--- Tentativo con dati non validi (lista di dizionari) ---")
InputModel(**invalid_data_2)
except Exception as e:
print(f"\nErrore di validazione previsto:\n{e}")