From 3a4e7bf41be05fe18cb4584e397ae3faef013e64 Mon Sep 17 00:00:00 2001 From: Kobe Date: Sun, 8 Jun 2025 17:07:31 +0200 Subject: [PATCH] sections! --- app.py | 142 ++++++++++++++++-- ..._add_section_model_and_relationship_to_.py | 34 +++++ templates/admin_base.html | 9 ++ templates/create_plant.html | 4 +- templates/edit_care_difficulty.html | 3 +- templates/edit_climate.html | 8 +- templates/edit_environment.html | 5 +- templates/edit_growth_rate.html | 8 +- templates/edit_light.html | 8 +- templates/edit_plant.html | 9 +- templates/edit_product.html | 5 +- templates/edit_section.html | 34 +++++ templates/edit_size.html | 8 +- templates/edit_toxicity.html | 8 +- templates/manage_care_difficulties.html | 3 +- templates/manage_climates.html | 3 +- templates/manage_environments.html | 3 +- templates/manage_growth_rates.html | 3 +- templates/manage_lights.html | 3 +- templates/manage_plants.html | 4 +- templates/manage_sections.html | 60 ++++++++ templates/manage_sizes.html | 3 +- templates/manage_toxicities.html | 3 +- 23 files changed, 322 insertions(+), 48 deletions(-) create mode 100644 migrations/versions/f746ca4a01f7_add_section_model_and_relationship_to_.py create mode 100644 templates/edit_section.html create mode 100644 templates/manage_sections.html diff --git a/app.py b/app.py index 30d5f06..7c8f4c6 100644 --- a/app.py +++ b/app.py @@ -53,6 +53,15 @@ class Product(db.Model): def __repr__(self): return f"Product('{self.name}')" +class Section(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(50), nullable=False, unique=True) + description = db.Column(db.Text, nullable=True) + icon = db.Column(db.String(200), nullable=True) + + def __repr__(self): + return f"Section('{self.name}')" + class Plant(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) @@ -64,6 +73,7 @@ class Plant(db.Model): size_id = db.Column(db.Integer, db.ForeignKey('size.id'), nullable=True) care_difficulty_id = db.Column(db.Integer, db.ForeignKey('care_difficulty.id'), nullable=True) growth_rate_id = db.Column(db.Integer, db.ForeignKey('growth_rate.id'), nullable=True) + section_id = db.Column(db.Integer, db.ForeignKey('section.id'), nullable=True) products = db.Column(db.Text, nullable=True) # Will store product IDs as comma-separated values description = db.Column(db.Text, nullable=True) date_added = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) @@ -76,6 +86,7 @@ class Plant(db.Model): size = db.relationship('Size', backref='plant_size') care_difficulty = db.relationship('CareDifficulty', backref='plant_care_difficulty') growth_rate = db.relationship('GrowthRate', backref='plant_growth_rate') + section = db.relationship('Section', backref='plants') def __repr__(self): return f"Plant('{self.name}', '{self.date_added}')" @@ -140,13 +151,15 @@ def inject_admin_counts(): climate_count = Climate.query.count() product_count = Product.query.count() plant_count = Plant.query.count() + section_count = Section.query.count() except Exception: - environment_count = climate_count = product_count = plant_count = 0 + environment_count = climate_count = product_count = plant_count = section_count = 0 return dict( environment_count=environment_count, climate_count=climate_count, product_count=product_count, - plant_count=plant_count + plant_count=plant_count, + section_count=section_count ) def none_if_empty(val): @@ -1126,6 +1139,91 @@ def delete_growth_rate(rate_id): flash('Growth rate category deleted successfully!', 'success') return redirect(url_for('manage_growth_rates')) +# Section management routes +@app.route('/admin/sections', methods=['GET', 'POST']) +def manage_sections(): + if not is_logged_in(): + return redirect(url_for('login')) + if request.method == 'POST': + name = request.form['name'] + description = request.form['description'] + icon_file = request.files.get('icon') + icon_filename = None + if not name: + flash('Name is required!', 'danger') + print('No name provided') + return redirect(url_for('manage_sections')) + if Section.query.filter_by(name=name).first(): + flash('Section with this name already exists!', 'danger') + print('Duplicate section name') + return redirect(url_for('manage_sections')) + if icon_file and icon_file.filename: + icon_filename = secure_filename(icon_file.filename) + icon_path = os.path.join('static/icons', icon_filename) + os.makedirs('static/icons', exist_ok=True) + try: + icon_file.save(icon_path) + except Exception as e: + print('Error saving icon:', e) + flash(f'Error saving icon: {e}', 'danger') + try: + section = Section(name=name, description=description, icon=icon_filename) + db.session.add(section) + db.session.commit() + flash('Section added successfully!', 'success') + print('Section added:', name) + except Exception as e: + db.session.rollback() + print('Error adding section:', e) + flash(f'Error adding section: {e}', 'danger') + return redirect(url_for('manage_sections')) + sections = Section.query.all() + return render_template('manage_sections.html', + sections=sections, + plant_count=len(Plant.query.all()), + climate_count=len(Climate.query.all()), + environment_count=len(Environment.query.all()), + product_count=len(Product.query.all()), + light_count=len(Light.query.all()), + toxicity_count=len(Toxicity.query.all()), + size_count=len(Size.query.all()), + difficulty_count=len(CareDifficulty.query.all()), + rate_count=len(GrowthRate.query.all()), + section_count=len(sections)) + +@app.route('/admin/sections/edit/', methods=['GET', 'POST']) +def edit_section(section_id): + if not is_logged_in(): + return redirect(url_for('login')) + section = Section.query.get_or_404(section_id) + if request.method == 'POST': + section.name = request.form['name'] + section.description = request.form['description'] + icon_file = request.files.get('icon') + if icon_file and icon_file.filename: + icon_filename = secure_filename(icon_file.filename) + icon_path = os.path.join('static/icons', icon_filename) + os.makedirs('static/icons', exist_ok=True) + try: + icon_file.save(icon_path) + except Exception as e: + print('Error saving icon:', e) + section.icon = icon_filename + db.session.commit() + flash('Section updated successfully!', 'success') + return redirect(url_for('manage_sections')) + return render_template('edit_section.html', section=section) + +@app.route('/admin/sections/delete/', methods=['POST']) +def delete_section(section_id): + if not is_logged_in(): + return redirect(url_for('login')) + section = Section.query.get_or_404(section_id) + db.session.delete(section) + db.session.commit() + flash('Section deleted successfully!', 'success') + return redirect(url_for('manage_sections')) + def seed_db(): # Environments environments = [ @@ -1137,6 +1235,18 @@ def seed_db(): if not Environment.query.filter_by(name=env['name']).first(): db.session.add(Environment(name=env['name'], description=env['description'])) + # Sections + sections = [ + {'name': 'Succulents', 'description': 'Plants that store water in their leaves, stems, or roots.'}, + {'name': 'Tropical', 'description': 'Plants native to tropical regions.'}, + {'name': 'Herbs', 'description': 'Plants used for culinary, medicinal, or aromatic purposes.'}, + {'name': 'Flowering', 'description': 'Plants grown primarily for their flowers.'}, + {'name': 'Foliage', 'description': 'Plants grown primarily for their attractive leaves.'} + ] + for sec in sections: + if not Section.query.filter_by(name=sec['name']).first(): + db.session.add(Section(name=sec['name'], description=sec['description'])) + # Climates climates = [ {'name': 'Tropical', 'description': 'Warm and humid year-round.'}, @@ -1233,57 +1343,64 @@ def seed_db(): medium = Size.query.filter_by(name='Medium').first() easy = CareDifficulty.query.filter_by(name='Easy').first() moderate = GrowthRate.query.filter_by(name='Moderate').first() + + # Get section instances + succulents = Section.query.filter_by(name='Succulents').first() + tropical_section = Section.query.filter_by(name='Tropical').first() + herbs = Section.query.filter_by(name='Herbs').first() + flowering = Section.query.filter_by(name='Flowering').first() + foliage = Section.query.filter_by(name='Foliage').first() # 10 example plants seed_plants = [ dict(name='Monstera Deliciosa', climate=tropical, environment=indoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=easy, growth_rate=moderate, + care_difficulty=easy, growth_rate=moderate, section=tropical_section, products=[soil, fert], description='A popular tropical houseplant with large, split leaves.', care_guide='
  • Water when the top 2 inches of soil are dry.
  • Provide bright, indirect light.
  • Fertilize monthly during growing season.
