diff --git a/client/react/src/App.js b/client/react/src/App.js
index 193099c..5f5846c 100644
--- a/client/react/src/App.js
+++ b/client/react/src/App.js
@@ -54,13 +54,13 @@ const App = () => {
useEffect(() => {
// Fetch plant data from the server
- fetch('https://localhost/plants')
+ fetch('http://127.0.0.1:5000/plants')
.then((response) => response.json())
.then((data) => setPlants(data))
.catch((error) => console.error('Error fetching plant data:', error));
const system = {temperature: 20, humidity: 80};
// Fetch plant data from the server
- fetch('https://localhost/system')
+ fetch('http://localhost:5000/system')
.then((response) => response.json())
.then(() => setSystem(system))
.catch(() => setSystem(system));
diff --git a/client/react/src/forms/NewPlantForm.js b/client/react/src/forms/NewPlantForm.js
index 8103493..fdfd839 100644
--- a/client/react/src/forms/NewPlantForm.js
+++ b/client/react/src/forms/NewPlantForm.js
@@ -10,13 +10,6 @@ import CheckSharpIcon from '@mui/icons-material/CheckSharp';
import CloseSharpIcon from '@mui/icons-material/CloseSharp';
const NewPlantForm = ({ isOpen, onRequestClose, onSave }) => {
- // TODO: Turn to server side
- const typesToGenus = new Map([
- ["Succulent", ["Echeveria"]],
- ["Cactus", ["Old Man"]],
- ["Philodendron", ["Pink Princess", "White Princess"]],
- ["Monstera", ["Albo", "Thai Constelation"]]
- ])
const stages = [
"Leaf",
@@ -25,28 +18,28 @@ const NewPlantForm = ({ isOpen, onRequestClose, onSave }) => {
"Senior"
];
-
useEffect(() => {
// Fetch plant data from the server
- fetch('https://localhost/types')
+ fetch('http://127.0.0.1:5000/species')
.then((response) => response.json())
- .then((data) => setTypes(data? Array.from(typesToGenus.keys()) : Array.from(typesToGenus.keys())))
+ .then((data) => setAllSpecies(data))
.catch((error) => console.error('Error fetching plant data:', error));
}, []);
const [name, setName] = useState('');
- const [type, setType] = useState('Succulent');
- const [genus, setGenus] = useState('Echeveria');
- const [stage, setStage] = useState('Senior')
- const [wateringFrequency, setWateringFrequency] = useState(14);
- const [submitted, setSubmit] = useState(false);
+ const [stage, setStage] = useState('Senior');
+ const [size, setSize] = useState(0);
+ const [cost, setCost] = useState(0);
- const [types, setTypes] = useState(Array.from(typesToGenus.keys()))
+ const [allSpecies, setAllSpecies] = useState([]);
+
+ const [submitted, setSubmit] = useState(false);
const handleSubmit = (event) => {
setSubmit(true);
event.preventDefault();
- onSave({ name, type, wateringFrequency, genus, stage });
+ console.log(allSpecies);
+ onSave({ name, stage });
clearForm();
onRequestClose();
};
@@ -58,9 +51,6 @@ const NewPlantForm = ({ isOpen, onRequestClose, onSave }) => {
const clearForm = () => {
setName('');
- setType('Succulent');
- setGenus('Echeveria');
- setWateringFrequency('');
};
return (
@@ -100,34 +90,6 @@ const NewPlantForm = ({ isOpen, onRequestClose, onSave }) => {
variant="standard"
onChange={(event) => setName(event.target.value)}
/>
- setType(event.target.value)}
- variant="standard"
- >
- {types.map((ty) => (
-
- ))}
-
- setGenus(event.target.value)}
- variant="standard"
- >
- {typesToGenus.get(type).map((gen) => (
-
- ))}
-
{
fullWidth
required
type="number"
- label="Watering Frequency"
- value={wateringFrequency}
- onChange={(event) => setWateringFrequency(event.target.value)}
+ label="Size (inches)"
+ value={size}
+ onChange={(event) => setSize(event.target.value)}
+ variant="standard"
+ />
+ setCost(event.target.value)}
variant="standard"
/>
diff --git a/server/app.py b/server/app.py
index 81131f6..e3740b7 100644
--- a/server/app.py
+++ b/server/app.py
@@ -1,13 +1,15 @@
-from colorama import init, Fore
+# from colorama import init, Fore
from flask import Flask, request, jsonify
+from flask_cors import CORS
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.engine import URL
import json
import logging
+from models.plant import Plant, Base, Species, Genus # Importing the Plant model
# Initialize Colorama
-init(autoreset=True)
+# init(autoreset=True)
# Load database configuration from JSON file
with open("db.json") as json_data_file:
@@ -23,10 +25,15 @@
port=db_config["port"],
)
engine = create_engine(url)
+#TODO: Temp, remove when all is working
+Base.metadata.drop_all(engine)
+Base.metadata.create_all(engine)
+
Session = sessionmaker(bind=engine)
# Create Flask app
app = Flask(__name__)
+CORS(app)
# Configure logging
logging.basicConfig(level=logging.INFO)
@@ -34,35 +41,95 @@
@app.route("/plants", methods=["POST"])
def add_plant():
+ logger.info("Attemping create plant")
# Get JSON data from request
- new_plant = request.get_json()
- # Log the request
- logger.info("Received request to add a new plant: %s", new_plant)
- # Perform database operations here
- # Example: Add new plant to the database
- # session = Session()
- # session.add(new_plant)
- # session.commit()
- # session.close()
+ new_plant_data = request.get_json()
+ # Create a new Plant object
+ new_plant = Plant(
+ cost=new_plant_data["cost"],
+ size=new_plant_data["size"]
+ )
+ # Add the new plant object to the session
+ session = Session()
+ session.add(new_plant)
+ session.commit()
+ session.close()
# Return response
- return jsonify(new_plant), 201
+ return jsonify({"message": "Plant added successfully"}), 201
# Example route to get all plants
@app.route("/plants", methods=["GET"])
def get_plants():
# Log the request
logger.info("Received request to retrieve all plants")
- print("HELLO")
- # Perform database query to retrieve all plants
- # Example: Query all plants from the database
- # session = Session()
- # plants = session.query(Plant).all()
- # session.close()
+ session = Session()
+ plants = session.query(Plant).all()
+ session.close()
# Transform plants to JSON format
- # Example: Convert plants to JSON format
- # plants_json = [plant.to_json() for plant in plants]
+ plants_json = [{"id": plant.id, "cost": plant.cost, "size": plant.size, "watering": plant.watering} for plant in plants]
+ # Return JSON response
+ return jsonify(plants_json)
+
+@app.route("/species", methods=["POST"])
+def add_species():
+ logger.info("Attempting create species")
+
+ new_species_data = request.get_json()
+
+ genus_id = new_species_data["genus_id"]
+
+ # I'm electing not to have a dedicated endpoint to creating genuses... sue me
+ # We can just embed it on the species object for now
+ new_genus = None
+ if not genus_id:
+ if not new_species_data["genus"]:
+ return jsonify({"message": "Did not specify genus when creating species"}), 400
+
+ new_genus_data = new_species_data["genus"]
+ new_genus = Genus(
+ name=new_genus_data["name"],
+ watering=new_genus_data["watering"]
+ )
+
+ # Create a new Type object
+ new_species = Species(
+ name=new_species_data["name"],
+ genus_id=genus_id
+ )
+
+ # Add the new species (and genus if applicable) object to the session
+ session = Session()
+ if new_genus:
+ session.add(new_genus)
+ session.add(new_type)
+ session.commit()
+ session.close()
+
+ return jsonify({"message": "Species added successfully", "id": new_species.id}), 201
+
+@app.route("/species", methods=["GET"])
+def get_species():
+ logger.info("Received request to retrieve all plant species")
+
+ session = Session()
+ species = session.query(Species).all()
+ session.close()
+ # Transform species to JSON format
+ species_json = [{"id": _species.id, "name": _species.name} for _species in species]
+ # Return JSON response
+ return jsonify(species_json)
+
+@app.route("/genus", methods=["GET"])
+def get_genuses():
+ logger.info("Received request to retrieve all plant genuses")
+
+ session = Session()
+ genuses = session.query(Genus).all()
+ session.close()
+ # Transform genuses to JSON format
+ genuses_json = [{"id": genus.id, "name": genus.name, "watering": genus.watering} for genus in genuses]
# Return JSON response
- return jsonify([]) # Placeholder response
+ return jsonify(genuses_json)
if __name__ == "__main__":
# Run the Flask app
diff --git a/server/models/plant.py b/server/models/plant.py
index 54befe9..bc26044 100644
--- a/server/models/plant.py
+++ b/server/models/plant.py
@@ -9,17 +9,17 @@
Base = declarative_base()
# class DeathCause(Enum, str):
- # TOO_LITTLE_WATER = "too little water"
- # TOO_MUCH_WATER = "too much water"
- # TOO_LITTLE_HUMIDITY = "too little humidity"
- # TOO_MUCH_HUMIDITY = "too much humidity"
- # TOO_LITTLE_SUN = "too little sun"
- # TOO_MUCH_SUN = "too much sun"
- # PROPAGATION = "propagation"
- # PESTS = "pests"
- # MOLD = "mold"
- # NEGLECT = "neglect"
- # UNKNOWN = "unknown"
+# TOO_LITTLE_WATER = "too little water"
+# TOO_MUCH_WATER = "too much water"
+# TOO_LITTLE_HUMIDITY = "too little humidity"
+# TOO_MUCH_HUMIDITY = "too much humidity"
+# TOO_LITTLE_SUN = "too little sun"
+# TOO_MUCH_SUN = "too much sun"
+# PROPAGATION = "propagation"
+# PESTS = "pests"
+# MOLD = "mold"
+# NEGLECT = "neglect"
+# UNKNOWN = "unknown"
class Plant(Base):
@@ -30,67 +30,50 @@ class Plant(Base):
# Created at specs
id = Column(Integer(), primary_key=True)
created_on = Column(DateTime(), default=datetime.now)
- cost = Column(Integer())
- size = Column(Integer()) # inches
- type_id: Mapped[int] = mapped_column(ForeignKey("type.id")) # Type of Plant
+ cost = Column(Integer(), default=0, nullable=False)
+ size = Column(Integer(), default=0, nullable=False) # inches
+ species_id: Mapped[int] = mapped_column(ForeignKey("species.id", ondelete='CASCADE')) # Species of Plant
updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now)
- # Batch
- batch_id: Mapped[int] = mapped_column(ForeignKey("batch.id"))
- batch: Mapped["Batch"] = relationship(back_populates="plants")
-
# Water Info
watered_on = Column(DateTime(), default=datetime.now)
- watering = Column(Integer()) # days
# Death Info
dead = Column(Boolean, default=False, nullable=False)
# dead_cause = Column(Enum(DeathCause), nullable=True)
dead_on = Column(DateTime(), default=None, nullable=True)
- def __repr__(self) -> str:
- return f"{self.name} ({self.type}/{self.genus})"
-
-
-class Type(Base):
- """Type of genus."""
+class Species(Base):
+ """Species of genus."""
- __tablename__ = "type"
+ __tablename__ = "species"
id = Column(Integer(), primary_key=True)
created_on = Column(DateTime(), default=datetime.now)
- name = Column(String(100), nullable=False)
+ name = Column(String(100), nullable=False, unique=True)
- plants: Mapped[List["Plant"]] = relationship()
+ # Available plants of this species
+ plants: Mapped[List["Plant"]] = relationship('Plant', backref='plant', passive_deletes=True)
- # TODO: Best lighting and soil mixes
+ # Genus of this species of plant
+ genus_id: Mapped[int] = mapped_column(ForeignKey("genus.id", ondelete='CASCADE'))
def __repr__(self) -> str:
return f"{self.name}"
-
class Genus(Base):
- """Genus of species."""
+ """Genus of plant."""
__tablename__ = "genus"
id = Column(Integer(), primary_key=True)
created_on = Column(DateTime(), default=datetime.now)
name = Column(String(100), nullable=False, unique=True)
+ watering = Column(Integer()) # days
- def __repr__(self) -> str:
- return f"{self.name}"
-
-
-class Species(Base):
- """Species of plants."""
-
- __tablename__ = "species"
-
- id = Column(Integer(), primary_key=True)
- created_on = Column(DateTime(), default=datetime.now)
- name = Column(String(100), nullable=False, unique=True)
+ # Available species of this genus
+ species: Mapped[List["Species"]] = relationship('Species', backref='species', passive_deletes=True)
def __repr__(self) -> str:
return f"{self.name}"
diff --git a/server/requirements.txt b/server/requirements.txt
index 8d07afe..8e4f1d3 100644
--- a/server/requirements.txt
+++ b/server/requirements.txt
@@ -1,11 +1,19 @@
blinker==1.7.0
click==8.1.7
Flask==3.0.1
-Flask-Cors==4.0.0
+Flask-Cors==4.0.1
importlib-metadata==7.0.1
itsdangerous==2.1.2
Jinja2==3.1.3
MarkupSafe==2.1.4
+numpy==1.26.4
+pandas==2.2.2
+psycopg2-binary==2.9.9
+python-dateutil==2.9.0.post0
+pytz==2024.1
+six==1.16.0
+SQLAlchemy==2.0.30
+typing_extensions==4.12.0
+tzdata==2024.1
Werkzeug==3.0.1
zipp==3.17.0
-pandas==2.2.2