Spaces:
Sleeping
Sleeping
| """ | |
| NurseLex — Pre-cached legislation for offline lookup. | |
| Loads full section text from nursing_sections.json (1,000+ sections). | |
| Source: legislation.gov.uk (Crown Copyright, OGL v3.0) via i.AI Lex API. | |
| """ | |
| import os | |
| import json | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| # --- Configuration --- | |
| JSON_PATH = os.path.join(os.path.dirname(__file__), "nursing_sections.json") | |
| # --- Load Sections --- | |
| CACHED_SECTIONS = [] | |
| try: | |
| if os.path.exists(JSON_PATH): | |
| with open(JSON_PATH, "r", encoding="utf-8") as f: | |
| CACHED_SECTIONS = json.load(f) | |
| logger.info(f"Loaded {len(CACHED_SECTIONS)} sections from {JSON_PATH}") | |
| else: | |
| logger.warning(f"Metadata file {JSON_PATH} not found.") | |
| except Exception as e: | |
| logger.error(f"Error loading {JSON_PATH}: {e}") | |
| # --- Keyword Map for Natural Language Shortcuts --- | |
| # Direct section links for common nursing search terms | |
| KEYWORD_MAP = { | |
| "nurse holding power": ("ukpga/1983/20", "5(4)"), | |
| "doctor holding power": ("ukpga/1983/20", "5(2)"), | |
| "section 2": ("ukpga/1983/20", "2"), | |
| "section 3": ("ukpga/1983/20", "3"), | |
| "section 4": ("ukpga/1983/20", "4"), | |
| "aftercare": ("ukpga/1983/20", "117"), | |
| "section 117": ("ukpga/1983/20", "117"), | |
| "leave of absence": ("ukpga/1983/20", "17"), | |
| "section 17": ("ukpga/1983/20", "17"), | |
| "place of safety": ("ukpga/1983/20", "136"), | |
| "section 136": ("ukpga/1983/20", "136"), | |
| "section 135": ("ukpga/1983/20", "135"), | |
| "best interests": ("ukpga/2005/9", "4"), | |
| "capacity test": ("ukpga/2005/9", "3"), | |
| "functional test": ("ukpga/2005/9", "3"), | |
| "mca principles": ("ukpga/2005/9", "1"), | |
| "safeguarding": ("ukpga/2014/23", "42"), | |
| "section 42": ("ukpga/2014/23", "42"), | |
| "advocacy": ("ukpga/2014/23", "67"), | |
| } | |
| def search_cached(query: str, max_results: int = 5) -> list: | |
| """ | |
| Search local sections by keyword, title, or legislation ID. | |
| Returns a list of section dictionaries. | |
| """ | |
| if not query: | |
| return [] | |
| query = query.lower().strip() | |
| results = [] | |
| # 1. Check Keyword Map First (High Precision) | |
| for kw, (leg_id, sec_num) in KEYWORD_MAP.items(): | |
| if kw in query: | |
| # Find the specific section in our list | |
| for s in CACHED_SECTIONS: | |
| if s.get("legislation_id") == leg_id and str(s.get("number")) == sec_num: | |
| if s not in results: | |
| results.append(s) | |
| # Find closest matches if exact number not found (e.g. 5(4) vs 5) | |
| if not results: | |
| for s in CACHED_SECTIONS: | |
| if s.get("legislation_id") == leg_id and str(s.get("number")).startswith(sec_num.split('(')[0]): | |
| if s not in results: | |
| results.append(s) | |
| # 2. Text-based Search | |
| # Sort sections by relevance (title match > text match) | |
| scored_results = [] | |
| for s in CACHED_SECTIONS: | |
| score = 0 | |
| title = s.get("title", "").lower() | |
| text = s.get("text", "").lower() | |
| leg_id = s.get("legislation_id", "").lower() | |
| num = str(s.get("number", "")).lower() | |
| # Exact section reference (e.g. "Section 5") | |
| if f"section {num}" in query or f"s.{num}" in query or f"s {num}" in query: | |
| score += 100 | |
| # Title matches | |
| if query in title: | |
| score += 50 | |
| # Word matches in title | |
| for word in query.split(): | |
| if len(word) > 3 and word in title: | |
| score += 10 | |
| # Content matches | |
| if query in text: | |
| score += 5 | |
| if score > 0: | |
| scored_results.append((score, s)) | |
| # Sort and add to results | |
| scored_results.sort(key=lambda x: x[0], reverse=True) | |
| for _, s in scored_results: | |
| if s not in results: | |
| results.append(s) | |
| if len(results) >= max_results: | |
| break | |
| return results[:max_results] | |