'), dict(name='Aloe Vera', climate=arid, environment=indoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=easy, growth_rate=moderate, + care_difficulty=easy, growth_rate=moderate, section=succulents, products=[soil], description='A succulent known for its medicinal properties.', care_guide='
  • Allow soil to dry completely between waterings.
  • Place in bright, indirect sunlight.
  • Use well-draining soil.
'), dict(name='Fiddle Leaf Fig', climate=tropical, environment=indoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=moderate, growth_rate=moderate, + care_difficulty=moderate, growth_rate=moderate, section=foliage, products=[soil, fert, can], description='A trendy houseplant with large, violin-shaped leaves.', care_guide='
  • Keep soil consistently moist but not soggy.
  • Needs bright, filtered light.
  • Rotate plant for even growth.
'), dict(name='Snake Plant', climate=arid, environment=indoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=easy, growth_rate=moderate, + care_difficulty=easy, growth_rate=moderate, section=foliage, products=[soil, can], description='A hardy plant that tolerates low light and irregular watering.', care_guide='
  • Water sparingly; let soil dry out between waterings.
  • Tolerates low to bright light.
  • Wipe leaves to remove dust.
'), dict(name='Spider Plant', climate=temperate, environment=indoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=easy, growth_rate=moderate, + care_difficulty=easy, growth_rate=moderate, section=foliage, products=[soil, can], description='An easy-care plant with arching leaves and baby plantlets.', care_guide='
  • Keep soil slightly moist.
  • Thrives in bright, indirect light.
  • Trim brown tips as needed.
