diff --git a/README.md b/README.md index c8800fb..eeb9bfb 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,185 @@ -# Nature In Pots – Modular Flask Plant Management App +# 🌿 Nature In Pots β€” Ultra Plant Tracker Platform -This project is a modular, plugin-driven Flask application for tracking plants, their health, lineage, pricing, media, and more. +> Modular, collaborative, end-to-end plant tracking and pricing platform with QR+barcode IDs, propagation logs, resale tools, and role-based management. --- -## πŸ“¦ Core Features +## 🧩 Overview -- πŸ” User Authentication (`auth`) -- 🌱 Plant Identity Profiles (`plant`) -- πŸ“Š Grow Logs with Events (`growlog`) -- πŸ–Ό Media Upload & Attachments (`media`) -- πŸ” Tag-based and name-based Search (`search`) -- πŸ›  CLI Tools for Preloading and Seeding (`cli`) -- 🎨 UI Macros and Shared Layout (`core_ui`) +This is a full-feature, plugin-driven Flask 2+ web application for managing and tracking plant ownership, propagation, pricing, and growth. It supports dynamic plant attributes, collaboration groups, trade logs, QR/barcode labeling, resale workflows, offline sync, moderator tools, and future AI/ML modules. + +Built for hobbyists, businesses, breeders, and community gardens. --- -## 🧱 Plugin System +## πŸš€ Core Features -Each feature is implemented as a plugin under the `plugins/` directory. Each plugin is self-contained and includes: +### 🌱 Plant Profiles +- Add, edit, and log plants with full propagation and ownership history +- Each plant is assigned a permanent, scannable **QR code** and **barcode** +- Plants can be marked as `Public`, `Unlisted`, or `Folder-only` +- All pricing, logs, and lineage are tied to a plant ID and user -- `models.py` -- `routes.py` -- `forms.py` *(if applicable)* -- `templates/` -- `plugin.json` +### 🧾 Grow Logs +- Add logs with images, notes, and growth metrics +- Track success/failure events, mutation events, pest/disease sightings +- Link logs to substrate recipes and fertilizers -Plugins are auto-loaded via `app/__init__.py`. +### πŸ”— Verified Lineage Tracking +- Lineage links are created by the new owner +- Parent plant's owner must **approve** the linkage +- Verified lineage is marked with a badge and shown in plant lineage tree +- Pending links are visible only to the creator + +### πŸ’° Pricing Logic +- Only the current owner and admins can see pricing +- On transfer, original price is retained but hidden from the buyer +- Buyer must submit their own price for tracking resale data +- Admins see full price history; mods and others do not + +### πŸ§ͺ Substrate + Fertilizer Tracking +- Track custom mixes by ingredient (e.g., β€œFine Pumice”, β€œLarge Bark”) +- Store cost per ingredient and auto-calculate total mix cost +- Recipes can be reused across plants +- Fertilizer schedules can be attached to logs and outcomes tracked + +### πŸ“¦ Shipping Tracker +- When a plant is sold, sellers can add: + - Carrier, tracking number, est. delivery date +- Ownership updates post buyer confirmation +- Shipping logs are attached to the plant transfer log + +### πŸ“ Plant Folders +- Organize plants into folders (e.g., β€œFor Sale”, β€œ2025 Spring Batch”) +- Each folder gets its own QR code: + `https://domain.com/{username}/folder/{id}|{slug}` +- Folders can be public, private, or unlisted --- -## πŸ— Installation +## πŸ§β€β™‚οΈ Users, Roles, and Groups -### Prerequisites +### πŸ‘€ User Roles +- **User** – default +- **Moderator** – can manage flags, notes +- **Admin** – full backend, plugin, pricing, banning control +- Roles are extensible via admin UI -- Python 3.11+ -- MySQL server -- Virtualenv or Docker +### πŸ›‘ Moderator Panel +- View and resolve reports +- Add private notes to users or plants (e.g., warnings, suspicion) +- Ban users + - Banned users' plants become read-only + - Cannot add new plants + - Buyers can request transfer via email approval from banned seller -### Setup +### πŸ‘₯ Collaboration Groups +- Users can form groups to share: + - Logs + - Images + - Pricing (opt-in) +- Groups have role-based permissions (manager, editor, viewer) +- Useful for stores, teams, or shared collections + +--- + +## 🏷️ Labeling System: QR + Barcode + +### QR Code +- Generated on server after initial sync +- Unique to plant, never changes +- SVG and PNG available + +### Barcode Fallback +- CODE-128 format +- If data too long, split into stacked barcodes +- Same encoded info: plant ID, owner ID, visibility, timestamp +- Printable as label from dashboard + +--- + +## πŸ” Permissions Matrix + +| Feature | Owner | Group Member | Moderator | Admin | +|--------------------------|-------|---------------|-----------|--------| +| View Logs | βœ… | βœ… (if shared) | βœ… | βœ… | +| Edit Logs | βœ… | πŸ”* | 🚫 | βœ… | +| View Pricing | βœ… | βœ… (opt-in) | 🚫 | βœ… | +| Ban User / Flag Review | 🚫 | 🚫 | βœ… | βœ… | +| Approve Lineage | 🚫 | 🚫 | 🚫 | βœ… | +| Confirm Lineage | βœ… | 🚫 | 🚫 | βœ… | + +πŸ”* if granted by group manager + +--- + +## 🌍 Offline Sync (PWA) +- Full add/log/edit possible offline +- Sync queue uploads on connection +- QR/barcodes generated **after** server confirms sync +- Client-side validation before queue + +--- + +## 🧠 Smart Tools + +### ⭐️ User Reputation System +- Users rated after trades (accuracy, responsiveness, helpfulness) +- β€œTrusted Grower” tag auto-assigned above threshold +- Can be revoked via vote or admin action + +### 🌿 Inter-Plant Comparison +- Timeline comparison for growth, size, or log outcomes +- Side-by-side charts and event overlays + +--- + +## πŸ”§ Admin & Dev Tools + +### πŸ“€ Seed Data Generator +- Seeds with common aroids, herbs, and test users +- Covers full range of roles, plant types, and edge cases + +### πŸ—‚ Plugin System +- CLI & plugin discovery system +- Admin can toggle plugins +- Plugin types: CLI, UI Panel, API extension, webhook, scheduler + +### πŸ—ƒ Data Export / Disaster Recovery +- Export: SQL dump, file archive, JSON profile +- Restore: Admin-initiated rollback or full upload restore + +--- + +## πŸ›£ API System + +### REST & GraphQL APIs +- REST: OpenAPI-documented endpoints +- GraphQL: Advanced multi-entity queries +- JWT-secured +- Follows role-based access rules + +--- + +## 🌐 Internationalization +- Flask-Babel integration +- Language switcher in UI +- Community-managed translation interface (admin toggled) + +--- + +## πŸ“… Future Enhancements + +- 🧠 AI Journal Assistant (log suggestions, summarization) +- πŸ“† Calendar View for logs/reminders +- 🧰 Visual ERD Generator Tool +- πŸ›’ Live Auctions Plugin (for curated resale events) + +--- + +## 🧾 License & Contribution + +This project is part of **Nature In Pots** under **High Thyme Ventures**. + +Please contact the repository owner for collaboration, plugin submission, or access requests. -```bash -make install # Install dependencies -make dev # Start Flask development server -make db # Initialize database -make seed # Seed database with test data diff --git a/app/errors.py b/app/errors.py index e7419fb..177089e 100644 --- a/app/errors.py +++ b/app/errors.py @@ -6,6 +6,10 @@ bp = Blueprint('errors', __name__) def bad_request(error): return render_template('400.html'), 400 +@bp.app_errorhandler(404) +def bad_request(error): + return render_template('404.html'), 404 + @bp.app_errorhandler(500) def internal_error(error): return render_template('500.html'), 500 diff --git a/app/templates/400.html b/app/templates/400.html new file mode 100644 index 0000000..27a931e --- /dev/null +++ b/app/templates/400.html @@ -0,0 +1,12 @@ + + + + + 400 Bad Request + + +

