"""Rule-based and static fallbacks when LLM matching is unavailable.""" from __future__ import annotations from typing import Any, Dict, List STATIC_FALLBACK_PROGRAMS: List[Dict[str, Any]] = [ { "id": "feng-smart-path", "name": "Ścieżka SMART (FENG)", "type": "FENG", "match": 72, "chance": "Średnia", "amount": "do 2 mln PLN", "shortDesc": "Wsparcie B+R i wdrożenia innowacji dla MŚP w ramach FENG.", "fullDesc": "Program dla mikro, małych i średnich przedsiębiorstw realizujących projekty badawczo-rozwojowe.", "url": "https://www.funduszeeuropejskie.gov.pl/", "explanation": { "reason": "Ogólne dopasowanie do profilu innowacyjnego MŚP (tryb awaryjny bez AI).", "criteria": ["MŚP", "B+R", "innowacje produktowe"], "risks": "Wymaga weryfikacji manualnej kwalifikowalności i statusu MŚP.", }, }, { "id": "parp-step-innovation", "name": "STEP — Innowacje dla MŚP (PARP)", "type": "PARP", "match": 65, "chance": "Średnia", "amount": "zależnie od naboru", "shortDesc": "PARP — wsparcie cyfryzacji i rozwoju kompetencji MŚP.", "fullDesc": "Programy PARP dla przedsiębiorstw planujących inwestycje w technologie i usługi.", "url": "https://www.parp.gov.pl/", "explanation": { "reason": "Alternatywne dopasowanie dla firm usługowych i produkcyjnych (tryb awaryjny).", "criteria": ["MŚP", "cyfryzacja", "rozwój"], "risks": "Sprawdź aktualny status naboru i region.", }, }, ] def get_static_fallback_programs() -> List[Dict[str, Any]]: return [dict(p) for p in STATIC_FALLBACK_PROGRAMS] def scored_to_programs(scored: List[Dict[str, Any]]) -> List[Dict[str, Any]]: programs: List[Dict[str, Any]] = [] for rec in scored: score = int(rec.get("relevance_score") or 0) programs.append( { "id": rec.get("grant_id") or rec.get("id") or "unknown", "name": rec.get("grant_name") or "Program dotacyjny", "type": _program_type_from_grant(rec), "match": score, "chance": "Wysoka" if score >= 80 else ("Średnia" if score >= 60 else "Niska"), "amount": "zależnie od naboru", "shortDesc": (rec.get("why_it_fits") or "")[:200], "fullDesc": rec.get("why_it_fits") or "", "url": rec.get("official_page_url") or rec.get("regulation_url") or "", "explanation": { "reason": rec.get("why_it_fits") or "Dopasowanie rule-based.", "criteria": rec.get("reasons") or [], "risks": "; ".join(rec.get("penalties") or []) or "Brak", }, } ) return programs def matches_to_programs(matches) -> List[Dict[str, Any]]: programs: List[Dict[str, Any]] = [] for m in matches: score = getattr(m, "score", None) or (m.get("score") if isinstance(m, dict) else 0) programs.append( { "id": getattr(m, "program_id", None) or (m.get("program_id") if isinstance(m, dict) else ""), "name": getattr(m, "program_name", None) or (m.get("program_name") if isinstance(m, dict) else ""), "type": getattr(m, "source", None) or (m.get("source") if isinstance(m, dict) else "PROGRAM"), "match": score, "chance": "Wysoka" if score >= 80 else ("Średnia" if score >= 60 else "Niska"), "amount": "TBD", "shortDesc": getattr(m, "ui_description", None) or (m.get("ui_description") if isinstance(m, dict) else ""), "fullDesc": getattr(m, "rationale", None) or (m.get("rationale") if isinstance(m, dict) else ""), "url": getattr(m, "official_url", None) or (m.get("official_url") if isinstance(m, dict) else ""), "explanation": { "reason": getattr(m, "rationale", None) or (m.get("rationale") if isinstance(m, dict) else ""), "criteria": [], "risks": "Wymaga weryfikacji" if getattr(m, "requires_verification", False) else "Brak", }, "regulation_grounding": getattr(m, "regulation_grounding", None) or (m.get("regulation_grounding") if isinstance(m, dict) else ""), } ) return programs def _program_type_from_grant(grant: Dict[str, Any]) -> str: for key in ("operator", "program", "zrodlo", "source"): val = (grant.get(key) or "").upper() if "PARP" in val: return "PARP" if "FENG" in val: return "FENG" if "NCBR" in val: return "NCBR" name = (grant.get("grant_name") or grant.get("name") or "").upper() if "SMART" in name: return "SMART" return "PROGRAM"