'), dict(name='Peace Lily', climate=tropical, environment=indoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=easy, growth_rate=moderate, + care_difficulty=easy, growth_rate=moderate, section=flowering, products=[soil, fert, can], description='A flowering plant that thrives in shade and purifies air.', care_guide='
  • Water when leaves droop slightly.
  • Prefers low to medium light.
  • Remove spent flowers to encourage new blooms.
'), dict(name='Jade Plant', climate=arid, environment=indoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=easy, growth_rate=moderate, + care_difficulty=easy, growth_rate=moderate, section=succulents, products=[soil], description='A succulent with thick, shiny leaves and a tree-like form.', care_guide='
  • Let soil dry between waterings.
  • Needs several hours of direct sunlight.
  • Prune to maintain shape.
'), dict(name='Tomato', climate=temperate, environment=outdoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=moderate, growth_rate=moderate, + care_difficulty=moderate, growth_rate=moderate, section=herbs, products=[soil, fert, can], description='A classic edible plant for outdoor gardens.', care_guide='
  • Water regularly, keeping soil consistently moist.
  • Provide full sun.
  • Support with stakes or cages.
'), dict(name='Basil', climate=mediterranean, environment=greenhouse, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=easy, growth_rate=moderate, + care_difficulty=easy, growth_rate=moderate, section=herbs, products=[soil, fert, light], description='A fragrant herb often grown in greenhouses or sunny windows.', care_guide='
  • Water when soil surface is dry.
  • Needs at least 6 hours of sunlight daily.
  • Pinch off flowers to encourage leaf growth.
'), dict(name='Cactus', climate=arid, environment=indoor, light=bright_indirect, toxicity=non_toxic, size=medium, - care_difficulty=easy, growth_rate=moderate, + care_difficulty=easy, growth_rate=moderate, section=succulents, products=[soil, light], description='A spiny plant adapted to dry, sunny conditions.', care_guide='
  • Water sparingly, especially in winter.
  • Place in bright, direct sunlight.
  • Use cactus-specific soil mix.
