diff --git a/plants/forms.py b/plants/forms.py index 2a3f165..3fe77a5 100644 --- a/plants/forms.py +++ b/plants/forms.py @@ -9,16 +9,16 @@ MONTH_CHOICES = [ class PlantForm(forms.ModelForm): - pruning_months = forms.MultipleChoiceField( + bloom_months = forms.MultipleChoiceField( choices=MONTH_CHOICES, widget=forms.CheckboxSelectMultiple, required=False, - label='Pruning months', + label='Bloom months', ) class Meta: model = Plant - fields = ['name', 'location', 'is_indoor', 'pruning_months', 'photo', 'notes'] + fields = ['name', 'location', 'is_indoor', 'bloom_months', 'photo', 'notes'] widgets = { 'notes': forms.Textarea(attrs={'rows': 3}), 'name': forms.TextInput(attrs={'class': 'form-control'}), @@ -27,14 +27,13 @@ class PlantForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - # JSONField stores ints; CheckboxSelectMultiple expects strings matching choice values - if self.instance.pk and self.instance.pruning_months: - self.initial['pruning_months'] = [str(m) for m in self.instance.pruning_months] - elif not self.instance.pk and self.initial.get('pruning_months'): - self.initial['pruning_months'] = [str(m) for m in self.initial['pruning_months']] + if self.instance.pk and self.instance.bloom_months: + self.initial['bloom_months'] = [str(m) for m in self.instance.bloom_months] + elif not self.instance.pk and self.initial.get('bloom_months'): + self.initial['bloom_months'] = [str(m) for m in self.initial['bloom_months']] - def clean_pruning_months(self): - return [int(m) for m in self.cleaned_data.get('pruning_months', [])] + def clean_bloom_months(self): + return [int(m) for m in self.cleaned_data.get('bloom_months', [])] class PruningLogForm(forms.ModelForm): diff --git a/plants/migrations/0003_add_bloom_months.py b/plants/migrations/0003_add_bloom_months.py new file mode 100644 index 0000000..4784f98 --- /dev/null +++ b/plants/migrations/0003_add_bloom_months.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.1 on 2026-05-28 15:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('plants', '0002_alter_pruninglog_plant_alter_species_max_height_cm'), + ] + + operations = [ + migrations.AddField( + model_name='plant', + name='bloom_months', + field=models.JSONField(default=list), + ), + migrations.AddField( + model_name='species', + name='bloom_months', + field=models.JSONField(default=list), + ), + ] diff --git a/plants/models.py b/plants/models.py index eda0716..4f2b663 100644 --- a/plants/models.py +++ b/plants/models.py @@ -12,6 +12,7 @@ class Species(models.Model): growth_rate = models.CharField(max_length=100, blank=True) growth_season = models.CharField(max_length=100, blank=True) pruning_months = models.JSONField(default=list) + bloom_months = models.JSONField(default=list) api_image_url = models.URLField(blank=True) perenual_id = models.IntegerField(unique=True, null=True, blank=True) @@ -32,6 +33,7 @@ class Plant(models.Model): location = models.CharField(max_length=200) is_indoor = models.BooleanField(default=False) pruning_months = models.JSONField(default=list) + bloom_months = models.JSONField(default=list) photo = models.ImageField(upload_to='plants/', blank=True) notes = models.TextField(blank=True) date_added = models.DateField(auto_now_add=True) diff --git a/plants/templates/plants/base.html b/plants/templates/plants/base.html index 4aac15e..4d6608c 100644 --- a/plants/templates/plants/base.html +++ b/plants/templates/plants/base.html @@ -30,7 +30,6 @@ 🌿 PlantDB
Plants - ✂️ +
diff --git a/plants/templates/plants/dashboard.html b/plants/templates/plants/dashboard.html index 41dd125..4a431f8 100644 --- a/plants/templates/plants/dashboard.html +++ b/plants/templates/plants/dashboard.html @@ -1,4 +1,5 @@ {% extends "plants/base.html" %} +{% load plant_extras %} {% block title %}Dashboard — PlantDB{% endblock %} {% block content %}
{{ today|date:"F Y" }}
@@ -15,39 +16,26 @@
- -
-
-
- {{ needs_pruning_count }} -
-
Need pruning
-
+
-{% if overdue or due_this_month %} +{% if blooming_now %}
-
✂️ Needs attention
+
🌸 Blooming this month
- {% for plant in overdue %} + {% for plant in blooming_now %}
{{ plant.name }}
{{ plant.location }}
- overdue -
- {% endfor %} - {% for plant in due_this_month %} - -
-
{{ plant.name }}
- {{ plant.location }} -
- this month +
{% month_strip plant.bloom_months %}
{% endfor %}
diff --git a/plants/templates/plants/plant_detail.html b/plants/templates/plants/plant_detail.html index 3f8e756..e09c0b4 100644 --- a/plants/templates/plants/plant_detail.html +++ b/plants/templates/plants/plant_detail.html @@ -52,7 +52,13 @@ {% endif %} {% endif %} -{% include "plants/partials/pruning_strip.html" %} +{% load plant_extras %} +{% if plant.bloom_months %} +
+ 🌸 Bloom +
{% month_strip plant.bloom_months %}
+
+{% endif %} {% if plant.notes %}
diff --git a/plants/templates/plants/plant_form.html b/plants/templates/plants/plant_form.html index 9a6c9e0..3315b1d 100644 --- a/plants/templates/plants/plant_form.html +++ b/plants/templates/plants/plant_form.html @@ -41,9 +41,9 @@
- +
- {% for widget in form.pruning_months %} + {% for widget in form.bloom_months %}
{{ widget.tag }} diff --git a/plants/tests/test_views.py b/plants/tests/test_views.py index 1b72c4c..6b5b8ca 100644 --- a/plants/tests/test_views.py +++ b/plants/tests/test_views.py @@ -16,21 +16,18 @@ class TestDashboard: resp = client.get(reverse('dashboard')) assert resp.context['total_plants'] == 2 - def test_overdue_plant_appears_in_context(self, client): - plant = Plant.objects.create(name='Rose', location='Garden', pruning_months=[3]) - # No log — overdue relative to current date (month 3 is more than 6 months ago only if checked after September) - # Use a month that's definitely in the recent past: use today's month - 1 - resp = client.get(reverse('dashboard')) - # Just check the view works; pruning logic is tested separately - assert 'overdue' in resp.context - - def test_due_this_month_plant_appears_in_context(self, client): + def test_blooming_now_plant_appears_in_context(self, client): today = date.today() - plant = Plant.objects.create( - name='Apple', location='Garden', pruning_months=[today.month] - ) + plant = Plant.objects.create(name='Rose', location='Garden', bloom_months=[today.month]) resp = client.get(reverse('dashboard')) - assert plant in resp.context['due_this_month'] + assert plant in resp.context['blooming_now'] + + def test_non_blooming_plant_not_in_blooming_now(self, client): + today = date.today() + other_month = today.month % 12 + 1 + plant = Plant.objects.create(name='Apple', location='Garden', bloom_months=[other_month]) + resp = client.get(reverse('dashboard')) + assert plant not in resp.context['blooming_now'] @pytest.mark.django_db diff --git a/plants/views/dashboard.py b/plants/views/dashboard.py index 23019e0..a1443c6 100644 --- a/plants/views/dashboard.py +++ b/plants/views/dashboard.py @@ -1,26 +1,16 @@ from datetime import date from django.shortcuts import render from plants.models import Plant -from plants.utils.pruning import pruning_status def dashboard(request): today = date.today() - all_plants = list( - Plant.objects.select_related('species').prefetch_related('pruning_logs') - ) - overdue, due_this_month = [], [] - for plant in all_plants: - status = pruning_status(plant, today) - if status == 'overdue': - overdue.append(plant) - elif status == 'due_this_month': - due_this_month.append(plant) + all_plants = list(Plant.objects.select_related('species').all()) + blooming_now = [p for p in all_plants if today.month in (p.bloom_months or [])] return render(request, 'plants/dashboard.html', { 'total_plants': len(all_plants), - 'overdue': overdue, - 'due_this_month': due_this_month, - 'needs_pruning_count': len(overdue) + len(due_this_month), + 'blooming_now': blooming_now, + 'blooming_now_count': len(blooming_now), 'today': today, }) diff --git a/plants/views/plants.py b/plants/views/plants.py index 6a04355..3efcb8f 100644 --- a/plants/views/plants.py +++ b/plants/views/plants.py @@ -78,7 +78,7 @@ def plant_add(request): plant.save() return redirect('plant_detail', pk=plant.pk) else: - initial = {'pruning_months': species.pruning_months} if species else {} + initial = {'bloom_months': species.bloom_months} if species else {} form = PlantForm(initial=initial) return render(request, 'plants/plant_form.html', {