---
title: "Plant Care"
description: "Houseplant care API that turns soil moisture and watering history into care advice via computed columns"
url: "https://www.pixeltable.com/recipes/plant-care"
source: "https://github.com/pierrebrunelle/pixeltable-plant-care"
---

# Plant Care

Houseplant care API that turns soil moisture and watering history into care advice via computed columns

One `app.py` with 1 table, 3 computed columns, 4 HTTP routes. Needs `pixeltable[serve]>=0.7.14`.

## Tables

### plants (table)

| Column | Kind | Type or expression |
| --- | --- | --- |
| id | primary key | `pxtf.uuid.uuid7()` |
| species | stored | String |
| location | stored | String |
| moisture | stored | String (optional) |
| days_since_water | stored | Int (optional) |
| notes | stored | String (optional) |
| water_label | computed on insert | `water_advice(moisture, days_since_water)` |
| species_upper | computed on insert | `pxtf.string.upper(species)` |
| notes_blurb | computed on insert | `care_blurb(species, location)` |

## Routes (service `plants_api`)

- `POST /plants`: inserts into `plants` (takes species, location, moisture, days_since_water, notes; returns id, water_label, species_upper, notes_blurb)
- `POST /plants/update`: updates `plants` (takes moisture, days_since_water; returns id, water_label)
- `POST /water-label`: computes `plants` columns without storing (takes moisture, days_since_water; returns water_label)
- `GET /plants/thirsty`: runs the query `thirsty`

## Run it locally

```sh
git clone https://github.com/pierrebrunelle/pixeltable-plant-care
cd pixeltable-plant-care
uv sync
uv run pxt schema update app.py plants
uv run python seed.py plants
uv run pxt service run app.py plants --port 8000
```

## Run it on Pixeltable Cloud

```sh
uv run pxt new    # a free database, no account
# put the pxt:// address it prints in pixeltable.toml
uv run pxt db update pxt://<org>:main -f
uv run pxt schema update app.py pxt://<org>:main -f
uv run pxt service update app.py pxt://<org>:main -f
```

## app.py

At commit `08d450c3` (https://github.com/pierrebrunelle/pixeltable-plant-care/blob/08d450c3385a352afb69ca348ea6fceed9a9928d/app.py):

```python
"""Plant Care API built with Pixeltable.

    pxt schema update app.py plants
    pxt service run app.py plants
"""
import pixeltable as pxt
import pixeltable.functions as pxtf
from pixeltable.serving import FastAPIRouter

from udfs import care_blurb, water_advice

# ---- tables ----
TableModel = pxt.model_base()


class Plants(TableModel, name='plants'):
    id = pxt.Column(value=pxtf.uuid.uuid7(), primary_key=True)
    species: pxt.String
    location: pxt.String
    moisture: pxt.String | None
    days_since_water: pxt.Int | None
    notes: pxt.String | None

    water_label = water_advice(moisture, days_since_water)
    species_upper = pxtf.string.upper(species)
    notes_blurb = care_blurb(species, location)


# ---- queries ----
@pxt.query
def thirsty():
    """Plants whose computed advice is 'water today'."""
    return Plants.where(Plants.water_label == 'water today').select(
        Plants.id, Plants.notes_blurb, Plants.days_since_water
    ).order_by(Plants.days_since_water, asc=False)


# ---- routes ----
plants_api = FastAPIRouter(name='plants_api')
plants_api.add_insert_route(
    Plants, path='/plants',
    inputs=[Plants.species, Plants.location, Plants.moisture, Plants.days_since_water, Plants.notes],
    outputs=[Plants.id, Plants.water_label, Plants.species_upper, Plants.notes_blurb],
)
plants_api.add_update_route(Plants, path='/plants/update', inputs=[Plants.moisture, Plants.days_since_water],
                            outputs=[Plants.id, Plants.water_label])
plants_api.add_compute_route(Plants, path='/water-label', inputs=[Plants.moisture, Plants.days_since_water],
                             outputs=[Plants.water_label])
plants_api.add_query_route(path='/plants/thirsty', query=thirsty, method='get')
```
