+
    PoiU0                         R t ^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIHt ^ RIH	t	 ^ RI
HtHtHtHtHtHt ]! 4        RtRtR tRR	 ltR
 tR t]R8X  d
   ]! 4        R# R# )u  
GPT property analysis — store-based, structured input, JSON mode, single call.

Replaces analyze_from_urls.py. Instead of scraping pages and sending raw HTML to GPT,
this reads pre-computed data from the store and sends a compact property card.

Usage:
    python3 analyze_properties.py              # Analyze all unanalyzed active properties
    python3 analyze_properties.py --limit 5    # Test on 5 properties
    python3 analyze_properties.py --dry-run    # Show what would be analyzed
    python3 analyze_properties.py --force      # Re-analyze even if already done
    python3 analyze_properties.py --model gpt-4o-mini  # Choose model (default: gpt-4o-mini)
N)datetime)load_dotenv)OpenAI)loadpersistupsert	is_active	get_score	short_urla  You are a real estate analyst evaluating properties for a multi-functional rural project combining:
- Regenerative market garden (permaculture, no-dig)
- Guest accommodation (B&B / vacation rental)
- Workshop / food processing
- Rental units for income

Score each criterion 1-5 based on the property data provided. Be honest and critical.
1 = Unsuitable (fundamental problems)
2 = Limited (significant disadvantages)
3 = Moderate (mix of pros/cons)
4 = Well suited (minor adaptations)
5 = Excellent (ideal fit)

Respond ONLY with valid JSON.a  Evaluate this property:

{property_card}

Return JSON with exactly this structure:
{{
  "market_garden": {{"score": <1-5>, "reason": "<20 words max>"}},
  "guest_accommodation": {{"score": <1-5>, "reason": "<20 words max>"}},
  "workshop": {{"score": <1-5>, "reason": "<20 words max>"}},
  "rental_units": {{"score": <1-5>, "reason": "<20 words max>"}},
  "location": {{"score": <1-5>, "reason": "<20 words max>"}},
  "local_market": {{"score": <1-5>, "reason": "<20 words max>"}},
  "has_electricity": <true|false|null>,
  "has_mains_water": <true|false|null>,
  "risk_profile": "<low|medium|high>",
  "risk_reason": "<20 words max>",
  "renovation_estimate": "<none|minor|moderate|major|rebuild>",
  "summary": "<2 sentences: strongest point + biggest concern>"
}}

