From f5941053114552ba3e8c58aa21809f96f63575de Mon Sep 17 00:00:00 2001 From: Stephan Kerkman Date: Thu, 28 May 2026 20:20:06 +0200 Subject: [PATCH] feat: import VPC plant by pasting URL directly in search MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit slug_from_query() detects VPC URLs and bare slugs (must contain a hyphen to avoid treating single search words as slugs). When matched, the search bypasses the keyword search and shows the plant directly as a VPC result. Updated search hint text to make this discoverable. Fixes: VPC search by English name (e.g. "crowfoot") is a VPC limitation — their search only indexes Latin titles, not custom fields like Engelse naam. The URL paste workflow is the reliable workaround. Co-Authored-By: Claude Sonnet 4.6 --- plants/services/vpc.py | 14 ++++++++ plants/templates/plants/species_search.html | 2 +- plants/views/species.py | 37 ++++++++++++--------- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/plants/services/vpc.py b/plants/services/vpc.py index 2911d97..1ebd2c0 100644 --- a/plants/services/vpc.py +++ b/plants/services/vpc.py @@ -76,6 +76,20 @@ def _slug_from_url(url): return m.group(1) if m else '' +def slug_from_query(query): + """Return a VPC slug if query is a VPC URL or bare slug, else empty string.""" + query = query.strip() + # Full URL containing /soorten/ + slug = _slug_from_url(query) + if slug: + return slug + # Bare slug: lowercase, hyphens, no spaces, must contain at least one hyphen + # (single words are normal search terms, not slugs) + if re.fullmatch(r'[a-z0-9][a-z0-9\-]+', query) and '-' in query: + return query + return '' + + def search_species(query): """ Search VPC and return up to 8 result dicts: diff --git a/plants/templates/plants/species_search.html b/plants/templates/plants/species_search.html index 0e33bce..a661809 100644 --- a/plants/templates/plants/species_search.html +++ b/plants/templates/plants/species_search.html @@ -2,7 +2,7 @@ {% block title %}Add Plant — PlantDB{% endblock %} {% block content %}
Add a plant
-

Search for the species to auto-fill care info, or skip to enter details manually.

+

Search by name, or paste a VPC URL to import a specific plant directly.

= 2: - def _run_perenual(): - try: - return perenual_search(q), None - except PerenualError as e: - return [], str(e) + # If the query looks like a VPC URL or slug, skip the search and show + # it directly as a single importable result. + vpc_slug = vpc_service.slug_from_query(q) + if vpc_slug: + vpc_results = [{'slug': vpc_slug, 'common_name': vpc_slug.replace('-', ' ').title(), + 'scientific_name': '', 'thumbnail_url': '', 'source': 'vpc'}] + else: + 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 [] + 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() + 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,