Compare commits

..

No commits in common. "master" and "feature/dashboard" have entirely different histories.

25 changed files with 1466 additions and 2538 deletions

14
.idea/csv-editor.xml generated
View File

@ -17,20 +17,6 @@
</Attribute>
</value>
</entry>
<entry key="/dumpDataPreview">
<value>
<Attribute>
<option name="separator" value="," />
</Attribute>
</value>
</entry>
<entry key="/textView">
<value>
<Attribute>
<option name="separator" value="," />
</Attribute>
</value>
</entry>
</map>
</option>
</component>

124
README.md
View File

@ -1,124 +0,0 @@
# Dashboard Produzione Olio d'Oliva
Questa dashboard interattiva permette di simulare e analizzare la produzione di olio d'oliva basandosi su diversi parametri ambientali e varietali.
## Requisiti di Sistema
- Python 3.8 o superiore
- Git LFS (per i file di grandi dimensioni)
- Conda (consigliato) o pip
## Configurazione dell'Ambiente
### Utilizzando Conda (Consigliato)
1. Clona il repository:
2. Crea e attiva l'ambiente conda:
```bash
conda env create -f src/environment.yml
conda activate olive-dashboard
```
### Utilizzando pip
1. Clona il repository:
2. Crea e attiva un ambiente virtuale:
```bash
python -m venv venv
source venv/bin/activate # Linux/MacOS
# oppure
.\venv\Scripts\activate # Windows
```
3. Installa le dipendenze:
```bash
pip install -r src/requirements.txt
```
## Esecuzione della Dashboard
1. Assicurati di essere nella directory `src`:
```bash
cd src
```
2. Avvia la dashboard:
```bash
python olive-oil-dashboard.py [options]
```
#### Opzioni disponibili:
- port PORT: Specifica la porta su cui avviare il server (default: 8888)
- debug : Attiva la modalità debug con auto-reload (default: False)
#### Esempio:
```bash
python olive-oil-dashboard.py --port 8888 --debug
```
3. Apri un browser e vai all'indirizzo:
```
http://localhost:8888
```
## Struttura del Progetto
```
src/
├── dashboard/ # Componenti della dashboard
├── sources/ # Dati e modelli
├── utils/ # Utility functions
├── olive-oil-dashboard.py # Main application
├── olive_config.json # Configuration file
└── requirements.txt # Python dependencies
```
## Funzionalità Principali
La dashboard offre diverse funzionalità:
1. **Configurazione Oliveto**
- Gestione delle varietà di olive
- Configurazione delle tecniche di coltivazione
- Impostazione delle percentuali di mix varietale
2. **Simulazione Ambientale**
- Simulazione degli effetti delle condizioni meteorologiche
- Analisi dell'impatto sulla produzione
- Visualizzazione KPI
3. **Analisi Economica**
- Gestione dei costi di produzione
- Analisi dei margini
- Proiezioni finanziarie
4. **Produzione**
- Monitoraggio della produzione
- Analisi del fabbisogno idrico
- Dettagli per varietà
## Risoluzione Problemi Comuni
### ModuleNotFoundError
Se riscontri errori del tipo "ModuleNotFoundError", verifica di:
1. Aver attivato l'ambiente virtuale corretto
2. Aver installato tutte le dipendenze:
```bash
pip install -r requirements.txt # se usi pip
# oppure
conda env update -f environment.yml # se usi conda
```
### Errori di Importazione dei Modelli
Se riscontri errori nell'importazione dei modelli:
1. Verifica che `DEV_MODE=True` nel file `.env`
2. Controlla che tutti i file necessari siano presenti nella directory `sources/`
## Supporto
Per problemi o domande, aprire una issue nel repository del progetto.

14
src/.gitignore vendored
View File