Key scoring notes:
- HARD REQUIREMENTS: Must have electricity AND running water (mains or well with pump). No utilities = high risk.
- Market garden: land >= 3000m2 minimum (non-negotiable). 5000m2+ AND rainfall 600-1300mm = score 3+. No land or < 3000m2 = score 1.
- Guest accommodation: MUST be habitable now. Ruin = score 1. Charm + bedrooms = higher.
- Workshop: needs existing outbuilding 30m2+. No outbuildings = score 2.
- Rental units: needs 3+ bedrooms or multiple buildings.
- Location: airport < 2h = good. Under 45 min = excellent.
- Local market: population density > 50/km2 or tourist area = score 3+.
- has_electricity/has_mains_water: true if mentioned, false if explicitly absent, null if unknown.c           	        . pV P                  R4      pV'       d   VP                  RVR 24       V P                  R4      pV P                  R4      pV P                  R4      p. pV'       d   VP                  RV 24       V'       d   VP                  RVR	 R
24       V'       d   VP                  RVR R
24       V'       d!   VP                  RP                  V4      4       V P                  R4      pV P                  R4      pV'       g	   V'       dJ   V'       d   RV 2MRp	V'       d   RV 2MRp
VP                  RP                  \        RW.4      4      4       V P                  R4      pV'       d   VP                  RV R24       V P                  R. 4      pV'       dT   V Uu. uF$  qP	                  RR4      P	                  RR4      NK&  	  ppVP                  RRP                  V4       24       . pV P                  R4      '       g   RV9   d   VP                  R4       V P                  R4      '       g   RV9   d   VP                  R4       V'       d%   VP                  R RP                  V4       24       MVP                  R!4       . pV P                  R"4      '       d   VP                  V R",          4       V P                  R#4      '       d   VP                  V R#,          4       V P                  R$4      '       d   VP                  V R$,          4       V'       d%   VP                  R%RP                  V4       24       M2V P                  R&4      '       d   VP                  R%V R&,           24       . pV P                  R'4      '       d   VP                  R(V R',           R)24       V P                  R*4      '       d   VP                  R+V R*,          R, 24       V P                  R-4      '       d   VP                  R.V R-,           R/24       V P                  R04      '       d   VP                  R14       V'       d!   VP                  RP                  V4      4       V P                  R24      pV'       d   VP                  R3VR	 R424       V P                  R54      ;'       g    V P                  R64      pV'       dA   \        V\        \        34      '       d%   VR78  d   VP                  R8VR7,          R	 R924       V P                  R:4      pV'       d   VP                  R;V 24       V P                  R<4      ;'       g    V P                  R=4      ;'       g    RpV'       d,   \        V4      ^28  d   VP                  R>VR?,           24       V P                  R@R4      pV'       d+   RAVP                  4       9  d   VP                  ^ RBV 24       V'       d   RCP                  V4      # RD# u upi )Ez7Build a compact text card from pre-computed store data.pricezPrice: EUR z,.0fbedroomsbuilding_size_m2land_size_m2z
Bedrooms: z
Building: z.0fm2zLand: z | dpe
year_builtzDPE:  zBuilt: ~Nrenovation_scorezRenovation estimate: z/5 (1=pristine, 5=rebuild)keyword_signalshas__ z
Features: z, has_electricityzelectricity: YEShas_mains_waterzmains water: YESzUtilities: z"Utilities: UNKNOWN (check listing)citycountyregionz
Location: locationsoil_quality_scorezSoil quality: z/5soil_phzpH: z.1f
risk_scorezNatural risk: z/5 (5=safe)has_flood_riskz
FLOOD ZONEannual_rainfall_mmz
Rainfall: zmm/yearairport_distance_kmhospital_distance_mi  z
Airport: ~kmproperty_typezType: descriptionanalysisz
Description:
:Ni   Ntitlejust a momentzTitle: 
zNo data available)getappendjoinfilterreplace
isinstanceintfloatlenlowerinsert)proplinesr   bedsbuildinglandpartsr   yeardpe_stryear_strrenosignalsslabels	utilities	loc_parts	api_partsrainfall
airport_kmptypedescr*   s   &                      Z/Users/jonathan/Documents/Zakelijk/ClaudeOS/03_Lab/farmmatch/scraper/analyze_properties.pybuild_property_cardrM   K   s   E HHWE{5,/088JDxx*+H88N#DEz$()z(3r23vd4[+,UZZ&' ((5/C88L!D
d#&E#-B(,XdV$"UZZtg-@ ABC 88&'D,TF2LMN hh("-GCJK7a))FB'//S97Kz$))F"3!456 Ixx!""&77&B+,xx!""&77&B+,{499Y#7"89:9: Ixxf&xxh(xxh(z$))I"6!789	*		z$z"2!345 Ixx$%%>$/C*D)ERHIxx	4Y456xx>$|*<)=[IJxx !!&UZZ	*+ xx,-Hz(3w78/0SSDHH=R4SJj3,//J4ELL:joc%:"=> HH_%EveW%& 88M"@@dhhz&:@@bDD	B'T
|45 HHWb!E5Q'%)*$499U=*==C Ls   -*Xc                *   \        V 4      '       g   R# V'       d   R# V P                  R4      '       d   R# V P                  R/ 4      pV'       dC   \        V4      ^8  d3   V P                  R4      ;'       g    RP                  4       pRV9  d   R# R# )zProperty needs GPT analysis.FTgpt_analyzed_atcriteriar*   r   r+   )r   r-   r5   r6   )r8   forcerP   r*   s   &&  rL   needs_analysisrR      sr    T??xx!""xx
B'HCMQ&'"((b//1%'    c                `   \        V4      p\        VP                  4       4      ^8  d   R # \        P	                  VR7      p V P
                  P                  P                  VRR/RRR\        /RRRV/.R	R
R7      pVP                  ^ ,          P                  P                  P                  4       p\        P                  ! V4      p/ pR! F  p	VP                  V	/ 4      p
\        V
\         4      '       g   K-  RV
9   g   K6  V
R,          p\        V\"        \$        34      '       g   K]  ^Tu;8:  d
   ^8:  g   Kn  M Kr  \#        V4      W&   K  	  \        V4      ^8  d   RR\        V4       R23# RVRVP                  RR4      RVP                  RR4      RVP                  RR4      RVP                  RR4      R\&        P(                  ! 4       P+                  4       RV/pR" F0  pVP                  V4      p\        V\,        4      '       g   K,  WV&   K2  	  VP.                  pRVP0                  RVP2                  RVP4                  /pVV3#   \        P6                   d"   pRR\9        T4      R,           23u Rp?# Rp?i\:         d"   pRR\9        T4      R,           23u Rp?# Rp?ii ; i)#z5Send property card to GPT and return parsed criteria.N)property_cardtypejson_objectrolesystemcontentuserg        i  )modelresponse_formatmessagestemperature
max_tokensscorezonly z criteria parsedrP   risk_profilemediumrisk_reasonr   renovation_estimategpt_summarysummaryrO   	gpt_modelinputoutputtotalzJSON parse error: :N<   NzAPI error: :NP   N)Nzinsufficient data)market_gardenguest_accommodationworkshoprental_unitsr   local_market)r   r   )rM   r5   stripCRITERIA_PROMPTformatchatcompletionscreateSYSTEM_PROMPTchoicesmessagerZ   jsonloadsr-   r2   dictr3   r4   r   now	isoformatboolusageprompt_tokenscompletion_tokenstotal_tokensJSONDecodeErrorstr	Exception)clientr8   r\   cardpromptresptextdatarP   keyentryra   resultflagvalr   tokenses   &&&               rL   analyze_propertyr      sv   t$D
4::<2((##$#7F61{{&&--#]39m<F3  . 	
 ||A&&..446zz$ ACHHS"%E%&&7e+;gec5\22qEQ$'JHMA x=15X/?@@@ DHH^X>488M26!488,A2#F488Ir2x||~779
 ;D((4.C#t$$"t ; 

5..%:Q:Q5--/ v~ 8)#a&+777 1{3q6#;-0001s\   B1I 9I "I )I :5I 0BI =I J-"I>8J->J-J-J("J-(J-c                     \         P                  ! R R7      p V P                  R\        ^ RR7       V P                  RRRR7       V P                  R	RR
R7       V P                  RRRR7       V P	                  4       p\
        P                  ! R4      pV'       g   \        R4       R# \        4       pVP                  4        UUu. uF$  w  rE\        WQP                  4      '       g   K"  VNK&  	  pppVP                  '       d   VRVP                   p\        R\        V4       R24       \        RVP                   24       \        R\        V4       R24       \        4        VP                  '       dy   VR,           F:  pW4,          p\        \!        V4      4      p\        R\#        V4       RV R24       K<  	  \        V4      ^8  d    \        R\        V4      ^,
           R24       R# V'       g   \        R4       R# \%        VR7      p^ p	^ p
R^ R ^ R!^ /p\'        V4       EF\  w  rW4,          p\        R"V^,            R#\        V4       R$\#        V4       R%2R&R'R(7       \)        WVP                  4      w  rV'       d   \+        W4V4       V	^,          p	VR),          pR&P-                  R* VP                  4        4       4      p\        R+V R,24       \/        V\0        4      '       d/   V F(  pVV;;,          VP3                  V^ 4      ,          uu&   K*  	  MV
^,          p
\        R-V R,24       V^,           ^
,          ^ 8X  d#   \5        V4       \        R.VR!,          R/ R024       \6        P8                  ! R14       EK_  	  \        R2V	 R#\        V4       RV
 R324       \        R4VR,          R/ R5VR ,          R/ R6VR!,          R/ R724       VR!,          ^ 8  dE   VR,          R8,          R9,          pVR ,          R:,          R9,          p\        R;VV,           R< 24       V	^ 8  d   \5        V4       R# R# u uppi )=z#GPT property analysis (store-based))r(   z--limitzMax properties (0=all))rV   defaulthelpz	--dry-run
store_truezShow what would be analyzed)actionr   z--forcezRe-analyze already donez--modelzgpt-4o-miniz#OpenAI model (default: gpt-4o-mini))r   r   OPENAI_API_KEYz=Error: OPENAI_API_KEY not set. Create .env file or export it.Nu   GPT ANALYSIS — z properties to analyzez	  Model: z	  Store: z total properties:N   Nz  z (z chars)z
  ... and z morezNothing to do.)api_keyri   rj   rk   z  [/z] z...r   T)endflushrP   c              3   B   "   T F  w  rVR ,           RV 2x  K  	  R# 5i):N   N=N ).0kvs   &  rL   	<genexpr>main.<locals>.<genexpr>?  s       KNDAAbE7!A3Ns   zOK ()zFAIL (u     [saved progress — ,z tokens used]g      ?z

Analyzed z failed)z
  Tokens: z	 input + z
 output = z totalg333333?i@B g333333?z  Estimated cost: $z.4f)argparseArgumentParseradd_argumentr3   