'), ] @@ -1297,6 +1414,7 @@ def seed_db(): size_id=plant['size'].id if plant['size'] else None, care_difficulty_id=plant['care_difficulty'].id if plant['care_difficulty'] else None, growth_rate_id=plant['growth_rate'].id if plant['growth_rate'] else None, + section_id=plant['section'].id if plant['section'] else None, products=','.join(str(p.id) for p in plant['products'] if p), description=plant['description'], care_guide=plant['care_guide'] diff --git a/migrations/versions/f746ca4a01f7_add_section_model_and_relationship_to_.py b/migrations/versions/f746ca4a01f7_add_section_model_and_relationship_to_.py new file mode 100644 index 0000000..ef1caf6 --- /dev/null +++ b/migrations/versions/f746ca4a01f7_add_section_model_and_relationship_to_.py @@ -0,0 +1,34 @@ +"""Add Section model and relationship to Plant + +Revision ID: f746ca4a01f7 +Revises: f83d55cc5aa7 +Create Date: 2025-06-08 17:00:55.885897 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f746ca4a01f7' +down_revision = 'f83d55cc5aa7' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('plant', schema=None) as batch_op: + batch_op.add_column(sa.Column('section_id', sa.Integer(), nullable=True)) + batch_op.create_foreign_key(None, 'section', ['section_id'], ['id']) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('plant', schema=None) as batch_op: + batch_op.drop_constraint(None, type_='foreignkey') + batch_op.drop_column('section_id') + + # ### end Alembic commands ### diff --git a/templates/admin_base.html b/templates/admin_base.html index 11fca9d..6840ff8 100644 --- a/templates/admin_base.html +++ b/templates/admin_base.html @@ -95,6 +95,15 @@ {{ product_count }} + +
+ + + + Sections +
+ {{ section_count }} +
diff --git a/templates/create_plant.html b/templates/create_plant.html index 4f0f673..3324ab2 100644 --- a/templates/create_plant.html +++ b/templates/create_plant.html @@ -14,8 +14,8 @@
- +
diff --git a/templates/edit_care_difficulty.html b/templates/edit_care_difficulty.html index 4cbc603..2eaaa95 100644 --- a/templates/edit_care_difficulty.html +++ b/templates/edit_care_difficulty.html @@ -18,7 +18,8 @@
- + {% if difficulty.icon %}
Current icon: diff --git a/templates/edit_climate.html b/templates/edit_climate.html index 98d18fb..f5903cc 100644 --- a/templates/edit_climate.html +++ b/templates/edit_climate.html @@ -18,15 +18,13 @@
- + {% if climate.icon %} Icon {% endif %}
- + Cancel
diff --git a/templates/edit_environment.html b/templates/edit_environment.html index 3571be5..9ef5ed6 100644 --- a/templates/edit_environment.html +++ b/templates/edit_environment.html @@ -23,10 +23,7 @@ Icon {% endif %}
- + Cancel diff --git a/templates/edit_growth_rate.html b/templates/edit_growth_rate.html index 6910918..3290de4 100644 --- a/templates/edit_growth_rate.html +++ b/templates/edit_growth_rate.html @@ -18,7 +18,8 @@
- + {% if rate.icon %}
Current icon: @@ -26,7 +27,10 @@
{% endif %}
- + {% endblock %} \ No newline at end of file diff --git a/templates/edit_light.html b/templates/edit_light.html index 76a6f41..942d64a 100644 --- a/templates/edit_light.html +++ b/templates/edit_light.html @@ -18,7 +18,8 @@
- + {% if light.icon %}
Current icon: @@ -26,7 +27,10 @@
{% endif %}
- + {% endblock %} \ No newline at end of file diff --git a/templates/edit_plant.html b/templates/edit_plant.html index 848e64d..d3609b5 100644 --- a/templates/edit_plant.html +++ b/templates/edit_plant.html @@ -14,8 +14,8 @@
- + {% if plant.picture %} {{ plant.name }} {% endif %} @@ -108,7 +108,10 @@
{{ plant.care_guide|safe }}
- + diff --git a/templates/edit_product.html b/templates/edit_product.html index ca53f9b..c84bcd6 100644 --- a/templates/edit_product.html +++ b/templates/edit_product.html @@ -16,10 +16,7 @@ - + Cancel diff --git a/templates/edit_section.html b/templates/edit_section.html new file mode 100644 index 0000000..3cc52d7 --- /dev/null +++ b/templates/edit_section.html @@ -0,0 +1,34 @@ +{% extends "admin_base.html" %} + +{% block title %}Edit Section{% endblock %} + +{% block admin_content %} +
+

