Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server modeling #7

Merged
merged 7 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/react/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
74 changes: 23 additions & 51 deletions client/react/src/forms/NewPlantForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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();
};
Expand All @@ -58,9 +51,6 @@ const NewPlantForm = ({ isOpen, onRequestClose, onSave }) => {

const clearForm = () => {
setName('');
setType('Succulent');
setGenus('Echeveria');
setWateringFrequency('');
};

return (
Expand Down Expand Up @@ -100,34 +90,6 @@ const NewPlantForm = ({ isOpen, onRequestClose, onSave }) => {
variant="standard"
onChange={(event) => setName(event.target.value)}
/>
<TextField
margin="normal"
fullWidth
required
select
label="Type"
value={type}
onChange={(event) => setType(event.target.value)}
variant="standard"
>
{types.map((ty) => (
<MenuItem key={ty} value={ty}>{ty}</MenuItem>
))}
</TextField>
<TextField
margin="normal"
fullWidth
required
select
label="Genus"
value={genus}
onChange={(event) => setGenus(event.target.value)}
variant="standard"
>
{typesToGenus.get(type).map((gen) => (
<MenuItem key={gen} value={gen}>{gen}</MenuItem>
))}
</TextField>
<TextField
margin="normal"
fullWidth
Expand All @@ -147,9 +109,19 @@ const NewPlantForm = ({ isOpen, onRequestClose, onSave }) => {
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"
/>
<TextField
margin="normal"
fullWidth
required
type="number"
label="Cost"
value={cost}
onChange={(event) => setCost(event.target.value)}
variant="standard"
/>
</div>
Expand Down
109 changes: 88 additions & 21 deletions server/app.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -23,46 +25,111 @@
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)
logger = logging.getLogger(__name__)

@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
Expand Down
69 changes: 26 additions & 43 deletions server/models/plant.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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}"
Loading
Loading