400 – Bad Request

+

{{ e.description or "Sorry, we couldn’t understand that request." }}

+ Return home + + diff --git a/docker-compose.yml b/docker-compose.yml index 055d183..dcca79f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,39 +24,6 @@ services: retries: 3 start_period: 30s - command: > - bash -c " - set -e - - echo '[βœ”] Ensuring .env...' - if [ ! -f '.env' ]; then cp .env.example .env; fi - - echo '[βœ”] Waiting for MySQL to be ready...' - until nc -z ${MYSQL_HOST} ${MYSQL_PORT}; do sleep 1; done - - echo '[βœ”] Ensuring migration structure...' - if [ ! -d 'migrations' ]; then - echo '[β„Ή] Running flask db init...' - flask db init - fi - - echo '[β„Ή] Autogenerating migration...' - flask db migrate -m 'auto' || echo '[β„Ή] No changes detected.' - - echo '[βœ”] Running DB migrations...' - flask db upgrade - - if [ \"$ENABLE_DB_SEEDING\" = \"1\" ]; then - echo '[🌱] Seeding Data...' - flask preload-data - else - echo '[⚠️] DB seeding skipped by config.' - fi - - echo '[πŸš€] Starting Flask server...' - flask run --host=0.0.0.0 - " - db: image: mysql:8 diff --git a/entrypoint.sh b/entrypoint.sh index 9f7c14d..a15e9cb 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -20,11 +20,12 @@ echo "[βœ”] Running migrations" flask db migrate -m "auto" flask db upgrade -# Seed database if enabled -if [ "$ENABLE_DB_SEEDING" = "true" ]; then +# Seed database if enabled (accept β€œ1” or β€œtrue”) +if [ "$ENABLE_DB_SEEDING" = "true" ] || [ "$ENABLE_DB_SEEDING" = "1" ]; then echo "[🌱] Seeding Data" flask preload-data fi + # Start the main process exec "$@" diff --git a/files.zip b/new.zip similarity index 67% rename from files.zip rename to new.zip index cbb41a9..b3f1f81 100644 Binary files a/files.zip and b/new.zip differ diff --git a/plugins/auth/templates/auth/login.html b/plugins/auth/templates/auth/login.html index ef842e8..54bc7c8 100644 --- a/plugins/auth/templates/auth/login.html +++ b/plugins/auth/templates/auth/login.html @@ -1,7 +1,8 @@ {% extends 'core_ui/base.html' %} {% block content %}