parse_argsosgetenvprintr   itemsrR   rQ   limitr5   r\   dry_runrM   r
   r   	enumerater   r   r/   r2   r~   r-   r   timesleep)parserargsr   storeurlp
candidatescard_lenr   analyzedfailedr   ir8   r   infoscores	score_strr   
cost_inputcost_outputs                        rL   mainr   
  s   $$1VWF
	Q=UV
L?\]
	,=VW
	=?deDii()GMNFE$)KKMSM&#^Azz5R##MJSzzz,
	c*o..D
EF	Idjj\
"#	Ic%j\!2
34	G|||c??C
A.q12HBy~&b
':; # z?RJs:34E:;G$FHFQ!Wa8LJ'zAaC5#j/*"Ys^,<C@cQUV'djjA5v&MHJ'F KFLLN KKID1%&$%%%A Otxx1~5O & aKFF4&"# ER<1EN*<+@*C=QR

35 (8 
Kz3z?"32fXX
FG	J|G,Q/yh9OPQ8RR\]ijq]rst\uu{
|} Gq !'*T1I=
"8,t3i?#J$<S#ABC!| E Ts   P;)P;__main__)F)__doc__r   r|   r   r   r   dotenvr   openair   r   r   r   r   r   r	   r
   ry   rt   rM   rR   r   r   __name__r   rS   rL   <module>r      si      	     H H !f@g>T&?1DQh zF rS   