Edit Section

+
+
+ + +
+
+ + +
+
+ + {% if section.icon %} +
+ {{ section.name }} +
+ {% endif %} + +

Leave empty to keep the current icon

+
+ + Cancel +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/edit_size.html b/templates/edit_size.html index e665b9d..0cad466 100644 --- a/templates/edit_size.html +++ b/templates/edit_size.html @@ -18,7 +18,8 @@
- + {% if size.icon %}
Current icon: @@ -26,7 +27,10 @@
{% endif %}
- + {% endblock %} \ No newline at end of file diff --git a/templates/edit_toxicity.html b/templates/edit_toxicity.html index 8cff5b9..93383bd 100644 --- a/templates/edit_toxicity.html +++ b/templates/edit_toxicity.html @@ -18,7 +18,8 @@
- + {% if toxicity.icon %}
Current icon: @@ -26,7 +27,10 @@
{% endif %}
- + {% endblock %} \ No newline at end of file diff --git a/templates/manage_care_difficulties.html b/templates/manage_care_difficulties.html index c50dbc3..9b37d52 100644 --- a/templates/manage_care_difficulties.html +++ b/templates/manage_care_difficulties.html @@ -20,7 +20,8 @@
- +
diff --git a/templates/manage_climates.html b/templates/manage_climates.html index 51054da..47b02e9 100644 --- a/templates/manage_climates.html +++ b/templates/manage_climates.html @@ -20,7 +20,8 @@
- +
diff --git a/templates/manage_environments.html b/templates/manage_environments.html index 179e456..afed3e7 100644 --- a/templates/manage_environments.html +++ b/templates/manage_environments.html @@ -20,7 +20,8 @@
- +
diff --git a/templates/manage_growth_rates.html b/templates/manage_growth_rates.html index 504d5b0..6f9c01e 100644 --- a/templates/manage_growth_rates.html +++ b/templates/manage_growth_rates.html @@ -20,7 +20,8 @@
- +
diff --git a/templates/manage_lights.html b/templates/manage_lights.html index 528e750..3f680e9 100644 --- a/templates/manage_lights.html +++ b/templates/manage_lights.html @@ -20,7 +20,8 @@
- +
diff --git a/templates/manage_plants.html b/templates/manage_plants.html index 86694d0..a9f31ad 100644 --- a/templates/manage_plants.html +++ b/templates/manage_plants.html @@ -23,8 +23,8 @@
- +
diff --git a/templates/manage_sections.html b/templates/manage_sections.html new file mode 100644 index 0000000..9e8364e --- /dev/null +++ b/templates/manage_sections.html @@ -0,0 +1,60 @@ +{% extends "admin_base.html" %} + +{% block title %}Manage Sections{% endblock %} + +{% block admin_content %} +
+ +
+

Add New Section

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ + +
+

Existing Sections

+
+ {% for section in sections %} +
+
+ {% if section.icon %} + {{ section.name }} + {% endif %} +
+

{{ section.name }}

+ {% if section.description %} +

{{ section.description }}

+ {% endif %} +
+
+
+ Edit +
+ +
+
+
+ {% else %} +

No sections added yet.

+ {% endfor %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/manage_sizes.html b/templates/manage_sizes.html index b5c434a..e98fd3c 100644 --- a/templates/manage_sizes.html +++ b/templates/manage_sizes.html @@ -20,7 +20,8 @@
- +
diff --git a/templates/manage_toxicities.html b/templates/manage_toxicities.html index c166e38..d597403 100644 --- a/templates/manage_toxicities.html +++ b/templates/manage_toxicities.html @@ -20,7 +20,8 @@
- +