commit f6a1b2a26d0f547d14499716f3e44e98e0372305 Author: Raihan <17radf@gmail.com> Date: Tue May 6 12:04:42 2025 +0700 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/ems-model/.gitignore b/ems-model/.gitignore new file mode 100644 index 0000000..4a65e2a --- /dev/null +++ b/ems-model/.gitignore @@ -0,0 +1,11 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info +pickles/ + +# Virtual environments +.venv diff --git a/ems-model/.ipynb_checkpoints/model-checkpoint.ipynb b/ems-model/.ipynb_checkpoints/model-checkpoint.ipynb new file mode 100644 index 0000000..363fcab --- /dev/null +++ b/ems-model/.ipynb_checkpoints/model-checkpoint.ipynb @@ -0,0 +1,6 @@ +{ + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ems-model/.python-version b/ems-model/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/ems-model/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/ems-model/README.md b/ems-model/README.md new file mode 100644 index 0000000..9398381 --- /dev/null +++ b/ems-model/README.md @@ -0,0 +1,10 @@ +## Overview +This is the machine learning model for the EMS-Sense project. + +## How to use +This model is built using [sklearn](https://scikit-learn.org/stable). To use it, simply follow these steps: +1. Setup uv on your machine [uv](https://github.com/astral-sh/uv). +2. Run `uv venv` to create your virtual environment. +3. Run `uv pip install -r pyproject.toml` to install the dependencies. +4. Run `uv run model.py` to train the model. +5. Run `fastapi dev api.py` to serve the api for the model. diff --git a/ems-model/api.py b/ems-model/api.py new file mode 100644 index 0000000..53c8e14 --- /dev/null +++ b/ems-model/api.py @@ -0,0 +1,127 @@ +from fastapi import FastAPI, File, UploadFile, HTTPException +from fastapi.encoders import jsonable_encoder +from fastapi.responses import JSONResponse +from treatment import Treatment +import pandas as pd +import pickle +import subprocess + +app = FastAPI() + + +def predict(treatment: Treatment): + # load the model from pickle files + with open("pickles/ems_model.pkl", "rb") as f: + model = pickle.load(f) + + with open("pickles/label_encoding.pkl", "rb") as f: + le = pickle.load(f) + + with open("pickles/scaler_encoding.pkl", "rb") as f: + scaler = pickle.load(f) + + # encode the species species + species = le.transform([treatment.species]) + + # normalize the input data + transformed_data = scaler.transform( + pd.DataFrame( + { + "soakDuration": [treatment.soakDuration], + "lowestTemp": [treatment.lowestTemp], + "highestTemp": [treatment.highestTemp], + } + ) + ) + + data = pd.DataFrame( + { + "species": [species], + "emsConcentration": [treatment.emsConcentration], + "soakDuration": [transformed_data[0][0]], + "lowestTemp": [transformed_data[0][1]], + "highestTemp": [transformed_data[0][2]], + } + ) + + prediction_prob = model.predict_proba(data)[0] + prediction = model.predict(data)[0] + + confidence_score = prediction_prob[prediction] * 100 + + result = { + "result": int(prediction), + "confidence_score": float(confidence_score), + "success_rate": float(prediction_prob[1] * 100), + } + + return JSONResponse(content=jsonable_encoder(result)) + + +@app.post("/process") +def process(treatment: Treatment): + return predict(treatment) + + +@app.get("/species") +def get_species(): + df = pd.read_csv("csv/ems_data.csv") + unique_values = df.iloc[:, 0].drop_duplicates().tolist() + + return JSONResponse(content=unique_values) + + +@app.get("/retrain-model") +def retrain_model(): + try: + result = subprocess.run( + ["python", "model.py"], + check=True, + capture_output=True, + text=True, + ) + output = result.stdout.strip() + return {"message": "Retraining succeeded!", "details": output} + except subprocess.CalledProcessError as e: + return {"message": "Retraining failed!", "error": str(e)} + + +@app.post("/upload-csv") +async def upload_csv(file: UploadFile = File(...)): + try: + # check if the uploaded file is a csv + if not file.filename.endswith(".csv"): + raise HTTPException(status_code=400, detail="Only CSV files are allowed.") + + # save the uploaded file to csv/ems_data.csv (overwrite) + file_path = "csv/ems_data.csv" + with open(file_path, "wb") as f: + content = await file.read() + f.write(content) + + # verify if the file is valid (optional: try loading it as a dataframe) + try: + pd.read_csv( + file_path + ) + except Exception: + os.remove(file_path) + raise HTTPException( + status_code=400, detail="Uploaded file is not a valid CSV." + ) + + return {"message": "File uploaded and overwritten successfully!"} + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.get("/get-csv") +def get_csv(): + file_path = "csv/ems_data.csv" + + try: + df = pd.read_csv(file_path) + + data = df.to_dict(orient="records") + return data + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error processing CSV: {str(e)}") diff --git a/ems-model/csv/ems_data.csv b/ems-model/csv/ems_data.csv new file mode 100644 index 0000000..3f807f4 --- /dev/null +++ b/ems-model/csv/ems_data.csv @@ -0,0 +1,155 @@ +species,emsConcentration,soakDuration,lowestTemp,highestTemp,result +Cotton - TM-1,1.50,180,20.0,32.0,1 +Hemp - Lembang A,0.25,360,25.0,30.0,0 +Cotton - TM-1,3.50,360,20.0,32.0,0 +Hyoscyamus niger - Black Henbane,0.02,60,25.0,28.0,1 +Sunflower - Helianthus annuus (BBS-1),1.00,480,22.0,25.0,0 +Cotton - TM-1,0.50,360,20.0,32.0,0 +Watermelon - G42,0.10,40,25.0,30.0,1 +Sunflower - Helianthus annuus (BBS-1),2.00,480,22.0,25.0,0 +Watermelon - G42,0.15,80,25.0,30.0,0 +Cotton - TM-1,1.50,360,20.0,32.0,1 +Marigold - Tagetes patula,0.00,2880,25.0,30.0,1 +Hemp - Bandung A,0.50,180,25.0,30.0,1 +Hyoscyamus niger - Black Henbane,0.03,60,25.0,28.0,1 +Marigold - Tagetes patula,1.50,240,25.0,30.0,0 +Hemp - Kumamoto,0.75,180,25.0,30.0,1 +Sesame - Tilottama,1.00,360,23.0,25.0,1 +Hemp - Indochina,0.75,180,25.0,30.0,1 +Hyoscyamus niger - Black Henbane,0.06,60,25.0,28.0,0 +Sesame - Tilottama,1.00,120,23.0,25.0,0 +Marigold - Tagetes patula,0.60,240,25.0,30.0,0 +Chinese cabbage - Brassica rapa ssp. pekinensis,0.10,360,3.0,18.0,0 +Sesame - Tilottama,0.50,240,23.0,25.0,0 +Marigold - Tagetes erecta,0.80,240,25.0,30.0,1 +Watermelon - G42,0.20,60,25.0,30.0,0 +Fodder Barley - Hordeum vulgare,0.50,90,24.0,24.0,0 +Marigold - Tagetes patula,1.00,240,25.0,30.0,0 +Hemp - Lembang A,0.50,6,25.0,30.0,0 +Watermelon - G42,0.10,60,25.0,30.0,1 +Maize - Zea mays (B73),0.70,480,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.10,30,24.0,24.0,0 +Marigold - Tagetes patula,0.19,360,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.90,30,24.0,24.0,0 +Fodder Barley - Hordeum vulgare,0.10,120,24.0,24.0,1 +Marigold - Tagetes erecta,0.40,240,25.0,30.0,1 +Marigold - Tagetes patula,2.50,240,25.0,30.0,0 +Sesame - Tilottama,0.25,360,23.0,25.0,0 +Hemp - Indochina,1.00,180,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.90,60,24.0,24.0,0 +Fodder Barley - Hordeum vulgare,0.30,30,24.0,24.0,0 +Watermelon - G42,0.15,60,25.0,30.0,1 +Watermelon - G42,0.20,40,25.0,30.0,0 +Cotton - Luyanmian21,1.50,360,20.0,32.0,1 +Hemp - Lembang A,0.50,180,25.0,30.0,1 +Hemp - Lembang A,0.75,180,25.0,30.0,1 +Hemp - Bandung A,0.75,180,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.50,60,24.0,24.0,0 +Fodder Barley - Hordeum vulgare,0.10,60,24.0,24.0,1 +Hemp - Seiki Selskin,0.50,180,25.0,30.0,1 +Marigold - Tagetes erecta,0.00,2880,25.0,30.0,1 +Sunflower - Helianthus annuus (BBS-1),0.75,480,22.0,25.0,0 +Fodder Barley - Hordeum vulgare,0.90,150,24.0,24.0,0 +Hemp - Bandung A,0.25,180,25.0,30.0,0 +Sesame - Tilottama,0.50,360,23.0,25.0,1 +Hemp - Indochina,0.25,360,25.0,30.0,0 +Cotton - Luyanmian21,3.50,360,20.0,32.0,0 +Hyoscyamus niger - Black Henbane,0.08,60,25.0,28.0,0 +Sunflower - Helianthus annuus (BBS-1),3.00,480,22.0,25.0,0 +Maize - Zea mays (B73),1.00,120,25.0,30.0,1 +Marigold - Tagetes erecta,1.00,240,25.0,30.0,0 +Hemp - Indochina,0.50,360,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.90,120,24.0,24.0,0 +Sesame - Tilottama,1.00,240,23.0,25.0,1 +Hemp - Kumamoto,0.75,360,25.0,30.0,0 +Fodder Barley - Hordeum vulgare,0.90,90,24.0,24.0,0 +Hemp - Lembang A,0.75,6,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.50,30,24.0,24.0,0 +Cotton - Luyanmian21,0.50,180,20.0,32.0,0 +Jasmine - Jasminum grandiflorum,0.40,60,28.0,30.0,0 +Chinese cabbage - Brassica rapa ssp. pekinensis,0.70,1440,5.0,25.0,1 +Fodder Barley - Hordeum vulgare,0.30,60,24.0,24.0,1 +Hemp - Lembang A,1.00,180,25.0,30.0,0 +Hemp - Bandung A,1.00,180,25.0,30.0,0 +Hemp - Seiki Selskin,0.75,180,25.0,30.0,1 +Hemp - Seiki Selskin,0.75,360,25.0,30.0,1 +Hemp - Indochina,1.00,360,25.0,30.0,1 +Fenugreek - Trigonella foenum-graecum,0.30,360,25.0,30.0,0 +Hemp - Indochina,0.25,180,25.0,30.0,0 +Hyoscyamus niger - Black Henbane,0.07,60,25.0,28.0,0 +Fodder Barley - Hordeum vulgare,0.30,150,24.0,24.0,0 +Marigold - Tagetes patula,0.02,1440,25.0,30.0,0 +Cotton - Luyanmian21,2.50,360,20.0,32.0,0 +Marigold - Tagetes patula,2.00,240,25.0,30.0,1 +Cotton - TM-1,2.50,360,20.0,32.0,0 +Hemp - Lembang A,1.00,360,25.0,30.0,0 +Hyoscyamus niger - Black Henbane,0.10,60,25.0,28.0,0 +Fodder Barley - Hordeum vulgare,0.70,120,24.0,24.0,0 +Cotton - Luyanmian21,3.50,180,20.0,32.0,0 +Hyoscyamus niger - Black Henbane,0.04,60,25.0,28.0,0 +Fodder Barley - Hordeum vulgare,0.70,150,24.0,24.0,0 +Hemp - Lembang A,0.25,180,25.0,30.0,1 +Jasmine - Jasminum grandiflorum,0.30,60,28.0,30.0,1 +Jasmine - Jasminum grandiflorum,0.35,60,28.0,30.0,0 +Hemp - Indochina,0.75,360,25.0,30.0,1 +Cotton - TM-1,3.50,180,20.0,32.0,0 +Cotton - TM-1,2.50,180,20.0,32.0,0 +Marigold - Tagetes patula,0.20,240,25.0,30.0,0 +Hemp - Kumamoto,0.50,180,25.0,30.0,1 +Sesame - Tilottama,0.25,120,23.0,25.0,0 +Sunflower - Helianthus annuus (BBS-1),0.60,480,22.0,25.0,1 +Fenugreek - Trigonella foenum-graecum,0.40,360,25.0,30.0,0 +Jasmine - Jasminum grandiflorum,0.25,60,28.0,30.0,1 +Cotton - TM-1,0.50,180,20.0,32.0,0 +Fodder Barley - Hordeum vulgare,0.70,60,24.0,24.0,0 +Marigold - Tagetes erecta,0.00,240,25.0,30.0,0 +Hemp - Kumamoto,0.50,360,25.0,30.0,1 +Hemp - Kumamoto,1.00,360,25.0,30.0,1 +Marigold - Tagetes erecta,0.60,240,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.50,120,24.0,24.0,0 +Fodder Barley - Hordeum vulgare,0.30,120,24.0,24.0,0 +Marigold - Tagetes patula,0.80,240,25.0,30.0,0 +Hemp - Seiki Selskin,1.00,360,25.0,30.0,1 +Hemp - Indochina,0.50,180,25.0,30.0,1 +Watermelon - G42,0.20,80,25.0,30.0,0 +Watermelon - G42,0.15,40,25.0,30.0,1 +Hemp - Seiki Selskin,0.50,360,25.0,30.0,0 +Hemp - Kumamoto,0.25,180,25.0,30.0,0 +Fodder Barley - Hordeum vulgare,0.50,150,24.0,24.0,0 +Cotton - Luyanmian21,2.50,180,20.0,32.0,0 +Hemp - Seiki Selskin,0.25,180,25.0,30.0,1 +Marigold - Tagetes erecta,0.20,240,25.0,30.0,0 +Hemp - Bandung A,0.25,360,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.70,30,24.0,24.0,0 +Fodder Barley - Hordeum vulgare,0.10,90,24.0,24.0,1 +Hemp - Bandung A,1.00,360,25.0,30.0,0 +Marigold - Tagetes patula,0.40,240,25.0,30.0,0 +Hyoscyamus niger - Black Henbane,0.05,60,25.0,28.0,0 +Hemp - Bandung A,0.75,360,25.0,30.0,1 +Sesame - Tilottama,0.50,120,23.0,25.0,0 +Marigold - Tagetes patula,3.00,240,25.0,30.0,0 +Maize - Zea mays (B73),0.50,240,25.0,30.0,0 +Hemp - Seiki Selskin,0.25,360,25.0,30.0,0 +Fodder Barley - Hordeum vulgare,0.10,150,24.0,24.0,0 +Cotton - Luyanmian21,0.50,360,20.0,32.0,0 +Fenugreek - Trigonella foenum-graecum,0.10,360,25.0,30.0,1 +Marigold - Tagetes erecta,0.01,1440,25.0,30.0,1 +Sesame - Tilottama,0.25,240,23.0,25.0,0 +Marigold - Tagetes patula,0.00,240,25.0,30.0,0 +Hemp - Kumamoto,1.00,180,25.0,30.0,0 +Sunflower - Helianthus annuus (BBS-1),0.50,480,22.0,25.0,1 +Hyoscyamus niger - Black Henbane,0.01,60,25.0,28.0,1 +Chinese cabbage - Brassica rapa ssp. pekinensis,0.50,720,4.0,25.0,1 +Hemp - Bandung A,0.50,360,25.0,30.0,1 +Fodder Barley - Hordeum vulgare,0.70,90,24.0,24.0,0 +Chinese cabbage - Brassica rapa ssp. pekinensis,1.00,2880,4.0,28.0,1 +Fenugreek - Trigonella foenum-graecum,0.20,360,25.0,30.0,1 +Cotton - Luyanmian21,1.50,180,20.0,32.0,1 +Watermelon - G42,0.10,80,25.0,30.0,0 +Hemp - Seiki Selskin,1.00,180,25.0,30.0,0 +Fodder Barley - Hordeum vulgare,0.30,90,24.0,24.0,1 +Maize - Zea mays (B73),1.20,720,25.0,30.0,0 +Hyoscyamus niger - Black Henbane,0.09,60,25.0,28.0,0 +Hemp - Kumamoto,0.25,360,25.0,30.0,0 +Chinese cabbage - Brassica rapa ssp. pekinensis,0.30,480,4.0,20.0,0 +Marigold - Tagetes erecta,0.08,360,25.0,30.0,0 diff --git a/ems-model/csv/ems_journal.csv b/ems-model/csv/ems_journal.csv new file mode 100644 index 0000000..4b3b12a --- /dev/null +++ b/ems-model/csv/ems_journal.csv @@ -0,0 +1,155 @@ +species,emsConcentration,soakDuration,lowestTemp,highestTemp,result +Hemp - Kumamoto,0.25,180,25,30,0 +Hemp - Kumamoto,0.50,180,25,30,1 +Hemp - Kumamoto,0.75,180,25,30,1 +Hemp - Kumamoto,1.00,180,25,30,0 +Hemp - Kumamoto,0.25,360,25,30,0 +Hemp - Kumamoto,0.50,360,25,30,1 +Hemp - Kumamoto,0.75,360,25,30,0 +Hemp - Kumamoto,1.00,360,25,30,1 +Hemp - Seiki Selskin,0.25,180,25,30,1 +Hemp - Seiki Selskin,0.50,180,25,30,1 +Hemp - Seiki Selskin,0.75,180,25,30,1 +Hemp - Seiki Selskin,1.00,180,25,30,0 +Hemp - Seiki Selskin,0.25,360,25,30,0 +Hemp - Seiki Selskin,0.50,360,25,30,0 +Hemp - Seiki Selskin,0.75,360,25,30,1 +Hemp - Seiki Selskin,1.00,360,25,30,1 +Hemp - Lembang A,0.25,180,25,30,1 +Hemp - Lembang A,0.50,180,25,30,1 +Hemp - Lembang A,0.75,180,25,30,1 +Hemp - Lembang A,1.00,180,25,30,0 +Hemp - Lembang A,0.25,360,25,30,0 +Hemp - Lembang A,0.50,6,25,30,0 +Hemp - Lembang A,0.75,6,25,30,1 +Hemp - Lembang A,1.00,360,25,30,0 +Hemp - Bandung A,0.25,180,25,30,0 +Hemp - Bandung A,0.50,180,25,30,1 +Hemp - Bandung A,0.75,180,25,30,1 +Hemp - Bandung A,1.00,180,25,30,0 +Hemp - Bandung A,0.25,360,25,30,1 +Hemp - Bandung A,0.50,360,25,30,1 +Hemp - Bandung A,0.75,360,25,30,1 +Hemp - Bandung A,1.00,360,25,30,0 +Hemp - Indochina,0.25,180,25,30,0 +Hemp - Indochina,0.50,180,25,30,1 +Hemp - Indochina,0.75,180,25,30,1 +Hemp - Indochina,1.00,180,25,30,1 +Hemp - Indochina,0.25,360,25,30,0 +Hemp - Indochina,0.50,360,25,30,1 +Hemp - Indochina,0.75,360,25,30,1 +Hemp - Indochina,1.00,360,25,30,1 +Marigold - Tagetes erecta,0.0,240,25,30,0 +Marigold - Tagetes erecta,0.2,240,25,30,0 +Marigold - Tagetes erecta,0.4,240,25,30,1 +Marigold - Tagetes erecta,0.6,240,25,30,1 +Marigold - Tagetes erecta,0.8,240,25,30,1 +Marigold - Tagetes erecta,1.0,240,25,30,0 +Marigold - Tagetes erecta,0.082,360,25,30,0 +Marigold - Tagetes erecta,0.0082,1440,25,30,1 +Marigold - Tagetes erecta,0.00082,2880,25,30,1 +Marigold - Tagetes patula,0.0,240,25,30,0 +Marigold - Tagetes patula,0.2,240,25,30,0 +Marigold - Tagetes patula,0.4,240,25,30,0 +Marigold - Tagetes patula,0.6,240,25,30,0 +Marigold - Tagetes patula,0.8,240,25,30,0 +Marigold - Tagetes patula,1.0,240,25,30,0 +Marigold - Tagetes patula,1.5,240,25,30,0 +Marigold - Tagetes patula,2.0,240,25,30,1 +Marigold - Tagetes patula,2.5,240,25,30,0 +Marigold - Tagetes patula,3.0,240,25,30,0 +Marigold - Tagetes patula,0.187,360,25,30,1 +Marigold - Tagetes patula,0.0187,1440,25,30,0 +Marigold - Tagetes patula,0.00187,2880,25,30,1 +Fodder Barley - Hordeum vulgare,0.1,30,24,24,0 +Fodder Barley - Hordeum vulgare,0.1,60,24,24,1 +Fodder Barley - Hordeum vulgare,0.1,90,24,24,1 +Fodder Barley - Hordeum vulgare,0.1,120,24,24,1 +Fodder Barley - Hordeum vulgare,0.1,150,24,24,0 +Fodder Barley - Hordeum vulgare,0.3,30,24,24,0 +Fodder Barley - Hordeum vulgare,0.3,60,24,24,1 +Fodder Barley - Hordeum vulgare,0.3,90,24,24,1 +Fodder Barley - Hordeum vulgare,0.3,120,24,24,0 +Fodder Barley - Hordeum vulgare,0.3,150,24,24,0 +Fodder Barley - Hordeum vulgare,0.5,30,24,24,0 +Fodder Barley - Hordeum vulgare,0.5,60,24,24,0 +Fodder Barley - Hordeum vulgare,0.5,90,24,24,0 +Fodder Barley - Hordeum vulgare,0.5,120,24,24,0 +Fodder Barley - Hordeum vulgare,0.5,150,24,24,0 +Fodder Barley - Hordeum vulgare,0.7,30,24,24,0 +Fodder Barley - Hordeum vulgare,0.7,60,24,24,0 +Fodder Barley - Hordeum vulgare,0.7,90,24,24,0 +Fodder Barley - Hordeum vulgare,0.7,120,24,24,0 +Fodder Barley - Hordeum vulgare,0.7,150,24,24,0 +Fodder Barley - Hordeum vulgare,0.9,30,24,24,0 +Fodder Barley - Hordeum vulgare,0.9,60,24,24,0 +Fodder Barley - Hordeum vulgare,0.9,90,24,24,0 +Fodder Barley - Hordeum vulgare,0.9,120,24,24,0 +Fodder Barley - Hordeum vulgare,0.9,150,24,24,0 +Watermelon - G42,0.1,40,25,30,1 +Watermelon - G42,0.1,60,25,30,1 +Watermelon - G42,0.1,80,25,30,0 +Watermelon - G42,0.15,40,25,30,1 +Watermelon - G42,0.15,60,25,30,1 +Watermelon - G42,0.15,80,25,30,0 +Watermelon - G42,0.2,40,25,30,0 +Watermelon - G42,0.2,60,25,30,0 +Watermelon - G42,0.2,80,25,30,0 +Cotton - TM-1,0.5,180,20,32,0 +Cotton - TM-1,1.5,180,20,32,1 +Cotton - TM-1,2.5,180,20,32,0 +Cotton - TM-1,3.5,180,20,32,0 +Cotton - TM-1,0.5,360,20,32,0 +Cotton - TM-1,1.5,360,20,32,1 +Cotton - TM-1,2.5,360,20,32,0 +Cotton - TM-1,3.5,360,20,32,0 +Cotton - Luyanmian21,0.5,180,20,32,0 +Cotton - Luyanmian21,1.5,180,20,32,1 +Cotton - Luyanmian21,2.5,180,20,32,0 +Cotton - Luyanmian21,3.5,180,20,32,0 +Cotton - Luyanmian21,0.5,360,20,32,0 +Cotton - Luyanmian21,1.5,360,20,32,1 +Cotton - Luyanmian21,2.5,360,20,32,0 +Cotton - Luyanmian21,3.5,360,20,32,0 +Sesame - Tilottama,0.25,120,23,25,0 +Sesame - Tilottama,0.25,240,23,25,0 +Sesame - Tilottama,0.25,360,23,25,0 +Sesame - Tilottama,0.50,120,23,25,0 +Sesame - Tilottama,0.50,240,23,25,0 +Sesame - Tilottama,0.50,360,23,25,1 +Sesame - Tilottama,1.00,120,23,25,0 +Sesame - Tilottama,1.00,240,23,25,1 +Sesame - Tilottama,1.00,360,23,25,1 +Chinese cabbage - Brassica rapa ssp. pekinensis,0.5,720,4,25,1 +Chinese cabbage - Brassica rapa ssp. pekinensis,0.3,480,4,20,0 +Chinese cabbage - Brassica rapa ssp. pekinensis,0.7,1440,5,25,1 +Chinese cabbage - Brassica rapa ssp. pekinensis,0.1,360,3,18,0 +Chinese cabbage - Brassica rapa ssp. pekinensis,1.0,2880,4,28,1 +Jasmine - Jasminum grandiflorum,0.25,60,28,30,1 +Jasmine - Jasminum grandiflorum,0.30,60,28,30,1 +Jasmine - Jasminum grandiflorum,0.35,60,28,30,0 +Jasmine - Jasminum grandiflorum,0.40,60,28,30,0 +Maize - Zea mays (B73),1.0,120,25,30,1 +Maize - Zea mays (B73),0.5,240,25,30,0 +Maize - Zea mays (B73),0.7,480,25,30,1 +Maize - Zea mays (B73),1.2,720,25,30,0 +Sunflower - Helianthus annuus (BBS-1),0.5,480,22,25,1 +Sunflower - Helianthus annuus (BBS-1),0.6,480,22,25,1 +Sunflower - Helianthus annuus (BBS-1),0.75,480,22,25,0 +Sunflower - Helianthus annuus (BBS-1),1.0,480,22,25,0 +Sunflower - Helianthus annuus (BBS-1),2.0,480,22,25,0 +Sunflower - Helianthus annuus (BBS-1),3.0,480,22,25,0 +Fenugreek - Trigonella foenum-graecum,0.1,360,25,30,1 +Fenugreek - Trigonella foenum-graecum,0.2,360,25,30,1 +Fenugreek - Trigonella foenum-graecum,0.3,360,25,30,0 +Fenugreek - Trigonella foenum-graecum,0.4,360,25,30,0 +Hyoscyamus niger - Black Henbane,0.01,60,25,28,1 +Hyoscyamus niger - Black Henbane,0.02,60,25,28,1 +Hyoscyamus niger - Black Henbane,0.03,60,25,28,1 +Hyoscyamus niger - Black Henbane,0.04,60,25,28,0 +Hyoscyamus niger - Black Henbane,0.05,60,25,28,0 +Hyoscyamus niger - Black Henbane,0.06,60,25,28,0 +Hyoscyamus niger - Black Henbane,0.07,60,25,28,0 +Hyoscyamus niger - Black Henbane,0.08,60,25,28,0 +Hyoscyamus niger - Black Henbane,0.09,60,25,28,0 +Hyoscyamus niger - Black Henbane,0.1,60,25,28,0 diff --git a/ems-model/csv/ems_test.csv b/ems-model/csv/ems_test.csv new file mode 100644 index 0000000..e69de29 diff --git a/ems-model/model.ipynb b/ems-model/model.ipynb new file mode 100644 index 0000000..c751fef --- /dev/null +++ b/ems-model/model.ipynb @@ -0,0 +1,1671 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "564dfa7f-55ae-423a-ae59-22ac4f00364f", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "from sklearn.model_selection import StratifiedShuffleSplit, cross_val_score, train_test_split\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.preprocessing import LabelEncoder, MinMaxScaler\n", + "from sklearn.metrics import classification_report, confusion_matrix\n", + "import numpy as np\n", + "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", + "import pickle\n", + "import os\n", + "import subprocess\n", + "np.random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b0867074-62b1-4bff-9188-0c6e73f814d0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CSV Loaded, Dataset shape: (154, 6)\n" + ] + } + ], + "source": [ + "# Load the data\n", + "df = pd.read_csv(\"csv/ems_data.csv\")\n", + "print(\"CSV Loaded, Dataset shape:\", df.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "0e2ebde8-90f1-483a-a3cb-4119878459d2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + " | species | \n", + "emsConcentration | \n", + "soakDuration | \n", + "lowestTemp | \n", + "highestTemp | \n", + "result | \n", + "
---|---|---|---|---|---|---|
0 | \n", + "Cotton - TM-1 | \n", + "1.50 | \n", + "180 | \n", + "20.0 | \n", + "32.0 | \n", + "1 | \n", + "
1 | \n", + "Hemp - Lembang A | \n", + "0.25 | \n", + "360 | \n", + "25.0 | \n", + "30.0 | \n", + "0 | \n", + "
2 | \n", + "Cotton - TM-1 | \n", + "3.50 | \n", + "360 | \n", + "20.0 | \n", + "32.0 | \n", + "0 | \n", + "
3 | \n", + "Hyoscyamus niger - Black Henbane | \n", + "0.02 | \n", + "60 | \n", + "25.0 | \n", + "28.0 | \n", + "1 | \n", + "
4 | \n", + "Sunflower - Helianthus annuus (BBS-1) | \n", + "1.00 | \n", + "480 | \n", + "22.0 | \n", + "25.0 | \n", + "0 | \n", + "
... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "
149 | \n", + "Maize - Zea mays (B73) | \n", + "1.20 | \n", + "720 | \n", + "25.0 | \n", + "30.0 | \n", + "0 | \n", + "
150 | \n", + "Hyoscyamus niger - Black Henbane | \n", + "0.09 | \n", + "60 | \n", + "25.0 | \n", + "28.0 | \n", + "0 | \n", + "
151 | \n", + "Hemp - Kumamoto | \n", + "0.25 | \n", + "360 | \n", + "25.0 | \n", + "30.0 | \n", + "0 | \n", + "
152 | \n", + "Chinese cabbage - Brassica rapa ssp. pekinensis | \n", + "0.30 | \n", + "480 | \n", + "4.0 | \n", + "20.0 | \n", + "0 | \n", + "
153 | \n", + "Marigold - Tagetes erecta | \n", + "0.08 | \n", + "360 | \n", + "25.0 | \n", + "30.0 | \n", + "0 | \n", + "
154 rows × 6 columns
\n", + "\n", + " | species | \n", + "sum | \n", + "
---|---|---|
0 | \n", + "Chinese cabbage - Brassica rapa ssp. pekinensis | \n", + "5 | \n", + "
1 | \n", + "Cotton - Luyanmian21 | \n", + "8 | \n", + "
2 | \n", + "Cotton - TM-1 | \n", + "8 | \n", + "
3 | \n", + "Fenugreek - Trigonella foenum-graecum | \n", + "4 | \n", + "
4 | \n", + "Fodder Barley - Hordeum vulgare | \n", + "25 | \n", + "
5 | \n", + "Hemp - Bandung A | \n", + "8 | \n", + "
6 | \n", + "Hemp - Indochina | \n", + "8 | \n", + "
7 | \n", + "Hemp - Kumamoto | \n", + "8 | \n", + "
8 | \n", + "Hemp - Lembang A | \n", + "8 | \n", + "
9 | \n", + "Hemp - Seiki Selskin | \n", + "8 | \n", + "
10 | \n", + "Hyoscyamus niger - Black Henbane | \n", + "10 | \n", + "
11 | \n", + "Jasmine - Jasminum grandiflorum | \n", + "4 | \n", + "
12 | \n", + "Maize - Zea mays (B73) | \n", + "4 | \n", + "
13 | \n", + "Marigold - Tagetes erecta | \n", + "9 | \n", + "
14 | \n", + "Marigold - Tagetes patula | \n", + "13 | \n", + "
15 | \n", + "Sesame - Tilottama | \n", + "9 | \n", + "
16 | \n", + "Sunflower - Helianthus annuus (BBS-1) | \n", + "6 | \n", + "
17 | \n", + "Watermelon - G42 | \n", + "9 | \n", + "
\n", + " | soakDuration | \n", + "lowestTemp | \n", + "highestTemp | \n", + "
---|---|---|---|
0 | \n", + "180 | \n", + "20.0 | \n", + "32.0 | \n", + "
1 | \n", + "360 | \n", + "25.0 | \n", + "30.0 | \n", + "
2 | \n", + "360 | \n", + "20.0 | \n", + "32.0 | \n", + "
3 | \n", + "60 | \n", + "25.0 | \n", + "28.0 | \n", + "
4 | \n", + "480 | \n", + "22.0 | \n", + "25.0 | \n", + "
... | \n", + "... | \n", + "... | \n", + "... | \n", + "
149 | \n", + "720 | \n", + "25.0 | \n", + "30.0 | \n", + "
150 | \n", + "60 | \n", + "25.0 | \n", + "28.0 | \n", + "
151 | \n", + "360 | \n", + "25.0 | \n", + "30.0 | \n", + "
152 | \n", + "480 | \n", + "4.0 | \n", + "20.0 | \n", + "
153 | \n", + "360 | \n", + "25.0 | \n", + "30.0 | \n", + "
154 rows × 3 columns
\n", + "\n", + " | soakDuration | \n", + "lowestTemp | \n", + "highestTemp | \n", + "
---|---|---|---|
0 | \n", + "0.060543 | \n", + "0.68 | \n", + "1.000000 | \n", + "
1 | \n", + "0.123173 | \n", + "0.88 | \n", + "0.857143 | \n", + "
2 | \n", + "0.123173 | \n", + "0.68 | \n", + "1.000000 | \n", + "
3 | \n", + "0.018789 | \n", + "0.88 | \n", + "0.714286 | \n", + "
4 | \n", + "0.164927 | \n", + "0.76 | \n", + "0.500000 | \n", + "
... | \n", + "... | \n", + "... | \n", + "... | \n", + "
149 | \n", + "0.248434 | \n", + "0.88 | \n", + "0.857143 | \n", + "
150 | \n", + "0.018789 | \n", + "0.88 | \n", + "0.714286 | \n", + "
151 | \n", + "0.123173 | \n", + "0.88 | \n", + "0.857143 | \n", + "
152 | \n", + "0.164927 | \n", + "0.04 | \n", + "0.142857 | \n", + "
153 | \n", + "0.123173 | \n", + "0.88 | \n", + "0.857143 | \n", + "
154 rows × 3 columns
\n", + "\n", + " | species | \n", + "emsConcentration | \n", + "soakDuration | \n", + "lowestTemp | \n", + "highestTemp | \n", + "result | \n", + "
---|---|---|---|---|---|---|
0 | \n", + "2 | \n", + "1.50 | \n", + "0.060543 | \n", + "0.68 | \n", + "1.000000 | \n", + "1 | \n", + "
1 | \n", + "8 | \n", + "0.25 | \n", + "0.123173 | \n", + "0.88 | \n", + "0.857143 | \n", + "0 | \n", + "
2 | \n", + "2 | \n", + "3.50 | \n", + "0.123173 | \n", + "0.68 | \n", + "1.000000 | \n", + "0 | \n", + "
3 | \n", + "10 | \n", + "0.02 | \n", + "0.018789 | \n", + "0.88 | \n", + "0.714286 | \n", + "1 | \n", + "
4 | \n", + "16 | \n", + "1.00 | \n", + "0.164927 | \n", + "0.76 | \n", + "0.500000 | \n", + "0 | \n", + "
... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "... | \n", + "
149 | \n", + "12 | \n", + "1.20 | \n", + "0.248434 | \n", + "0.88 | \n", + "0.857143 | \n", + "0 | \n", + "
150 | \n", + "10 | \n", + "0.09 | \n", + "0.018789 | \n", + "0.88 | \n", + "0.714286 | \n", + "0 | \n", + "
151 | \n", + "7 | \n", + "0.25 | \n", + "0.123173 | \n", + "0.88 | \n", + "0.857143 | \n", + "0 | \n", + "
152 | \n", + "0 | \n", + "0.30 | \n", + "0.164927 | \n", + "0.04 | \n", + "0.142857 | \n", + "0 | \n", + "
153 | \n", + "13 | \n", + "0.08 | \n", + "0.123173 | \n", + "0.88 | \n", + "0.857143 | \n", + "0 | \n", + "
154 rows × 6 columns
\n", + "RandomForestClassifier(random_state=42)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
RandomForestClassifier(random_state=42)
1?u.getGridIndex():u.getColIndex();e.dispatch("MOVE_ITEM",{query:o,index:f});let g=a.getIndex();if(g===void 0||g!==f){if(a.setIndex(f),g===void 0)return;e.dispatch("DID_REORDER_ITEMS",{items:e.query("GET_ACTIVE_ITEMS"),origin:r,target:f})}},vc=ge({DID_ADD_ITEM:gc,DID_REMOVE_ITEM:Ec,DID_DRAG_ITEM:Tc}),Ic=({root:e,props:t,actions:i,shouldOptimize:a})=>{vc({root:e,props:t,actions:i});let{dragCoordinates:n}=t,o=e.rect.element.width,l=e.childViews.filter(T=>T.rect.element.height),r=e.query("GET_ACTIVE_ITEMS").map(T=>l.find(I=>I.id===T.id)).filter(T=>T),s=n?Ji(e,r,n):null,p=e.ref.addIndex||null;e.ref.addIndex=null;let c=0,d=0,m=0;if(r.length===0)return;let u=r[0].rect.element,f=u.marginTop+u.marginBottom,g=u.marginLeft+u.marginRight,h=u.width+g,v=u.height+f,E=Ki(o,h);if(E===1){let T=0,I=0;r.forEach((y,b)=>{if(s){let _=b-s;_===-2?I=-f*.25:_===-1?I=-f*.75:_===0?I=f*.75:_===1?I=f*.25:I=0}a&&(y.translateX=null,y.translateY=null),y.markedForRemoval||ln(y,0,T+I);let x=(y.rect.element.height+f)*(y.markedForRemoval?y.opacity:1);T+=x})}else{let T=0,I=0;r.forEach((y,b)=>{b===s&&(c=1),b===p&&(m+=1),y.markedForRemoval&&y.opacity<.5&&(d-=1);let w=b+m+c+d,x=w%E,_=Math.floor(w/E),P=x*h,O=_*v,M=Math.sign(P-T),N=Math.sign(O-I);T=P,I=O,!y.markedForRemoval&&(a&&(y.translateX=null,y.translateY=null),ln(y,P,O,M,N))})}},xc=(e,t)=>t.filter(i=>i.data&&i.data.id?e.id===i.data.id:!0),yc=ne({create:fc,write:Ic,tag:"ul",name:"list",didWriteView:({root:e})=>{e.childViews.filter(t=>t.markedForRemoval&&t.opacity===0&&t.resting).forEach(t=>{t._destroy(),e.removeChildView(t)})},filterFrameActionsForChild:xc,mixins:{apis:["dragCoordinates"]}}),_c=({root:e,props:t})=>{e.ref.list=e.appendChildView(e.createChildView(yc)),t.dragCoordinates=null,t.overflowing=!1},Rc=({root:e,props:t,action:i})=>{e.query("GET_ITEM_INSERT_LOCATION_FREEDOM")&&(t.dragCoordinates={left:i.position.scopeLeft-e.ref.list.rect.element.left,top:i.position.scopeTop-(e.rect.outer.top+e.rect.element.marginTop+e.rect.element.scrollTop)})},wc=({props:e})=>{e.dragCoordinates=null},Sc=ge({DID_DRAG:Rc,DID_END_DRAG:wc}),Lc=({root:e,props:t,actions:i})=>{if(Sc({root:e,props:t,actions:i}),e.ref.list.dragCoordinates=t.dragCoordinates,t.overflowing&&!t.overflow&&(t.overflowing=!1,e.element.dataset.state="",e.height=null),t.overflow){let a=Math.round(t.overflow);a!==e.height&&(t.overflowing=!0,e.element.dataset.state="overflow",e.height=a)}},Ac=ne({create:_c,write:Lc,name:"list-scroller",mixins:{apis:["overflow","dragCoordinates"],styles:["height","translateY"],animations:{translateY:"spring"}}}),Oe=(e,t,i,a="")=>{i?se(e,t,a):e.removeAttribute(t)},Mc=e=>{if(!(!e||e.value==="")){try{e.value=""}catch{}if(e.value){let t=ke("form"),i=e.parentNode,a=e.nextSibling;t.appendChild(e),t.reset(),a?i.insertBefore(e,a):i.appendChild(e)}}},Oc=({root:e,props:t})=>{e.element.id=`filepond--browser-${t.id}`,se(e.element,"name",e.query("GET_NAME")),se(e.element,"aria-controls",`filepond--assistant-${t.id}`),se(e.element,"aria-labelledby",`filepond--drop-label-${t.id}`),Gn({root:e,action:{value:e.query("GET_ACCEPTED_FILE_TYPES")}}),Un({root:e,action:{value:e.query("GET_ALLOW_MULTIPLE")}}),Wn({root:e,action:{value:e.query("GET_ALLOW_DIRECTORIES_ONLY")}}),ki({root:e}),Hn({root:e,action:{value:e.query("GET_REQUIRED")}}),jn({root:e,action:{value:e.query("GET_CAPTURE_METHOD")}}),e.ref.handleChange=i=>{if(!e.element.value)return;let a=Array.from(e.element.files).map(n=>(n._relativePath=n.webkitRelativePath,n));setTimeout(()=>{t.onload(a),Mc(e.element)},250)},e.element.addEventListener("change",e.ref.handleChange)},Gn=({root:e,action:t})=>{e.query("GET_ALLOW_SYNC_ACCEPT_ATTRIBUTE")&&Oe(e.element,"accept",!!t.value,t.value?t.value.join(","):"")},Un=({root:e,action:t})=>{Oe(e.element,"multiple",t.value)},Wn=({root:e,action:t})=>{Oe(e.element,"webkitdirectory",t.value)},ki=({root:e})=>{let t=e.query("GET_DISABLED"),i=e.query("GET_ALLOW_BROWSE"),a=t||!i;Oe(e.element,"disabled",a)},Hn=({root:e,action:t})=>{t.value?e.query("GET_TOTAL_ITEMS")===0&&Oe(e.element,"required",!0):Oe(e.element,"required",!1)},jn=({root:e,action:t})=>{Oe(e.element,"capture",!!t.value,t.value===!0?"":t.value)},rn=({root:e})=>{let{element:t}=e;e.query("GET_TOTAL_ITEMS")>0?(Oe(t,"required",!1),Oe(t,"name",!1)):(Oe(t,"name",!0,e.query("GET_NAME")),e.query("GET_CHECK_VALIDITY")&&t.setCustomValidity(""),e.query("GET_REQUIRED")&&Oe(t,"required",!0))},Pc=({root:e})=>{e.query("GET_CHECK_VALIDITY")&&e.element.setCustomValidity(e.query("GET_LABEL_INVALID_FIELD"))},Dc=ne({tag:"input",name:"browser",ignoreRect:!0,ignoreRectUpdate:!0,attributes:{type:"file"},create:Oc,destroy:({root:e})=>{e.element.removeEventListener("change",e.ref.handleChange)},write:ge({DID_LOAD_ITEM:rn,DID_REMOVE_ITEM:rn,DID_THROW_ITEM_INVALID:Pc,DID_SET_DISABLED:ki,DID_SET_ALLOW_BROWSE:ki,DID_SET_ALLOW_DIRECTORIES_ONLY:Wn,DID_SET_ALLOW_MULTIPLE:Un,DID_SET_ACCEPTED_FILE_TYPES:Gn,DID_SET_CAPTURE_METHOD:jn,DID_SET_REQUIRED:Hn})}),sn={ENTER:13,SPACE:32},Fc=({root:e,props:t})=>{let i=ke("label");se(i,"for",`filepond--browser-${t.id}`),se(i,"id",`filepond--drop-label-${t.id}`),e.ref.handleKeyDown=a=>{(a.keyCode===sn.ENTER||a.keyCode===sn.SPACE)&&(a.preventDefault(),e.ref.label.click())},e.ref.handleClick=a=>{a.target===i||i.contains(a.target)||e.ref.label.click()},i.addEventListener("keydown",e.ref.handleKeyDown),e.element.addEventListener("click",e.ref.handleClick),Yn(i,t.caption),e.appendChild(i),e.ref.label=i},Yn=(e,t)=>{e.innerHTML=t;let i=e.querySelector(".filepond--label-action");return i&&se(i,"tabindex","0"),t},zc=ne({name:"drop-label",ignoreRect:!0,create:Fc,destroy:({root:e})=>{e.ref.label.addEventListener("keydown",e.ref.handleKeyDown),e.element.removeEventListener("click",e.ref.handleClick)},write:ge({DID_SET_LABEL_IDLE:({root:e,action:t})=>{Yn(e.ref.label,t.value)}}),mixins:{styles:["opacity","translateX","translateY"],animations:{opacity:{type:"tween",duration:150},translateX:"spring",translateY:"spring"}}}),Cc=ne({name:"drip-blob",ignoreRect:!0,mixins:{styles:["translateX","translateY","scaleX","scaleY","opacity"],animations:{scaleX:"spring",scaleY:"spring",translateX:"spring",translateY:"spring",opacity:{type:"tween",duration:250}}}}),Nc=({root:e})=>{let t=e.rect.element.width*.5,i=e.rect.element.height*.5;e.ref.blob=e.appendChildView(e.createChildView(Cc,{opacity:0,scaleX:2.5,scaleY:2.5,translateX:t,translateY:i}))},Bc=({root:e,action:t})=>{if(!e.ref.blob){Nc({root:e});return}e.ref.blob.translateX=t.position.scopeLeft,e.ref.blob.translateY=t.position.scopeTop,e.ref.blob.scaleX=1,e.ref.blob.scaleY=1,e.ref.blob.opacity=1},kc=({root:e})=>{e.ref.blob&&(e.ref.blob.opacity=0)},Vc=({root:e})=>{e.ref.blob&&(e.ref.blob.scaleX=2.5,e.ref.blob.scaleY=2.5,e.ref.blob.opacity=0)},Gc=({root:e,props:t,actions:i})=>{Uc({root:e,props:t,actions:i});let{blob:a}=e.ref;i.length===0&&a&&a.opacity===0&&(e.removeChildView(a),e.ref.blob=null)},Uc=ge({DID_DRAG:Bc,DID_DROP:Vc,DID_END_DRAG:kc}),Wc=ne({ignoreRect:!0,ignoreRectUpdate:!0,name:"drip",write:Gc}),qn=(e,t)=>{try{let i=new DataTransfer;t.forEach(a=>{a instanceof File?i.items.add(a):i.items.add(new File([a],a.name,{type:a.type}))}),e.files=i.files}catch{return!1}return!0},Hc=({root:e})=>{e.ref.fields={};let t=document.createElement("legend");t.textContent="Files",e.element.appendChild(t)},fi=(e,t)=>e.ref.fields[t],ea=e=>{e.query("GET_ACTIVE_ITEMS").forEach(t=>{e.ref.fields[t.id]&&e.element.appendChild(e.ref.fields[t.id])})},cn=({root:e})=>ea(e),jc=({root:e,action:t})=>{let n=!(e.query("GET_ITEM",t.id).origin===re.LOCAL)&&e.query("SHOULD_UPDATE_FILE_INPUT"),o=ke("input");o.type=n?"file":"hidden",o.name=e.query("GET_NAME"),e.ref.fields[t.id]=o,ea(e)},Yc=({root:e,action:t})=>{let i=fi(e,t.id);if(!i||(t.serverFileReference!==null&&(i.value=t.serverFileReference),!e.query("SHOULD_UPDATE_FILE_INPUT")))return;let a=e.query("GET_ITEM",t.id);qn(i,[a.file])},qc=({root:e,action:t})=>{e.query("SHOULD_UPDATE_FILE_INPUT")&&setTimeout(()=>{let i=fi(e,t.id);i&&qn(i,[t.file])},0)},$c=({root:e})=>{e.element.disabled=e.query("GET_DISABLED")},Xc=({root:e,action:t})=>{let i=fi(e,t.id);i&&(i.parentNode&&i.parentNode.removeChild(i),delete e.ref.fields[t.id])},Qc=({root:e,action:t})=>{let i=fi(e,t.id);i&&(t.value===null?i.removeAttribute("value"):i.type!="file"&&(i.value=t.value),ea(e))},Zc=ge({DID_SET_DISABLED:$c,DID_ADD_ITEM:jc,DID_LOAD_ITEM:Yc,DID_REMOVE_ITEM:Xc,DID_DEFINE_VALUE:Qc,DID_PREPARE_OUTPUT:qc,DID_REORDER_ITEMS:cn,DID_SORT_ITEMS:cn}),Kc=ne({tag:"fieldset",name:"data",create:Hc,write:Zc,ignoreRect:!0}),Jc=e=>"getRootNode"in e?e.getRootNode():document,ed=["jpg","jpeg","png","gif","bmp","webp","svg","tiff"],td=["css","csv","html","txt"],id={zip:"zip|compressed",epub:"application/epub+zip"},$n=(e="")=>(e=e.toLowerCase(),ed.includes(e)?"image/"+(e==="jpg"?"jpeg":e==="svg"?"svg+xml":e):td.includes(e)?"text/"+e:id[e]||""),ta=e=>new Promise((t,i)=>{let a=dd(e);if(a.length&&!ad(e))return t(a);nd(e).then(t)}),ad=e=>e.files?e.files.length>0:!1,nd=e=>new Promise((t,i)=>{let a=(e.items?Array.from(e.items):[]).filter(n=>od(n)).map(n=>ld(n));if(!a.length){t(e.files?Array.from(e.files):[]);return}Promise.all(a).then(n=>{let o=[];n.forEach(l=>{o.push.apply(o,l)}),t(o.filter(l=>l).map(l=>(l._relativePath||(l._relativePath=l.webkitRelativePath),l)))}).catch(console.error)}),od=e=>{if(Xn(e)){let t=ia(e);if(t)return t.isFile||t.isDirectory}return e.kind==="file"},ld=e=>new Promise((t,i)=>{if(cd(e)){rd(ia(e)).then(t).catch(i);return}t([e.getAsFile()])}),rd=e=>new Promise((t,i)=>{let a=[],n=0,o=0,l=()=>{o===0&&n===0&&t(a)},r=s=>{n++;let p=s.createReader(),c=()=>{p.readEntries(d=>{if(d.length===0){n--,l();return}d.forEach(m=>{m.isDirectory?r(m):(o++,m.file(u=>{let f=sd(u);m.fullPath&&(f._relativePath=m.fullPath),a.push(f),o--,l()}))}),c()},i)};c()};r(e)}),sd=e=>{if(e.type.length)return e;let t=e.lastModifiedDate,i=e.name,a=$n(ui(e.name));return a.length&&(e=e.slice(0,e.size,a),e.name=i,e.lastModifiedDate=t),e},cd=e=>Xn(e)&&(ia(e)||{}).isDirectory,Xn=e=>"webkitGetAsEntry"in e,ia=e=>e.webkitGetAsEntry(),dd=e=>{let t=[];try{if(t=md(e),t.length)return t;t=pd(e)}catch{}return t},pd=e=>{let t=e.getData("url");return typeof t=="string"&&t.length?[t]:[]},md=e=>{let t=e.getData("text/html");if(typeof t=="string"&&t.length){let i=t.match(/src\s*=\s*"(.+?)"/);if(i)return[i[1]]}return[]},ri=[],et=e=>({pageLeft:e.pageX,pageTop:e.pageY,scopeLeft:e.offsetX||e.layerX,scopeTop:e.offsetY||e.layerY}),ud=(e,t,i)=>{let a=fd(t),n={element:e,filterElement:i,state:null,ondrop:()=>{},onenter:()=>{},ondrag:()=>{},onexit:()=>{},onload:()=>{},allowdrop:()=>{}};return n.destroy=a.addListener(n),n},fd=e=>{let t=ri.find(a=>a.element===e);if(t)return t;let i=gd(e);return ri.push(i),i},gd=e=>{let t=[],i={dragenter:Ed,dragover:bd,dragleave:vd,drop:Td},a={};te(i,(o,l)=>{a[o]=l(e,t),e.addEventListener(o,a[o],!1)});let n={element:e,addListener:o=>(t.push(o),()=>{t.splice(t.indexOf(o),1),t.length===0&&(ri.splice(ri.indexOf(n),1),te(i,l=>{e.removeEventListener(l,a[l],!1)}))})};return n},hd=(e,t)=>("elementFromPoint"in e||(e=document),e.elementFromPoint(t.x,t.y)),aa=(e,t)=>{let i=Jc(t),a=hd(i,{x:e.pageX-window.pageXOffset,y:e.pageY-window.pageYOffset});return a===t||t.contains(a)},Qn=null,ii=(e,t)=>{try{e.dropEffect=t}catch{}},Ed=(e,t)=>i=>{i.preventDefault(),Qn=i.target,t.forEach(a=>{let{element:n,onenter:o}=a;aa(i,n)&&(a.state="enter",o(et(i)))})},bd=(e,t)=>i=>{i.preventDefault();let a=i.dataTransfer;ta(a).then(n=>{let o=!1;t.some(l=>{let{filterElement:r,element:s,onenter:p,onexit:c,ondrag:d,allowdrop:m}=l;ii(a,"copy");let u=m(n);if(!u){ii(a,"none");return}if(aa(i,s)){if(o=!0,l.state===null){l.state="enter",p(et(i));return}if(l.state="over",r&&!u){ii(a,"none");return}d(et(i))}else r&&!o&&ii(a,"none"),l.state&&(l.state=null,c(et(i)))})})},Td=(e,t)=>i=>{i.preventDefault();let a=i.dataTransfer;ta(a).then(n=>{t.forEach(o=>{let{filterElement:l,element:r,ondrop:s,onexit:p,allowdrop:c}=o;if(o.state=null,!(l&&!aa(i,r))){if(!c(n))return p(et(i));s(et(i),n)}})})},vd=(e,t)=>i=>{Qn===i.target&&t.forEach(a=>{let{onexit:n}=a;a.state=null,n(et(i))})},Id=(e,t,i)=>{e.classList.add("filepond--hopper");let{catchesDropsOnPage:a,requiresDropOnElement:n,filterItems:o=c=>c}=i,l=ud(e,a?document.documentElement:e,n),r="",s="";l.allowdrop=c=>t(o(c)),l.ondrop=(c,d)=>{let m=o(d);if(!t(m)){p.ondragend(c);return}s="drag-drop",p.onload(m,c)},l.ondrag=c=>{p.ondrag(c)},l.onenter=c=>{s="drag-over",p.ondragstart(c)},l.onexit=c=>{s="drag-exit",p.ondragend(c)};let p={updateHopperState:()=>{r!==s&&(e.dataset.hopperState=s,r=s)},onload:()=>{},ondragstart:()=>{},ondrag:()=>{},ondragend:()=>{},destroy:()=>{l.destroy()}};return p},Vi=!1,ut=[],Zn=e=>{let t=document.activeElement;if(t&&(/textarea|input/i.test(t.nodeName)||t.getAttribute("contenteditable")==="true")){let a=!1,n=t;for(;n!==document.body;){if(n.classList.contains("filepond--root")){a=!0;break}n=n.parentNode}if(!a)return}ta(e.clipboardData).then(a=>{a.length&&ut.forEach(n=>n(a))})},xd=e=>{ut.includes(e)||(ut.push(e),!Vi&&(Vi=!0,document.addEventListener("paste",Zn)))},yd=e=>{$i(ut,ut.indexOf(e)),ut.length===0&&(document.removeEventListener("paste",Zn),Vi=!1)},_d=()=>{let e=i=>{t.onload(i)},t={destroy:()=>{yd(e)},onload:()=>{}};return xd(e),t},Rd=({root:e,props:t})=>{e.element.id=`filepond--assistant-${t.id}`,se(e.element,"role","alert"),se(e.element,"aria-live","polite"),se(e.element,"aria-relevant","additions")},dn=null,pn=null,Pi=[],gi=(e,t)=>{e.element.textContent=t},wd=e=>{e.element.textContent=""},Kn=(e,t,i)=>{let a=e.query("GET_TOTAL_ITEMS");gi(e,`${i} ${t}, ${a} ${a===1?e.query("GET_LABEL_FILE_COUNT_SINGULAR"):e.query("GET_LABEL_FILE_COUNT_PLURAL")}`),clearTimeout(pn),pn=setTimeout(()=>{wd(e)},1500)},Jn=e=>e.element.parentNode.contains(document.activeElement),Sd=({root:e,action:t})=>{if(!Jn(e))return;e.element.textContent="";let i=e.query("GET_ITEM",t.id);Pi.push(i.filename),clearTimeout(dn),dn=setTimeout(()=>{Kn(e,Pi.join(", "),e.query("GET_LABEL_FILE_ADDED")),Pi.length=0},750)},Ld=({root:e,action:t})=>{if(!Jn(e))return;let i=t.item;Kn(e,i.filename,e.query("GET_LABEL_FILE_REMOVED"))},Ad=({root:e,action:t})=>{let a=e.query("GET_ITEM",t.id).filename,n=e.query("GET_LABEL_FILE_PROCESSING_COMPLETE");gi(e,`${a} ${n}`)},mn=({root:e,action:t})=>{let a=e.query("GET_ITEM",t.id).filename,n=e.query("GET_LABEL_FILE_PROCESSING_ABORTED");gi(e,`${a} ${n}`)},ai=({root:e,action:t})=>{let a=e.query("GET_ITEM",t.id).filename;gi(e,`${t.status.main} ${a} ${t.status.sub}`)},Md=ne({create:Rd,ignoreRect:!0,ignoreRectUpdate:!0,write:ge({DID_LOAD_ITEM:Sd,DID_REMOVE_ITEM:Ld,DID_COMPLETE_ITEM_PROCESSING:Ad,DID_ABORT_ITEM_PROCESSING:mn,DID_REVERT_ITEM_PROCESSING:mn,DID_THROW_ITEM_REMOVE_ERROR:ai,DID_THROW_ITEM_LOAD_ERROR:ai,DID_THROW_ITEM_INVALID:ai,DID_THROW_ITEM_PROCESSING_ERROR:ai}),tag:"span",name:"assistant"}),eo=(e,t="-")=>e.replace(new RegExp(`${t}.`,"g"),i=>i.charAt(1).toUpperCase()),to=(e,t=16,i=!0)=>{let a=Date.now(),n=null;return(...o)=>{clearTimeout(n);let l=Date.now()-a,r=()=>{a=Date.now(),e(...o)};l