"""KFS Smart Farm course - session 0 environment check. Run from the folder that contains 'projects/' and 'datasets/': python projects/setup/check_env.py Every line prints OK / WARN / FAIL. Fix every FAIL before session 1; WARN items are only needed later in the course (the session number is shown). """ import importlib import importlib.metadata as metadata import socket import sys from pathlib import Path RESULTS = [] def report(status, item, detail=""): RESULTS.append(status) print(f"[{status:4s}] {item:38s} {detail}") # 1) Python itself --------------------------------------------------------- v = sys.version_info if v >= (3, 10): report("OK", "Python", sys.version.split()[0]) elif v >= (3, 8): report("WARN", "Python", f"{sys.version.split()[0]} works, 3.10+ recommended") else: report("FAIL", "Python", f"{sys.version.split()[0]} is too old, install 3.10+") # 2) Packages: (import name, pip name, first session that needs it, required now?) PACKAGES = [ ("pandas", "pandas", 13, True), ("numpy", "numpy", 13, True), ("matplotlib", "matplotlib", 13, True), ("sklearn", "scikit-learn", 15, True), ("joblib", "joblib", 15, True), ("flask", "flask", 9, True), ("paho.mqtt", "paho-mqtt", 7, False), ("serial", "pyserial", 3, False), ("certifi", "certifi", 17, False), ] missing = [] for mod, pip_name, session, required in PACKAGES: try: importlib.import_module(mod) try: version = metadata.version(pip_name) except metadata.PackageNotFoundError: version = "installed" report("OK", f"{pip_name}", version) except Exception: missing.append(pip_name) report("FAIL" if required else "WARN", f"{pip_name}", f"missing - needed from session {session}") # 3) Course data ----------------------------------------------------------- here = Path(__file__).resolve() candidates = [Path.cwd() / "datasets", here.parents[2] / "datasets"] data_dir = next((p for p in candidates if p.is_dir()), None) daily = data_dir / "kfs_farm_daily_2023_2024.csv" if data_dir else None if daily and daily.exists(): report("OK", "Farm dataset", str(daily)) try: import pandas as pd d = pd.read_csv(daily, parse_dates=["date"]) july = d[d.date.dt.month == 7] rain_year = d.precipitation_mm.sum() / d.date.dt.year.nunique() et0_year = d.et0_mm.sum() / d.date.dt.year.nunique() print(f" first look at the farm: {len(d)} days, July ET0 = {july.et0_mm.mean():.1f} mm/day, " f"rain = {rain_year:.0f} mm/yr vs ET0 = {et0_year:.0f} mm/yr") except Exception as exc: # pandas missing or file damaged report("WARN", "Farm dataset read", str(exc)) else: report("FAIL", "Farm dataset", "datasets/kfs_farm_daily_2023_2024.csv not found - unzip agritech-datasets.zip here") # 4) Network ports the course uses ---------------------------------------- def port_free(port): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: return s.connect_ex(("127.0.0.1", port)) != 0 for port, use, session in [(5000, "Flask (smart-farm web)", 9), (1883, "MQTT broker (Mosquitto)", 7)]: if port_free(port): report("OK", f"Port {port} free", use) else: report("WARN", f"Port {port} in use", f"{use} - something already listens (fine if it is your broker)") # 5) Internet (Open-Meteo forecast is used live from session 17) ----------- try: socket.create_connection(("api.open-meteo.com", 443), timeout=4).close() report("OK", "Internet (api.open-meteo.com)", "reachable") except OSError: report("WARN", "Internet (api.open-meteo.com)", "not reachable - only needed for live forecasts") # 6) Serial ports (a connected Arduino / ESP32) --------------------------- try: from serial.tools import list_ports ports = [f"{p.device} ({p.description})" for p in list_ports.comports()] report("OK" if ports else "WARN", "Serial ports", ", ".join(ports) if ports else "no board connected (plug one in to test the driver)") except Exception: report("WARN", "Serial ports", "install pyserial to list boards") # Summary ------------------------------------------------------------------ print("-" * 70) fails, warns = RESULTS.count("FAIL"), RESULTS.count("WARN") if missing: print("Install the missing packages with:\n python -m pip install " + " ".join(missing)) print(f"Result: {RESULTS.count('OK')} OK, {warns} WARN, {fails} FAIL" + (" -> ready for session 1" if not fails else " -> fix the FAIL lines before session 1")) sys.exit(1 if fails else 0)