@ -1,15 +1 @@
/sources
/auth/config/users/*.json
users/*.json
# Python cache files
__pycache__/
*.py[cod]
*$py.class
# IDE
.idea/
.vscode/
*.swp
*.swo

View File

@ -34,6 +34,13 @@ Questa dashboard interattiva permette di simulare e analizzare la produzione di
```bash
pip install -r src/requirements.txt
```
## Configurazione del Progetto
1. Crea un file `.env` nella directory `src` con le seguenti variabili:
```env
DEV_MODE=True # Imposta su False per utilizzare i modelli ML
```
## Esecuzione della Dashboard
@ -44,22 +51,12 @@ Questa dashboard interattiva permette di simulare e analizzare la produzione di
2. Avvia la dashboard:
```bash
python olive-oil-dashboard.py [options]
```
#### Opzioni disponibili:
- port PORT: Specifica la porta su cui avviare il server (default: 8888)
- debug : Attiva la modalità debug con auto-reload (default: False)
#### Esempio:
```bash
python olive-oil-dashboard.py --port 8888 --debug
python olive-oil-dashboard.py
```
3. Apri un browser e vai all'indirizzo:
```
http://localhost:8888
http://localhost:8050
```
## Struttura del Progetto

Binary file not shown.

View File

View File

@ -1,91 +0,0 @@
import dash_bootstrap_components as dbc
from dash import html, dcc
from components.ids import Ids
def create_login_layout():
return dbc.Container([
dbc.Row([
dbc.Col([
html.H2("Login Dashboard Olio d'Oliva", className="text-center mb-4"),
dbc.Card([
dbc.CardBody([
dbc.Input(
id=Ids.LOGIN_USERNAME,
type="text",
placeholder="Username",
className="mb-3"
),
dbc.Input(
id=Ids.LOGIN_PASSWORD,
type="password",
placeholder="Password",
className="mb-3"
),
dbc.Button(
"Login",
id=Ids.LOGIN_BUTTON,
color="primary",
className="w-100 mb-3"
),
html.Div(id=Ids.LOGIN_ERROR),
html.Hr(),
html.P("Non hai un account?", className="text-center"),
dbc.Button(
"Registrati",
id=Ids.SHOW_REGISTER_BUTTON,
color="secondary",
className="w-100"
)
])
])
], md=6, className="mx-auto")
], className="vh-100 align-items-center")
])
def create_register_layout():
return dbc.Container([
dbc.Row([
dbc.Col([
html.H2("Registrazione", className="text-center mb-4"),
dbc.Card([
dbc.CardBody([
dbc.Input(
id=Ids.REGISTER_USERNAME,
type="text",
placeholder="Username",
className="mb-3"
),
dbc.Input(
id=Ids.REGISTER_PASSWORD,
type="password",
placeholder="Password",
className="mb-3"
),
dbc.Input(
id=Ids.REGISTER_CONFIRM,
type="password",
placeholder="Conferma Password",
className="mb-3"
),
dbc.Button(
"Registrati",
id=Ids.REGISTER_BUTTON,
color="primary",
className="w-100 mb-3"
),
html.Div(id=Ids.REGISTER_ERROR),
html.Div(id=Ids.REGISTER_SUCCESS),
html.Hr(),
html.P("Hai già un account?", className="text-center"),
dbc.Button(
"Torna al Login",
id=Ids.SHOW_LOGIN_BUTTON,
color="secondary",
className="w-100"
)
])
])
], md=6, className="mx-auto")
], className="vh-100 align-items-center")
])

View File

@ -1,236 +0,0 @@
import json
import os
import hashlib
import jwt as pyjwt
from datetime import datetime, timedelta
import secrets
# Costanti
SECRET_KEY = 'M!3EmyJ@P$yqt$dYRQ#73QtxFy$aTn8M98P8i5T9x9Fd5LHMcHgdfEEt#?H9EPg&9Qhokh$#pTyYLHxL' # In produzione, usare una variabile d'ambiente
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
USERS_FILE = os.path.join(BASE_DIR, "users", "users.json")
CONFIGS_DIR = os.path.join(BASE_DIR, "users", "configs")
def init_directory_structure():
"""
Inizializza la struttura delle directory necessarie per l'applicazione
"""
# Crea directory per utenti e configurazioni
os.makedirs(os.path.dirname(USERS_FILE), exist_ok=True)
os.makedirs(CONFIGS_DIR, exist_ok=True)
# Se il file users.json non esiste, crealo vuoto
if not os.path.exists(USERS_FILE):
with open(USERS_FILE, 'w') as f:
json.dump({}, f)
print(f"Initialized directory structure:")
print(f"Users file: {USERS_FILE}")
print(f"Configs directory: {CONFIGS_DIR}")
def hash_password(password):
"""Hash la password usando SHA-256"""
return hashlib.sha256(password.encode()).hexdigest()
# In auth/utils.py
def create_user(username, password):
"""
Crea un nuovo utente
Args:
username: nome utente
password: password in chiaro
Returns:
tuple: (success: bool, message: str)
"""
try:
print(f"Tentativo di creazione utente: {username}")
# Inizializza la struttura delle directory
init_directory_structure()
# Inizializza o carica il dizionario degli utenti
users = {}
if os.path.exists(USERS_FILE):
try:
with open(USERS_FILE, 'r') as f:
content = f.read()
if content.strip(): # Verifica che il file non sia vuoto
users = json.loads(content)
print(f"Utenti esistenti: {len(users)}")
except json.JSONDecodeError as e:
print(f"Errore nel parsing del file utenti: {e}")
pass
# Validazioni
if not username or len(username) < 3:
return False, "Username deve essere almeno 3 caratteri"
if not password or len(password) < 6:
return False, "Password deve essere almeno 6 caratteri"
if username in users:
return False, "Username già esistente"
# Genera il salt e hash della password
salt = secrets.token_hex(8)
password_hash = hashlib.sha256((password + salt).encode()).hexdigest()
# Ottieni il percorso della configurazione utente
user_config = get_user_config_path(username)
print(f"Percorso configurazione utente: {user_config}")
# Crea la configurazione dell'utente
os.makedirs(os.path.dirname(user_config), exist_ok=True)
# Salva la configurazione di default
default_config = get_default_config()
with open(user_config, 'w') as f:
json.dump(default_config, f, indent=4)
print("Configurazione default salvata")
# Aggiungi nuovo utente con struttura semplificata
users[username] = {
"salt": salt,
"password_hash": password_hash
}
# Salva il file utenti
with open(USERS_FILE, 'w') as f:
json.dump(users, f, indent=4)
print("File utenti aggiornato con successo")
return True, "Utente creato con successo"
except Exception as e:
print(f"Errore nella creazione dell'utente: {str(e)}")
import traceback
traceback.print_exc()
return False, f"Errore nella creazione dell'utente: {str(e)}"
def verify_user(username, password):
"""
Verifica le credenziali utente usando salt e hash
Args:
username: nome utente
password: password in chiaro
Returns:
bool: True se le credenziali sono valide, False altrimenti
"""
try:
if not os.path.exists(USERS_FILE):
print("File utenti non trovato")
return False
with open(USERS_FILE, 'r') as f:
users = json.load(f)
if username not in users:
print("Username non trovato")
return False
# Ottieni il salt e l'hash salvati
user_data = users[username]
stored_salt = user_data['salt']
stored_hash = user_data['password_hash']
# Calcola l'hash della password fornita con il salt salvato
password_hash = hashlib.sha256((password + stored_salt).encode()).hexdigest()
# Confronta gli hash
return stored_hash == password_hash
except Exception as e:
print(f"Errore nella verifica dell'utente: {str(e)}")
return False
def create_token(username):
"""Crea JWT token"""
expiration = datetime.utcnow() + timedelta(hours=24)
return pyjwt.encode(
{"user": username, "exp": expiration},
SECRET_KEY,
algorithm="HS256"
)
def verify_token(token):
"""Verifica JWT token"""
try:
payload = pyjwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return True, payload["user"]
except pyjwt.ExpiredSignatureError:
return False, "Token scaduto"
except pyjwt.InvalidTokenError:
return False, "Token non valido"
def get_user_config_path(username):
"""
Restituisce il percorso del file di configurazione per l'utente specificato
Args:
username: nome utente
Returns:
str: percorso assoluto del file di configurazione
"""
# Sostituisci caratteri non validi nel nome utente
safe_username = "".join(c for c in username if c.isalnum() or c in ('-', '_'))
# Costruisci il percorso assoluto
base_dir = os.path.dirname(os.path.abspath(__file__))
config_dir = os.path.join(base_dir, 'config', 'users')
config_path = os.path.join(config_dir, f"{safe_username}_config.json")
# Assicurati che la directory esista
os.makedirs(os.path.dirname(config_path), exist_ok=True)
return config_path
def get_default_config():
return {
"oliveto": {
"hectares": 1,
"varieties": [
{
"variety": "Nocellara dell'Etna",
"technique": "tradizionale",
"percentage": 50
},
{
"variety": "Frantoio",
"technique": "tradizionale",
"percentage": 10
},
{
"variety": "Coratina",
"technique": "tradizionale",
"percentage": 40
}
]
},
"costs": {
"fixed": {
"ammortamento": 2000,
"assicurazione": 500,
"manutenzione": 800,
"certificazioni": 3000
},
"variable": {
"raccolta": 0.35,
"potatura": 600,
"fertilizzanti": 400,
"irrigazione": 300
},
"transformation": {
"molitura": 0.15,
"stoccaggio": 0.2,
"bottiglia": 1.2,
"etichettatura": 0.3
},
"marketing": {
"budget_annuale": 15000,
"costi_commerciali": 0.5,
"prezzo_vendita": 12,
"perc_vendita_diretta": 30
}
},
"inference": {
"debug_mode": True,
'model_path': './sources/olive_oil_transformer/olive_oil_transformer_model.keras'
}
}

View File

@ -1,105 +0,0 @@
# components/ids.py
from dataclasses import dataclass
@dataclass
class Ids:
# Auth Container
USE_MODEL = 'use-model'
AUTH_CONTAINER = 'auth-container'
DASHBOARD_CONTAINER = 'dashboard-container'
# Login Form
LOGIN_FORM = 'login-form'
LOGIN_USERNAME = 'login-username'
LOGIN_PASSWORD = 'login-password'
LOGIN_BUTTON = 'login-button'
LOGIN_ERROR = 'login-error'
# Register Form
REGISTER_FORM = 'register-form'
REGISTER_USERNAME = 'register-username'
REGISTER_PASSWORD = 'register-password'
REGISTER_CONFIRM = 'register-confirm'
REGISTER_BUTTON = 'register-button'
REGISTER_ERROR = 'register-error'
REGISTER_SUCCESS = 'register-success'
# Navigation
SHOW_REGISTER_BUTTON = 'show-register-button'
SHOW_LOGIN_BUTTON = 'show-login-button'
# Inference
INFERENCE_CONTAINER = 'inference-container'
INFERENCE_STATUS = 'inference-status'
INFERENCE_MODE = 'inference-mode'
INFERENCE_LATENCY = 'inference-latency'
INFERENCE_REQUESTS = 'inference-requests'
INFERENCE_COUNTER = 'inference-counter'
DEBUG_SWITCH = 'debug-switch'
# Simulation
SIMULATE_BUTTON = 'simulate-btn'
GROWTH_CHART = 'growth-simulation-chart'
PRODUCTION_CHART = 'production-simulation-chart'
SIMULATION_SUMMARY = 'simulation-summary'
KPI_CONTAINER = 'kpi-container'
PRODUCTION_DEBUG_SWITCH = 'production-debug-switch'
PRODUCTION_INFERENCE_REQUESTS = 'production-inference-requests'
PRODUCTION_INFERENCE_MODE = 'production-inference-mode'
# Environment Controls
TEMP_SLIDER = 'temp-slider'
HUMIDITY_SLIDER = 'humidity-slider'
RAINFALL_INPUT = 'rainfall-input'
RADIATION_INPUT = 'radiation-input'
# Production Views
OLIVE_PRODUCTION_HA = 'olive-production_ha'
OIL_PRODUCTION_HA = 'oil-production_ha'
WATER_NEED_HA = 'water-need_ha'
OLIVE_PRODUCTION = 'olive-production'
OIL_PRODUCTION = 'oil-production'
WATER_NEED = 'water-need'
PRODUCTION_DETAILS = 'production-details'
WEATHER_IMPACT = 'weather-impact'
WATER_NEEDS = 'water-needs'
EXTRA_INFO = 'extra-info'
# Configuration
HECTARES_INPUT = 'hectares-input'
VARIETY_1_DROPDOWN = 'variety-1-dropdown'
TECHNIQUE_1_DROPDOWN = 'technique-1-dropdown'
PERCENTAGE_1_INPUT = 'percentage-1-input'
VARIETY_2_DROPDOWN = 'variety-2-dropdown'
TECHNIQUE_2_DROPDOWN = 'technique-2-dropdown'
PERCENTAGE_2_INPUT = 'percentage-2-input'
VARIETY_3_DROPDOWN = 'variety-3-dropdown'
TECHNIQUE_3_DROPDOWN = 'technique-3-dropdown'
PERCENTAGE_3_INPUT = 'percentage-3-input'
PERCENTAGE_WARNING = 'percentage-warning'
# Cost Inputs
COST_AMMORTAMENTO = 'cost-ammortamento'
COST_ASSICURAZIONE = 'cost-assicurazione'
COST_MANUTENZIONE = 'cost-manutenzione'
COST_CERTIFICAZIONI = 'cost-certificazioni'
COST_RACCOLTA = 'cost-raccolta'
COST_POTATURA = 'cost-potatura'
COST_FERTILIZZANTI = 'cost-fertilizzanti'
COST_IRRIGAZIONE = 'cost-irrigazione'
COST_MOLITURA = 'cost-molitura'
COST_STOCCAGGIO = 'cost-stoccaggio'
COST_BOTTIGLIA = 'cost-bottiglia'
COST_ETICHETTATURA = 'cost-etichettatura'
COST_MARKETING = 'cost-marketing'
COST_COMMERCIALI = 'cost-commerciali'
PRICE_OLIO = 'price-olio'
PERC_VENDITA_DIRETTA = 'perc-vendita-diretta'
# Other
LOADING_ALERT = 'loading-alert'
TABS = 'tabs'
SAVE_CONFIG_BUTTON = 'save-config-button'
SAVE_CONFIG_MESSAGE = 'save-config-message'
LOGOUT_BUTTON = 'logout-button'
DEV_MODE = 'dev-mode'

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"oliveto": {
"hectares": 1,
"hectares": 4.35,
"varieties": [
{
"variety": "Nocellara dell'Etna",
@ -10,12 +10,12 @@
{
"variety": "Frantoio",
"technique": "tradizionale",
"percentage": 20
"percentage": 10
},
{
"variety": "Coratina",
"technique": "tradizionale",
"percentage": 30
"percentage": 40
}
]
},
@ -46,7 +46,6 @@
}
},
"inference": {
"debug_mode": false,
"model_path": "./sources/olive_oil_transformer/olive_oil_transformer_model.keras"
"debug_mode": false
}
}

View File

@ -1,65 +0,0 @@
# Olive Oil Production Training Dataset Generator
## Overview
This Python script generates a synthetic training dataset for olive oil production simulation. It simulates various factors affecting olive production including weather conditions, olive varieties, cultivation techniques, and geographical zones.
## Features
- Parallel processing for efficient dataset generation
- Batch processing to manage memory usage
- Configurable simulation parameters
- Weather effect calculations
- Multiple olive varieties and cultivation techniques support
- Zone-based production simulation
## Prerequisites
Required Python packages:
- pandas
- numpy
- psutil
- tqdm
- concurrent.futures (part of Python standard library)
- multiprocessing (part of Python standard library)
Required input data files:
- `./sources/weather_data_solarenergy.parquet`: Weather data including solar energy measurements
- `./sources/olive_varieties.parquet`: Olive varieties and their characteristics
## Usage
### Command Line Arguments
```bash
python olive_oil_train_dataset.create_train_dataset [options]
```
Options:
- `--random-seed`: Seed for reproducible results (optional)
- `--num-simulations`: Total number of simulations to run (default: 100000)
- `--num-zones`: Number of zones per simulation (default: same as num-simulations)
- `--batch-size`: Size of each simulation batch (default: 10000)
- `--output-path`: Output file path (default: './sources/olive_training_dataset.parquet')
- `--max-workers`: Number of parallel workers (default: automatically optimized)
### Example
```bash
python olive_oil_train_dataset.create_train_dataset --num-simulations 50 --num-zones 10 --batch-size 50 --output-path "./output/olive_dataset.parquet"
```
## Output
The script generates a Parquet file containing simulated olive production data with the following key features:
- Simulation and zone identifiers
- Weather conditions (temperature, precipitation, solar energy)
- Production metrics per olive variety
- Oil yield calculations
- Water requirements
- Cultivation techniques
## Technical Details
### Simulation Parameters
The simulation takes into account:
- Temperature effects on production
- Water availability and drought resistance
- Solar radiation impact
- Variety-specific characteristics
- Cultivation technique influence
- Zone-specific variations

View File

@ -133,24 +133,22 @@ def simulate_zone(base_weather, olive_varieties, year, zone, all_varieties, vari
variety_info['Fabbisogno Acqua Estate (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Autunno (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Inverno (m³/ettaro)']
) / 12
) / 4
monthly_water_need = zone_weather.apply(
lambda row: calculate_water_need(row, base_water_need, variety_info['Temperatura Ottimale']),
axis=1
)
#print(f'Monthly - {variety} - hectares {hectares} - {monthly_water_need}')
monthly_water_need *= np.random.uniform(0.95, 1.05, len(monthly_water_need))
#print(f'Monthly 2 - {variety} - hectares {hectares} - {monthly_water_need}')
annual_variety_water_need = monthly_water_need.sum() * percentage * hectares
#print(f'Annual Variety - {variety} - hectares {hectares} - {annual_variety_water_need}')
# Aggiorna totali annuali
annual_production += annual_variety_production
annual_min_oil += min_oil_production
annual_max_oil += max_oil_production
annual_avg_oil += avg_oil_production
annual_water_need += annual_variety_water_need
#print(f'Total Annual {annual_water_need}')
# Aggiorna dati varietà
clean_variety = clean_column_name(variety)
variety_data[clean_variety].update({
@ -242,9 +240,9 @@ def simulate_olive_production_parallel(weather_data, olive_varieties, num_simula
print(f"Utilizzando {max_workers} workers ottimali basati sulle risorse del sistema")
# Calcolo numero di batch
num_batches = (num_simulations * num_zones - 1) // batch_size
num_batches = (num_simulations + batch_size - 1) // batch_size
print(f"Elaborazione di {num_simulations} simulazioni con {num_zones} zone in {num_batches} batch")
print(f"Totale record attesi: {num_simulations * num_zones}")
print(f"Totale record attesi: {num_simulations * num_zones:,}")
# Lista per contenere tutti i DataFrame dei batch
all_batches = []
@ -370,11 +368,11 @@ def calculate_production(variety_info, weather, percentage, hectares, seed):
variety_info['Fabbisogno Acqua Inverno (m³/ettaro)']
) / 4 * percentage * hectares
temp_factor = 1 + max(0, (weather["temp_mean"] - 20) / 50)
rain_factor = max(0.6, 1 - (weather["precip_sum"] / 1000))
water_need = base_water_need * temp_factor * rain_factor
print(f'temp factor: {temp_factor} rainfall factor: {rain_factor} water_need: {water_need}')
water_need = (
base_water_need *
(1 + max(0, (weather['temp_mean'] - 20) / 50)) *
max(0.6, 1 - (weather['precip_sum'] / 1000))
)
return {
'variety': variety_info['Varietà di Olive'],

View File

@ -1,7 +1,7 @@
# Data handling and analysis
pandas>=1.5.0
numpy>=1.21.0
scikit-learn==1.6.0
scikit-learn==1.5.2
# Dashboard and visualization
dash>=2.9.0
@ -18,7 +18,4 @@ python-dotenv>=0.21.0
psutil>=5.9.0
# File handling
dvc>=2.0.0 # Per la gestione dei file sources.dvc
# Auth
PyJWT==2.7.0
dvc>=2.0.0 # Per la gestione dei file sources.dvc

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -424,20 +424,10 @@ def calculate_weather_effect(row, optimal_temp):
return combined_effect
def calculate_water_need(weather_data, base_need, optimal_temp):
# Calcola il fattore temperatura (minimo 80% del fabbisogno)
temp_factor = max(0.8, 1 + 0.05 * (weather_data['temp_mean'] - optimal_temp))
# Calcola il fattore precipitazioni (minimo 50% del fabbisogno)
rain_factor = max(0.5, 1 - 0.001 * weather_data['precip_sum'])
# Calcola il fabbisogno idrico totale
water_need = base_need * temp_factor * rain_factor
# Debug: controlla che il fabbisogno idrico sia positivo
assert water_need >= 0, "Il fabbisogno idrico calcolato è negativo!"
return water_need
# Calcola il fabbisogno idrico basato su temperatura e precipitazioni
temp_factor = 1 + 0.05 * (weather_data['temp_mean'] - optimal_temp) # Aumenta del 5% per ogni grado sopra l'ottimale
rain_factor = 1 - 0.001 * weather_data['precip_sum'] # Diminuisce leggermente con l'aumentare delle precipitazioni
return base_need * temp_factor * rain_factor
def create_technique_mapping(olive_varieties, mapping_path='./sources/technique_mapping.joblib'):
# Estrai tutte le tecniche uniche dal dataset e convertile in lowercase