Species.vpc_raw (JSONField) stores the full scrape payload at import time. vpc_species_reset view restores bloom_months, sunlight, max_height_cm etc. from that snapshot. If vpc_raw is missing (species imported before this feature), it re-scrapes from VPC on demand and saves the result. Reset button appears on plant detail page for any plant with a VPC species. A 'next' hidden field returns the user to the same plant after reset. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
128 lines
4.6 KiB
Python
128 lines
4.6 KiB
Python
from concurrent.futures import ThreadPoolExecutor
|
|
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.urls import reverse
|
|
from django.views.decorators.http import require_POST
|
|
from plants.models import Species
|
|
from plants.services.perenual import search_species as perenual_search, PerenualError
|
|
from plants.services import vpc as vpc_service
|
|
from plants.services.vpc import VPCError
|
|
|
|
_MONTH_MAP = {
|
|
'January': 1, 'February': 2, 'March': 3, 'April': 4,
|
|
'May': 5, 'June': 6, 'July': 7, 'August': 8,
|
|
'September': 9, 'October': 10, 'November': 11, 'December': 12,
|
|
}
|
|
|
|
|
|
def species_search(request):
|
|
q = request.GET.get('q', '').strip()
|
|
perenual_results = []
|
|
vpc_results = []
|
|
error = None
|
|
|
|
if len(q) >= 2:
|
|
def _run_perenual():
|
|
try:
|
|
return perenual_search(q), None
|
|
except PerenualError as e:
|
|
return [], str(e)
|
|
|
|
def _run_vpc():
|
|
try:
|
|
return vpc_service.search_species(q)
|
|
except VPCError:
|
|
return []
|
|
|
|
with ThreadPoolExecutor(max_workers=2) as ex:
|
|
f_p = ex.submit(_run_perenual)
|
|
f_v = ex.submit(_run_vpc)
|
|
perenual_results, error = f_p.result()
|
|
vpc_results = f_v.result()
|
|
|
|
return render(request, 'plants/partials/species_results.html', {
|
|
'perenual_results': perenual_results,
|
|
'vpc_results': vpc_results,
|
|
'q': q,
|
|
'error': error,
|
|
})
|
|
|
|
|
|
def species_select(request):
|
|
perenual_id = int(request.POST['perenual_id'])
|
|
if not Species.objects.filter(perenual_id=perenual_id).exists():
|
|
pruning_str = request.POST.get('pruning_months', '')
|
|
pruning_months = [
|
|
_MONTH_MAP[m.strip()]
|
|
for m in pruning_str.split(',')
|
|
if m.strip() in _MONTH_MAP
|
|
]
|
|
Species.objects.create(
|
|
perenual_id=perenual_id,
|
|
common_name=request.POST.get('common_name', ''),
|
|
scientific_name=request.POST.get('scientific_name', ''),
|
|
watering=request.POST.get('watering', ''),
|
|
sunlight=request.POST.get('sunlight', ''),
|
|
pruning_months=pruning_months,
|
|
api_image_url=request.POST.get('api_image_url', ''),
|
|
)
|
|
species = Species.objects.get(perenual_id=perenual_id)
|
|
return redirect(f"{reverse('plant_add')}?species_id={species.pk}")
|
|
|
|
|
|
def vpc_select(request):
|
|
slug = request.POST.get('vpc_slug', '').strip()
|
|
if not slug:
|
|
return redirect(reverse('plant_add'))
|
|
|
|
existing = Species.objects.filter(vpc_slug=slug).first()
|
|
if existing:
|
|
return redirect(f"{reverse('plant_add')}?species_id={existing.pk}")
|
|
|
|
try:
|
|
data = vpc_service.scrape_plant(slug)
|
|
except VPCError:
|
|
# Fall back to plant_add without species pre-fill
|
|
return redirect(f"{reverse('plant_add')}?species_id=skip")
|
|
|
|
species = Species.objects.create(
|
|
common_name=data['common_name'],
|
|
scientific_name=data['scientific_name'],
|
|
bloom_months=data['bloom_months'],
|
|
max_height_cm=data['max_height_cm'],
|
|
sunlight=data['sunlight'],
|
|
growth_rate=data['growth_rate'],
|
|
description=data['description'],
|
|
api_image_url=data['api_image_url'],
|
|
vpc_slug=slug,
|
|
vpc_raw=data,
|
|
)
|
|
return redirect(f"{reverse('plant_add')}?species_id={species.pk}")
|
|
|
|
|
|
@require_POST
|
|
def vpc_species_reset(request, species_pk):
|
|
"""Restore species fields to the original VPC scrape values."""
|
|
species = get_object_or_404(Species, pk=species_pk, vpc_slug__gt='')
|
|
raw = species.vpc_raw
|
|
if not raw:
|
|
# vpc_raw missing (imported before this feature) — re-scrape
|
|
try:
|
|
raw = vpc_service.scrape_plant(species.vpc_slug)
|
|
species.vpc_raw = raw
|
|
except VPCError:
|
|
pass
|
|
|
|
if raw:
|
|
species.common_name = raw.get('common_name', species.common_name)
|
|
species.scientific_name = raw.get('scientific_name', species.scientific_name)
|
|
species.bloom_months = raw.get('bloom_months', species.bloom_months)
|
|
species.max_height_cm = raw.get('max_height_cm', species.max_height_cm)
|
|
species.sunlight = raw.get('sunlight', species.sunlight)
|
|
species.growth_rate = raw.get('growth_rate', species.growth_rate)
|
|
species.description = raw.get('description', species.description)
|
|
species.api_image_url = raw.get('api_image_url', species.api_image_url)
|
|
species.save()
|
|
|
|
# Redirect back to the plant that triggered the reset, if provided
|
|
next_url = request.POST.get('next') or reverse('plant_list')
|
|
return redirect(next_url)
|