Login

-
+ +
diff --git a/plugins/auth/templates/auth/register.html b/plugins/auth/templates/auth/register.html index f5e33a1..4b895bd 100644 --- a/plugins/auth/templates/auth/register.html +++ b/plugins/auth/templates/auth/register.html @@ -3,6 +3,7 @@ {% block content %}

Register

+
diff --git a/plugins/core_ui/routes.py b/plugins/core_ui/routes.py index 19d0c9c..83cd6fb 100644 --- a/plugins/core_ui/routes.py +++ b/plugins/core_ui/routes.py @@ -13,3 +13,7 @@ def admin_dashboard(): if current_user.role != 'admin': return "Access denied", 403 return render_template('core_ui/admin_dashboard.html') + +@bp.route('/health') +def health(): + return 'OK', 200 \ No newline at end of file diff --git a/plugins/plant/models.py b/plugins/plant/models.py index 8b014b4..ec6665e 100644 --- a/plugins/plant/models.py +++ b/plugins/plant/models.py @@ -41,4 +41,15 @@ class Plant(db.Model): updates = db.relationship('PlantUpdate', backref='growlog', lazy=True) lineage = db.relationship('PlantLineage', backref='child', lazy=True, foreign_keys='PlantLineage.child_plant_id') tags = db.relationship('Tag', secondary=plant_tags, backref='plants') - + + # β†’ relationships so we can pull in the actual names: + common_name = db.relationship( + 'PlantCommonName', + backref=db.backref('plants', lazy='dynamic'), + lazy=True + ) + scientific_name = db.relationship( + 'PlantScientificName', + backref=db.backref('plants', lazy='dynamic'), + lazy=True + ) \ No newline at end of file diff --git a/plugins/plant/templates/plant/detail.html b/plugins/plant/templates/plant/detail.html index ef65ffb..6140ce3 100644 --- a/plugins/plant/templates/plant/detail.html +++ b/plugins/plant/templates/plant/detail.html @@ -1,9 +1,37 @@ {% extends 'core_ui/base.html' %} +{% block title %}{{ plant.common_name.name }} – Nature In Pots{% endblock %} + {% block content %} -

{{ plant.name }}

-

Type: {{ plant.type }}

-

{{ plant.notes }}

-

Status: {% if plant.is_active %}Active{% else %}Inactive{% endif %}

-Edit -Back to list +
+
+
+ {{ plant.common_name.name }} +
+
+

{{ plant.common_name.name }}

+ {% if plant.scientific_name %} +

{{ plant.scientific_name.name }}

+ {% endif %} +
+
Date Added
+
{{ plant.date_added.strftime('%Y-%m-%d') }}
+ +
Status
+
+ {{ 'Dead' if plant.is_dead else 'Active' }} +
+
+ + + ← Back to list + + + Edit + +
+
+
{% endblock %} diff --git a/plugins/plant/templates/plant/index.html b/plugins/plant/templates/plant/index.html index c44e423..d042e87 100644 --- a/plugins/plant/templates/plant/index.html +++ b/plugins/plant/templates/plant/index.html @@ -1,10 +1,46 @@ {% extends 'core_ui/base.html' %} +{% block title %}Plant List – Nature In Pots{% endblock %} + {% block content %} -

Plant List

- -Add New Plant +
+

Plant List

+ + {% if plants %} +
+ {% for plant in plants %} +
+
+ + {{ plant.common_name.name if plant.common_name else 'Plant' }} +
+
+ {{ plant.common_name.name if plant.common_name else 'Unnamed' }} +
+ {% if plant.scientific_name %} +

+ {{ plant.scientific_name.name }} +

+ {% endif %} + + View Details + +
+
+
+ {% endfor %} +
+ {% else %} +

No plants found yet. Add one now.

+ {% endif %} + + +
{% endblock %}