
    Ƣ+j                    
   d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	m
Z
mZ d dlmZmZmZmZmZmZ dZdZe j*                  j-                  e j*                  j/                  e      d      Ze j*                  j/                  e      Ze j*                  j-                  ed      Ze j*                  j-                  ed      Zd	 Z e        d
 Zd Zd Z d Z!d Z"dddddddddddddddddddddddddddddd d!dd"d#d!dd$d%d!dd&d'd!dd(d)d*dd+d,d*dd-d.d*dd/d0d*dd1d2d3dd4d5d3dd6d7d3dd8d9d3dd:d;d3dgZ#g d<dddd=dddd>d?ddd@dAdddBddddCd'dddDdEdddFddddGdHdddIddddJdKdddLd dddMdNdddOddddPdQdddRdSdddTddddUdVdddWdXdddYd7dddZd[ddd\d)ddd]d^ddd_d`dddadbd!ddcddd!dded%d!ddfd;d!ddgd0d!ddhdid!ddjd#d!ddkdld!ddmd,d!ddndod!ddpdqd!ddrdsd!ddtdud!ddvdwd!ddxdyd*ddzd{d*dd|d}d*dd~dd*dddd*dddd*dddd*dddd*dddd3dddd3dddd3dddd3dZ$g dZ%g dZ&g dg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg dddg d¢ddg dĢddg dƢddg dȢddg dʢddg d̢ddg d΢ddg dТddg dҢddddgddddgddg dڢddddgdZ'dddddddddddddddddddddddddddddddddddddddddddddddddddddd ddddddddddddd	d
dddddddgddddddddddddddddddddddddddd d!d"dd#d$d%d&dd'd(dd)dd*d+d,d-dd.d/dd0dd1d2dddd3d4d5ddd6d7d5ddd8d9dd&dd:d;d<d=dgd>Z(d? Z)d@ Z*dA Z+dWdBZ,dC Z-dD Z.dXdEZ/dF Z0dG Z1dH Z2dI Z3dJ Z4dK Z5dL Z6dM Z7dN Z8dXdOZ9 G dP dQe
      Z:e;dRk(  rw e jx                  e j*                  j/                  e j*                  j{                  e                    e>dSe         e>dT        edUefe:      Z?	 e?j                          yy# eA$ r  e>dV       e?j                          Y yw xY w(Y      N)SimpleHTTPRequestHandler
HTTPServer)INTERIOR_PHOTOSMALE_PORTRAITSFEMALE_PORTRAITSget_portrait_urlget_interior_urlget_person_photoi  
   excel_backupszLfnb_merchant_seed_dataset_FNB_50_M011_M020_images_refreshed_relevant_v2.xlsxzlatest_reference.jsonc                     t         j                  j                  t         j                  j                  t              d      } t         j                  j                  |       sy 	 t        | dd      5 }|D ]  }|j                         }|r|j                  d      sd|vr+|j                  dd      \  }}|j                         }|j                         j                  d      j                  d	      }|s|t         j                  vs|t         j                  |<    	 d d d        y # 1 sw Y   y xY w# t        $ r}t        d
|        Y d }~y d }~ww xY w)Nz.envrutf-8encoding#=   "'z#Warning: failed to load .env file: )ospathjoindirname__file__existsopenstrip
startswithsplitenvironOSErrorprint)env_pathenv_fileraw_linelinekeyvaluees          app/server.pyload_env_filer,      s   ww||BGGOOH5v>H77>>(#9(C'2h$~~'ts3s$!ZZQ/
Uiik++C066s;3bjj0&+BJJsO % 322  93A37889sC   #D3 1BD'5D'D'D3 'D0,D3 0D3 3	E<EEc                     t         j                  j                  | xs d      j                         }|sd}dj	                  d |D              S )Nbackup.xlsx c              3   L   K   | ]  }|j                         s|d v r|nd  yw))-_. r2   N)isalnum).0cs     r+   	<genexpr>z+sanitize_backup_filename.<locals>.<genexpr>,   s(     ]S\a		q,@'@1cIS\s   "$)r   r   basenamer   r   )filename	safe_names     r+   sanitize_backup_filenamer<   (   sB      !:];AACI!	77]S\]]]    c                    t        j                  t        d       t        |       }t         j                  j                  |      \  }}|xs d}t         j                  j                  t        | |       }d}t         j                  j                  |      rPt         j                  j                  t        | d| |       }|dz  }t         j                  j                  |      rP|S )NT)exist_okz.xlsxr   r2   )r   makedirs
BACKUP_DIRr<   r   splitextr   r   )r:   r;   	base_nameext	candidatecounters         r+   reserve_backup_pathrG   .   s    KK
T*(2IWW%%i0NIs
.CZI;se)<=IG
''..
#GGLL	{!G9SE-JK	1 ''..
# r=   c                     t         j                  j                  | t              j	                  t         j
                  d      S )N/)r   r   relpathPROJECT_DIRreplacesep)	file_paths    r+   to_project_relative_pathrO   <   s(    77??9k2::2663GGr=   c                     t         j                  j                  t              r	 t	        t        dd      5 } t        j                  |       }d d d        j                  dd      j                         }|rEt         j                  j                  t        |      }t         j                  j                  |      r|S t        S t        S # 1 sw Y   |xY w# t        t
        j                  f$ r}t        d|        Y d }~t        S d }~ww xY w)Nr   r   r   relativePathr/   z3Warning: failed to read active reference metadata: )r   r   r   ACTIVE_REFERENCE_METAr   jsonloadgetr   r   rK   r"   JSONDecodeErrorr#   DEFAULT_DATASET_FILE)	meta_filedatarelative_pathabsolute_pathr*   s        r+   get_active_reference_pathr\   ?   s    	ww~~+,		M+S7Cyyy+ D HH^R8>>@M "[- H77>>-0((   DC --. 	MGsKLL	Ms/   C CA.C CC D
-DD
c                     t        |       }t        t        dd      5 }t        j                  d|i|       d d d        |S # 1 sw Y   |S xY w)Nwr   r   rQ   )rO   r   rR   rS   dump)rN   rZ   rX   s      r+   set_active_reference_pathr`   M   sE    ,Y7M	#S7	;y		>=19= 
< 
<s   AAzMei Ling Tanz photo-1494790108377-be9c29b29330Chinese)namephoto	ethnicityzWei Jie Limz photo-1507003211169-0a1dd7228f2dzXiu Hui Chenz photo-1517841905240-472988babdf9zJun Hao Wongz photo-1506794778202-cad84cf45f1dz
Hui Min Ngz photo-1534528741775-53994a69daebzZhi Wei Gohz photo-1472099645785-5658abf4ff4ezQi Xuan Leez photo-1488716820095-cbe80883c496zAmirah Binte Hassanz photo-1524504388940-b1c1722653e1MalayzFaizal Bin Ismailz photo-1570295999919-56ceb5ecca61zNurul Ain Binte Rahmanz photo-1531427186611-ecfd6d936c79zHafiz Bin Abdullahz photo-1519085360753-af0119f7cbe7zPriya Krishnamurthyz photo-1580489944761-15a19d654956IndianzArjun Selvamz photo-1568602471122-7832951cc4c5z
Deepa Nairzphoto-1546961329-78bef0414d7czRajan Pillaizphoto-1552058544-f2b08422138azJamie Pereiraz photo-1502685104226-ee32379fefbeEurasianzAlex D'Souzazphoto-1542909168-82c3e7fdca5czSophie Tan-Williamszphoto-1554151228-14d9def656e4zMarcus Fernandezzphoto-1552374196-c4e7ffc6e126z	Clara Yapzphoto-1544723795-3fb6469f5b39z
Cheryl Limz
Darren GohzNicholas Teoz photo-1500648767791-00dcc994a43ez
Fiona Chenz photo-1438761681033-6461ffad8d80zBrandon Tanz
Keith WongzRyan Kohz photo-1522075469751-3a6694fb2f61z	Rachel Ngz
Jeremy Lowz photo-1539571696357-5a69c17a67c6zNatalie Chuaz	Isaac Leez photo-1501196354995-cbb51c65aaeazMichelle Weez
Samuel Tayz photo-1492562080023-ab3db95bfbcezJessica Simz	Marcus Hozphoto-1560250097-0b93528c311azKimberly Ongzphoto-1544005313-94ddf0286df2zJonathan SeahzValerie Leongz photo-1531746020798-e6953c6e8e04z
Adrian Looz photo-1513956589380-bad6acb9b9d4zStephanie YeozGabriel Tanz photo-1527980965255-d3b416303d12zPatricia LimzBenjamin Chanz photo-1500048993953-d23a436266cfz	Evelyn Ngzphoto-1542206395-9feb3edaa68dzSyazwan Bin Ramliz photo-1566753323558-f4e0952af115zSiti Nurhalizaz photo-1529626455594-4ff0802cfb7ezFarhan Bin RoslizDiana Binte RoslanzIskandar Bin ZulkiflizHidayah Binte Samadz photo-1531123897727-8f129e1688cezKhairul Bin AnwarzFazira Binte Sallehz photo-1508214751196-bcfd4ca60f91zRidzuan Bin HashimzNadhirah Binte Jamilz photo-1573496359142-b8d87734a5a2zZulfikar Bin Hamidz photo-1517423568366-8b83523034fdzAishah Binte Mansurz photo-1537368910025-700350fe46c7zFirdaus Bin Latifz photo-1564564321837-a57b7070ac4fzKamariah Binte Daudz photo-1504257486230-1699f45f011czKarthik Rajanz photo-1520156473399-030ec603e5c4zShalini Deviz photo-1530268729831-4b0b9e170218zVignesh Kumarz photo-1509783236416-c9ad59bab472zPreethi Nairz photo-1589156280159-27698a70f29ezDinesh Pillayz photo-1534751516642-a131ffd10b7fzMeera Krishnanz photo-1567532939604-b6b5b0db2604zSuresh Naiduz photo-1579038773867-044c48829161zDivya Selvanz photo-1619380061814-58f03707f082zChristian de Souzaz photo-1607990283143-e81e7a2c93abzSarah Rodriguesz photo-1628157582853-a796fa650a6azNathanial Pereiraz photo-1607746882042-944635dfe10ezClara Fernandezz photo-1614644147798-f8c0fc9da7f6)zStore ManagerzOperations LeadzKitchen LeadzService CaptainzMarketing Coordinator)LeadSeniorri   StandardJuniorzphoto-1554118811-1e0d58224f24)zcafe interiorzcoffee shoptableszindoor seatingdining)idtagsz photo-1501339847302-ac426a4a7cbb)zcafe storefrontzoutdoor seatingpatioexteriorz photo-1495474472287-4d71bcdd2085)zespresso barzbarista counterzcoffee machinebrewingzphoto-1555396273-367ea4eb4db5)zrestaurant kitchenzchef counterz	food prepcookingz photo-1517248135467-4c7edcad34c4)zdining roomzgroup tableszrestaurant seatingzcozy diningzphoto-1546069901-ba9599a7e63c)z	salad barbuffetzhealthy food displayzorganic choicesz photo-1512621776951-a57141f2eefd)zvegan food cornerzgreen counterzfresh juicez photo-1511920170033-f8396924c348)zbakery displayzcake counterpastrieszdessert showcasez photo-1577896851231-70ef18881754)	classroomz
kids deskszlearning area	childcarez photo-1564424224828-5d10d0b2c85b)z	play areatoyskindergartenzindoor playz photo-1587654780291-39c9404d746b)zkids learning cornerzeducational games
activitiesz photo-1485546246426-74dc88dec4d9)nurseryz
baby cribszinfant careznap roomz photo-1503676260728-1c00da094a0b)zkids libraryzreading zone
storybookszphoto-1560066984-138dadb4c035)zhair salon chairszstyling stationzhaircut salonz photo-1604654894610-df63bc536371)znail salon deskzmanicure stationpedicurez photo-1522337360788-8b13dee7a37e)z
facial bedzskincare roomztreatment zonez photo-1540555700478-4be289fbecef)zspa massage roomztherapy tablezrelaxation roomz photo-1620331713507-b0a7d1a73387)zlash and brow chairzcosmetic studiomakeupz photo-1516734212186-a967f81ad0d7)zpet grooming tubzdog wash stationgroomingz photo-1581888227599-779811939961)zdog play areazpet cafe lobbyzcat playzonez photo-1535268647977-a403b69fc7f5)zveterinary exam tablez
pet clinicztreatment roomz photo-1601758228041-f3b2795255f1)zdog boarding kennelzpet supplies shelfz
food storez photo-1441986300917-64674bd600d8)zclothing rackzboutique interiorzshopping clothesz photo-1534452203293-494d7ddbf7e0)zshoe display shelveszshopping storeaccessoriesz photo-1472851294608-062f824d29cc)zcheckout counterzcashier deskzreception receptionzphoto-1542838132-92c53300491e)zsupermarket aislezgrocery shelveszretail displayz photo-1534438327276-14e5300c3a48)zweight lifting gymzdumbbells rackzworkout spacez photo-1518611012118-696072aa579a)zyoga mats floorzmeditation studio interiorpilatesz photo-1518310383802-640c2de311b2)ztreadmill rowzcardio areazfitness equipmentz photo-1526506118085-60ce8714f8c5)zpilates reformerzexercise machines
stretchingz photo-1511512578047-dfb367046420)zarcade machineszgaming zoneplaystationzphoto-1552820728-8b83bb6b773f)zvr headsets play areazgaming room	simulatorz photo-1609873963870-dfae66681634ztrampoline park jumping areatrampolinesz photo-1538481199705-c710c4e965fczbowling lanes pin deckbowlingz photo-1531538606174-0f90ff5dce83)zescape room game roompuzzleslockersz photo-1508847154043-be12a62861c1zindoor playground slideszball pitzSignature LattezdA smooth espresso latte with silky milk and light foam, crafted for a warm and familiar cafe moment.   Beverage)rb   descpricecatzBrunch Pancake StackztFluffy pancakes stacked with a golden finish, served as a hearty cafe brunch plate with a sweet, comforting profile.   BrunchzCafe Avocado ToastzrCrusty sourdough topped with fresh avocado, seasoning, and a poached egg for a light and satisfying brunch choice.   	CroissantzdA buttery, flaky croissant baked fresh and served warm as a quick pastry option for any time of day.   PastryzIced Americanoz`Cold brewed espresso with filtered water over ice, delivering a clean and refreshing coffee hit.   z
Flat WhitezlA smooth espresso-based coffee with velvety milk, balanced body, and a gentle finish for daily cafe sipping.
CappuccinozeA classic coffee with espresso, steamed milk, and airy foam, made for a warm and familiar cafe break.zIced CoffeezbCold coffee served over ice with a clean, refreshing taste and a light finish for warm afternoons.zBanana Bread SlicezlMoist banana bread slice with a soft crumb and gentle sweetness, ideal as a snack or light breakfast option.zCheesecake SlicezqCreamy New York-style cheesecake with a buttery crust and smooth filling, served as a dessert or afternoon treat.	   DessertzFresh Salad BowlziMixed greens, garden vegetables, and a light dressing tossed together for a refreshing cafe lunch choice.   z
Light MealzGrilled SandwichzkA toasted sandwich layered with fresh fillings, melted cheese, and a satisfying bite for a quick cafe meal.   SnackzLatte Art CoffeezkAn espresso latte topped with a hand-poured milk art design, offering both a visual and flavour experience.   zCreamy Pasta PlatezlCreamy pasta tossed until glossy and rich, finished as a filling plate for guests who want a warm cafe main.   PastazMatcha Lattez\Premium ceremonial matcha whisked with steamed milk for a smooth, earthy green tea beverage.Signature DishzNOur signature dish, crafted with fresh local ingredients and served with care.MainzHouse SpecialzUA crowd favourite made fresh daily, combining bold flavours with premium ingredients.   z
Set Meal AzUA complete set meal featuring a main, side, and beverage for a satisfying experience.   SetzChef's RecommendationzSHand-picked by our chef for quality and flavour, representing the best of our menu.   zChef's PickzClassic BowlzUA comforting bowl of rice, protein, and fresh vegetables served in traditional style.BowlzCombo PlatterzQA generous platter combining three of our most popular dishes, ideal for sharing.#   PlatterzLight BiteszHSmall bites of crisp appetisers served warm and fresh, perfect to start.   StarterzHouse NoodleszIHand-pulled noodles tossed in a rich sauce with toppings, prepared fresh.NoodleszGrilled ProteinzKFreshly grilled with seasoning and served with sides, suiting any appetite.   Grillz
House SoupzEA rich broth simmered for hours with premium ingredients, served hot.SoupzSeasonal DessertzRA sweet finish made from seasonal fruits and premium dairy, crafted to complement.z
Soft DrinkzAA chilled soft drink served with ice, perfect alongside any meal.   z	House TeazEFreshly brewed local tea served hot or iced, a refreshing complement.zSharing StarterzIA generous starter platter of bite-sized pieces to kick off a group meal.zWeekend SpecialzJA limited-weekend creation combining seasonal produce and chef creativity.   Special)Cafesdefaultc                     t        t              }| }t        t        |      dz
  dd      D ]2  }|dz  dz   dz  }|dk\  r|n|dz   }||dz   z  }||   ||   c||<   ||<   4 |d d S )	Nr   r   f _n<            r   )list	REVIEWERSrangelen)seedarrsi
unsigned_sjs         r+   sample_reviewersr      s    
y/CA3s8a<B'[:%3q&Qq;
!a% QQAA ( r7Nr=   c           
         d|v xs d|v }|r	t         d   nt         d   }|d d }g }t        |      D ]V  \  }}	|	d   }
|s
|
dk(  r| d}
|j                  d	| d
d   d|d
z    |
|	d   j                  d|      |	d   |	d   |dk  d       X |S )NCafeCoffeer   r      rb   r   z Signature DishIr   r1   r   z${merchantName}r   r      )rn   rb   r   r   r   featured)PRODUCT_TEMPLATES	enumerateappendrL   )midcategorymerchant_namer   is_cafepoolchosenproductsr   prb   s              r+   generate_productsr      s     8H$8G)0W%6G	6RD#2YFH&!1y4#33#_O4Dc!"gYa!u%fI%%&7GwZU8A
 	 " Or=   c                 t   g d}t        t              }|dz  dz   }t        t        |      dz
  dd      D ]2  }|dz  dz   d	z  }|dk\  r|n|d
z   }||dz   z  }	||	   ||   c||<   ||	<   4 |d d }
g }t	        t
              D ]5  \  }}|
|   }|j                  d| dd   ||    |d   |t        |   d       7 |S )N)ABCDEI      r   r   r   r   r   r   r   r   Prb   )rn   rb   rolelevel)r   PERSONNEL_POOLr   r   r   ROLESr   LEVELS)r   mid_numr   r   lettersr   r   r   r   r   r   	personnelr   r   s                 r+   generate_personnelr     s    #G
~
C"rA3s8a<B'[:%3q&Qq;
!a% QQAA	 (
 !WFIU#41Ic!"gYwqzl+fIAY	
 	 $ r=   c                    d| d|  }|r'|s%|dt         j                  j                  |       z  }|r|d| z  }t         j                  j	                  |ddi      }t         j                  j                  |      5 }t        j                  |j                         j                  d            cd d d        S # 1 sw Y   y xY w)	Nzhhttps://maps.googleapis.com/maps/api/place/nearbysearch/json?location=1.3521,103.8198&radius=12000&type=&key=z	&keyword=z&pagetoken=
User-AgentMozilla/5.0headersr   
urllibparsequoterequestRequesturlopenrS   loadsreaddecode)api_key
place_type
page_tokenkeywordurlreqress          r+   fetch_nearby_placesr   (  s    tu  uA  AF  GN  FO  PCz6<<--g6788ZL))
..
 
 |].K
 
LC				$zz#((*++G45 
%	$	$s   >2B::Cc                     ddddddddd	}|j                  | d      }d }| d
k(  r$ddddddddddddd}|j                  |d      \  }}||fS )Nschoolbeauty_salon
restaurant	pet_storestoregymamusement_parkestablishmentrw   beautyfnbpetsretailfitnessplayothersr  )cafezcafe coffee)r  zwestern restaurant bistro)r  zchinese restaurant)r  zjapanese restaurant sushi ramen)r  zkorean restaurant)r  zthai restaurant)r  zindian restaurant)r  zhalal malay restaurant)bakeryzdessert bakery cake)r  zhotpot steamboat restaurant)r  zvegan acai salad cafe)r  zrestaurant cafe food)Cafes & CoffeezWestern / FusionzChinese CuisinezJapanese FoodKoreanThairf   zMalay / HalalDesserts / BakeryHotpotz$Health (Vegan / Acai / Clean eating)zOthers (F&B))r  zrestaurant caferU   )industry_keyr   default_type_mapr   r   fnb_search_maps         r+   get_place_search_configr  3  s      !	 "%%lLAJGu5 KCN959E!BC4[B
 -00;\]
Gwr=   c                 j   d}d| dt         j                  j                  |       d|  }t         j                  j	                  |ddi      }t         j                  j                  |      5 }t        j                  |j                         j                  d            cd d d        S # 1 sw Y   y xY w)	Nzname,formatted_address,formatted_phone_number,website,rating,price_level,geometry,opening_hours,types,photos,vicinity,address_components,place_idzAhttps://maps.googleapis.com/maps/api/place/details/json?place_id=z&fields=r   r   r   r   r   r   )r   place_idfieldsr   r   r   s         r+   fetch_place_detailsr   T  s     aFMhZW_`f`l`l`r`rsy`z_{  |A  BI  AJ  KC
..
 
 |].K
 
LC				$zz#((*++G45 
%	$	$s   -2B))B2c                 t   | j                  d      xs dj                         | j                  dg       D ch c]  }|j                          }}h d}h d}g d}g d}g d}g d	}	t        ||z        }
t        ||z        }t        fd
|D              }t        fd|D              }t        fd|D              }t        fd|	D              }|ry|r|
sy|dk(  r"g d}d|v xs t        fd|D              xs |S |dk(  rg d}d|v xs t        fd|D              S |ry|
ry|r|ry|S c c}w )Nrb   r/   types>   r  r  r  >   foodmeal_deliverymeal_takeaway)r  restoranr  coffeer  bistroeaterykitchengrillbbqhotpotramensushinoodlepizzasteakbrunchkopitoastdessertespressolatte)hotelhostelresorttower	residence	apartmentmallzshopping centrezshopping centerhospitalmedicalclinicr  tuitionbankofficebuildingcondominiumzcountry club	clubhouseclubzcommunity centerzcommunity centresafra)barwhiskeywhiskypublounge)zfood centrezfood centerzhawker centrezhawker centerhawkerc              3   &   K   | ]  }|v  
 y wN r6   r   rb   s     r+   r8   z(is_relevant_fnb_place.<locals>.<genexpr>s  s     P>O7w$>O   c              3   &   K   | ]  }|v  
 y wrR  rS  rT  s     r+   r8   z(is_relevant_fnb_place.<locals>.<genexpr>t       #ZCYGtOCYrU  c              3   &   K   | ]  }|v  
 y wrR  rS  rT  s     r+   r8   z(is_relevant_fnb_place.<locals>.<genexpr>u  rW  rU  c              3   &   K   | ]  }|v  
 y wrR  rS  rT  s     r+   r8   z(is_relevant_fnb_place.<locals>.<genexpr>v  s     !VAUg'T/AUrU  Fr  )r  r'  r7  r8  r4  r5  r  c              3   &   K   | ]  }|v  
 y wrR  rS  rT  s     r+   r8   z(is_relevant_fnb_place.<locals>.<genexpr>~  s     %S]'go]rU  r  )r  caker6  pastrybreadr  c              3   &   K   | ]  }|v  
 y wrR  rS  rT  s     r+   r8   z(is_relevant_fnb_place.<locals>.<genexpr>  s     'WG4rU  T)rU   lowerboolany)placer   tr"  primary_typessupporting_typespositive_keywordshard_negative_keywordssoft_negative_keywordsfood_centre_keywordshas_primary_typehas_supporting_typehas_positive_keywordhas_hard_negative_keywordhas_soft_negative_keywordhas_food_centre_keywordcafe_keywordsbakery_keywordsrb   s                     @r+   is_relevant_fnb_placerr  \  s`   IIf#**,D %		'2 67 61QWWY 6E74MA
 KeEM12u'778P>OPP ##ZCY#Z Z ##ZCY#Z Z!!VAU!VV  )9##Pn#%S]%S"SnWnn&&J5 WC'W'W$WW3U 8s   D5c                     | sy| j                         }g d}|D ]  }|j                         |v s|c S  g d}|D ]!  }||v s|d   |dd  j                         z   c S  y)NCentral)0OrchardDhoby GhautSomersetTiong BahruBuona VistaBishanNovena	City HallBugis
Marina Bay	Chinatown	Toa PayohClementiJurong EastJurong WestBukit TimahBukit PanjangChoa Chu KangSengkangHougangPunggolTampinesBedok	Pasir RisChangiMarine ParadeKatong
Paya LebarSimei	Serangoon
Ang Mo KioYishun	Woodlands	Sembawang	AdmiraltyHarbourFrontSentosaLittle IndiaFarrer ParkKallangRiver Valley
Beach RoadLavenderGeylang
Queenstown	AlexandraRedhillBoon Lay)ORCHARDCLEMENTIJURONGTAMPINESBEDOKCHANGIBISHANPUNGGOLSENGKANGHOUGANGYISHUN	WOODLANDSz
ANG MO KIO	SERANGOONzBUKIT TIMAHNOVENA	CHINATOWNMARINAzTIONG BAHRUKATONGGEYLANGz
PAYA LEBARr   r   )upperr_  )addressr  areasarea	districtsds         r+   
infer_arear    ss    MMOE	E ::<5 K 2I :Q4!AB%++-''  r=   c                    i ddddddddddddd	dd
dddddddddddddddddddi ddddddddddddddddddddd dd!dd"dd#dd$d%d&d%d'd%d%d(d(dddddddddddd)}|j                  | d      S )*Nru  rt  rv  rw  rx  ry  Westrz  r{  r|  r}  r~  r  r  r  r  r  r  r  r  r  z
North-Eastr  r  r  Eastr  r  r  r  r  r  r  r  r  r  Northr  r  South)r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  )r  area_regions     r+   
get_regionr    s   9+Y8BIy"/9A9 	) )) 6=i 	i	 "-i	 :Ei	
 	F
 *6
 4A& 	y #26 <KF 	L #,\ <El 	F $V .9& 	& *6 4<V 	f &v 0;L 	l %-g 8CG 	W ,3GIiiy&F!K$ ??4++r=   c                    d|  }dd|igig|rdd|igind ddid}ddd}t        j                  |      j                  d	      }t        j                  j                  |||d
      }	 t        j                  j                  |      5 }t        j                  |j                         j                  d	            }	|	d   d   d   d   d   d   }
t        j                  |
j                               cd d d        S # 1 sw Y   y xY w# t        j                  j                  $ r?}|j                         j                  d	      }t        d|j                   d|       d }~ww xY w)Nz]https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=partstextresponseMimeTypeapplication/json)contentssystemInstructiongenerationConfigr   )Content-Typer   r   POSTrY   r   method
candidatesr   contentzGemini API HTTP Error : rS   dumpsencoder   r   r   r   r   r   r   r   error	HTTPError	Exceptioncoder   system_instructionprompt_textr   payloadr   rY   r   r   res_datar  r*   err_bodys                r+   call_geminir    sZ   ijqir
sC 5678J\g1C(D'EFbf 2
G +#G
 ::g%%g.D
..
 
 4
 
PCG^^##C(Czz#((*"3"3G"<=HL)!,Y7@CFKD::djjl+ )(( <<!! G668??7+08*EFFGs7   *D 	A)C<2	D <DD D E$%:EE$c                    d}dddid|dd|dgdd	}d
d|  dd}t        j                  |      j                  d      }t        j                  j                  |||d      }	 t        j                  j                  |      5 }t        j                  |j                         j                  d            }	|	d   d   d   d   }
t        j                  |
j                               cd d d        S # 1 sw Y   y xY w# t        j                  j                  $ r?}|j                         j                  d      }t        d|j                   d|       d }~ww xY w)Nz*https://api.openai.com/v1/chat/completionszgpt-4.1-minitypejson_objectsystem)r   r  userg333333?)modelresponse_formatmessagestemperaturer  zBearer r   )r  Authorizationr   r   r  r  choicesr   messager  zOpenAI API HTTP Error r  r  r  s                r+   call_openair    sC   
6C"M2*<=4
 G +"7),#G ::g%%g.D
..
 
 4
 
PCG^^##C(Czz#((*"3"3G"<=HI&q))4Y?D::djjl+ )(( <<!! G668??7+08*EFFGs7   %C= A#C1'	C= 1C:6C= :C= =E:EEc                     ddddddddddgddgdddgdddgdgi}|| d	}d
t        j                  t               dt        j                  |       dt        j                  |d       S )N	merchantsM101stringr   r   )reviewerNameoverallRatingqualityRatingserviceRatingenvironmentRatingvalueForMoneyRatingvibeTags
reviewText)categoryNamesubFacilitiesimage1PhotoIdimage2PhotoId)
merchantIdreviews
facilities)industryr  a  Generate realistic Singapore business reviews and facility categories for every merchant in the input JSON.

Return valid JSON only with the top-level key "merchants".

Rules for each merchant:
1. Generate exactly 5 reviews for the listed reviewers and keep the reviewer order exactly as given.
2. The overall ratings for the 5 reviews must be exactly: 5, 5, 4, 3, 2.
3. qualityRating should match overallRating.
4. serviceRating, environmentRating, and valueForMoneyRating must be integers from 1 to 5.
5. Each review must have 2-3 vibeTags.
6. reviewText must use this exact structure with line breaks and emojis:
   star line for quality mentioning 1-2 product names
   bell line for service mentioning 1 personnel name
   sparkle line for environment
   receipt line for overall verdict
7. Generate exactly 10 facility categories per merchant.
8. Each facility category must have exactly 2 subFacilities.
9. image1PhotoId and image2PhotoId must be different and selected only from the allowed image pool.

Allowed image pool:
z

Response schema example:
z

Input JSON:
F)ensure_ascii)rS   r  FACILITY_IMAGES)merchant_contextsindustry_labelschema_exampleinput_payloads       r+   build_gemini_batch_promptr	    s    $ )1)*)*)*-./0%-x$8&.	 )1*2H)=)1)1	
N: #&M
( O   N   M. /53 3r=   c                 h   t        | t              xr t        | j                  d      t              xr t        | j                  d      t              xr_ t        | j                  dg             dk(  xr? t        | j                  d      t              xr t        | j                  dg             dk(  S )Nr  r   r   r  r   )
isinstancedictrU   strr   r   )items    r+   is_valid_gemini_batch_itemr  1  s    4 	2txx-s3	2txx	*D1	2 B'(A-	2 txx-t4		2
 r*+r1r=   c                     |D ci c]  }t        |      r|j                  d      |! }}| D ]Z  }|d   }||   }|j                  |      }|r&|d   |d<   |d   |d<   t        d| d|d    d	       Ft        d
| d|d    d       \ y c c}w )Nr  r   geminiReviewsr  geminiFacilitiesz  z generated batch data for rb   z successfully.u	     ⚠️ z$ returned incomplete batch data for r3   )r  rU   r#   )	r  merchant_lookupbatch_resultssource_labelr  results_by_midcontextr   merchants	            r+   apply_batch_resultsr  ;  s     "!D%d+ 	$!   %l#"3'!!#&(,YH_%+/+=H'(B|n$>x?O>PP^_`Il^+OPXY_P`Oaabcd %s   $Bc                    | s|sy t        dt         d       d}t        dt        |      t              D ]  }|||t        z    }|t        z  dz   }	t        d|	 dt        |       d       g }
i }t	        |      D ]  \  }}||z   }||z   }dt        |      j                  d	       }t        |||d
   |      }t        ||||d
         }t        |dz  dz         }|
j                  ||d
   ||d
    d|j                          d|D cg c]  }|d
   |d   d c}|D cg c]  }|d
   |d   d c}|D cg c]  }|d
   	 c}d       |||<    t        |
|      }d}|rA	 t        |||      }t        |t              r|j!                  dg       ng }t#        |
||d       nd}|sv| sz	 t'        | ||      }t        |t              r|j!                  dg       ng }t#        |
||d        y c c}w c c}w c c}w # t$        $ r&}d}t        d|	 dt        |              Y d }~d }~ww xY w# t$        $ r%}t        d|	 dt        |              Y d }~(d }~ww xY w)Nz$Calling AI enrichment in batches of z merchants...zYou are an expert copywriter and dataset generator for Find, a Singapore business directory app. Generate realistic, high-quality reviews and facilities. Always return strict JSON matching the requested schema.r   r   z  Processing AI batch z with Mr   rb   a      z is a z1 business offering quality products and services.r   )rb   r   r   )rb   r   )r  merchantNamer   descriptionr   r   	reviewersFr  u   🧠 OpenAITu     ⚠️ OpenAI batch z	 failed: u   🤖 Gemini fallbacku     ⚠️ Gemini fallback batch )r#   GEMINI_BATCH_SIZEr   r   r   r  zfillr   r   r   r   r_  r	  r  r  r  rU   r  r  r  )
gemini_key
openai_keydetailed_merchants	start_midr  r   r  batch_startbatch_merchantsbatch_numberr  r  offsetr  absolute_indexr   r   r   r   chosen_reviewersr   r   promptopenai_failedresultr  r*   s                              r+    enrich_merchants_with_ai_batchesr0  M  s   j	01B0C=
QR	C  Q$6 79JK,[GX9XY"&77!;&|nF3;O:PP]^_ )/ :FH(61N.0Gc'l((+,-C(h8H'RH*38FCSTI/"r0AB$$! ( 0$"*6"2!36.:N:N:P9Q  RC   D '& vY&	:& &% vY7<% 2BB1AAai1AB&  $,OC / !;2 ++<nMP$Z1CVL?I&RV?W

; ;]_#$5Wde
 !MZY$Z1CVL?I&RV?W

; ;]_#$5Wmne L( C  P $.|nIc!fXNOOP  Y7~YsSTvhWXXYsB   G3 G88G=%?H0?H4	H1H,,H14	I"=II"c                    g d}g d}g }t        t        | t        |                  D ]  }||   }||t        |      z     }|j                  d| |d|z    d| ddt	        j                         dz  z   d	t	        j                         d
z  z   dit        dt	        j                         dz  z   d      t	        j                  g d      d        |S )N)1zKopi CornerzThe Bean SpotzMilo Dinosaur HousezNanyang Heritage CoffeezKatong Kaya ToastzTiong Bahru Bakery EastzCommon Man Coffeez
Jewel CafezSeng Kee Black Bean NoodlezLola's CafezPapa PalhetazChye Seng HuatzNylon Coffee RoasterszDuck & HippozGather HerezTwo HatszHomeground CoffeezWhite Label CoffeezVoid Deck CafezEighteen ChefszBARISTART CoffeezHighlander CoffeezPlatform CoffeezThe Refineryz
 PercolatezBooks & CoffeezForty HandsSarnieszThe Assembly GroundzCurious PaletteSymmetryzLittle Part 1z	Kith CafezProject AcaizGrain TradersWAATRziSETA CoffeezMellower CoffeezBettr BaristazColumbus CoffeezORIGIN + BLOOMzCUT by Wolfgang PuckSHANzTamarind HillzLong Beach SeafoodzCrystal JadezParadise DynastyzJumbo SeafoodzImperial Treasure)ru  r  r  r  r  r  rz  r  r  r  r  r  r  r  r  r{  r  r  r  
synthetic_r   z Singapore Street, locationg?g?g     Y@g333333?)latlngg      @g      ?r   )r      r:  r   )r  rb   vicinitygeometryratingprice_level)r   minr   r   randomroundchoice)countr   sg_namesr  placesr   rb   r  s           r+   generate_synthetic_placesrF    s    HE F3uc(m,-{QU^$$QC(6("5dV<$sV]]_s5J/JSY\b\i\i\knr\rSr&tvC&--/C"77;!==6
 	 . Mr=   c                   KL | xs
 t               } t        j                  j                  |       st	        d|         g S dd lKKfd}	 t        j                  | d      }dd l}|j                  D cg c]  }|j                  d|      s| }}g }|D ]  }||   }|j                  dd      d   }	g Lt        d|j                  dz         D ]G  }
t        dd	      D cg c]  }|j                  |
|
      j                  ! }}Lj!                  |       I Lfd}i }g dg dg dddgddgddgg dg dg dg dd
}|j#                         D ])  \  }} ||      }|r |L|dz
     d         ||<   %d ||<   + d}	 t%        |	dd        }|d   s)t(        |t+        t(              z     }t-        |dd      |d<   |d   s,t(        |dz   t+        t(              z     }t-        |dd      |d<   |d   sd |d<   |d!   s|d   xs d"}|j/                          d#|d!<   i }d$d%gg d&d'd(gd)d*gg d+g d,g d-g d.d/}|j#                         D ])  \  }} ||      }|r |L|dz
     d         ||<   %d ||<   + |d$   sd0|d$<   |d)   st1        |d1   xs d2      |d)<   |d'   st3        |d)   xs d3      |d'<   |d4   st5        d5|d6z  z         |d4<   |d7   sd8|d7<   |d9   sd:|d9<   i }g d;d<d=gg d>d?d@gg dAg dBg dCdD}|j#                         D ])  \  }} ||      }|r |L|dz
     d         ||<   %d ||<   + g } |dEdFg      }|rmt        ddG      D ]^  }L||z      }|d   s|j!                   ||d          ||d          ||dH          ||d          ||dI          ||dJ         dK       ` g } |dLdMg      }|r}d}	 ||z   } | t+        L      k\  rnfL|    }|d   r|d   dNv rnT|j!                   ||d          ||d          ||dH          ||d          ||dI          ||dJ         dO       |dz  }zg }! |g dN      }"|"rd}	 |"|z   } | t+        L      k\  rnL|    }|d   r|d   dPv rnr|!j!                   ||d          ||d          ||dH          ||d          ||dI          ||dJ          ||dQ          ||dR          ||dG         dS	       |dz  }g }# |dTdUg      }$|$rd}	 |$|z   } | t+        L      k\  rnL|    }|d   r|d   dVv rnr|#j!                   ||d          ||d          ||dH          ||d          ||dI          ||dJ          ||dQ          ||dR          ||dG         dW	       |dz  }g }% |g dV      }&|&rUd}	 |&|z   } | t+        L      k\  rn>L|    }|d   r|d   dXv rn,|%j!                   ||d          ||d         dY       |dz  }Rg }' |dZd[g      }(|(rd}	 |(|z   } | t+        L      k\  rnL|    }|d   r|d   d\v rnr|'j!                   ||d          ||d          ||dH          ||d          ||dI          ||dJ          ||dQ          ||dR          ||dG         d]	       |dz  }g }) |g d\      }*|*rd}	 |*|z   } | t+        L      k\  rnL|    }|d   r|d   d^v rn ||d         }+ ||d         }, ||dH         }-|d   }.g }/|.r	 t7        j8                  |.      }0t;        |0      D ]Z  \  }1}2|2j=                  d_d2      }3d`|3v r)t(        ||1z   t+        t(              z     }4t-        |4dadb      }3|3|2d_<   |/j!                  |2       \ 	 |dI   }6g }7|6r	 t7        j8                  |6      }7|)j!                  |+|,|-|/|7de       |dz  }g }8 |dfdgg      }9|9rBd}	 |9|z   } | t+        L      k\  rn*L|    }|d   r|d   dhv rn ||d         }: ||dH         }; ||d         }<|<rd`|<v rt?        |;|      }< ||dI         }= ||dJ         }> ||dQ         }? ||dR         }@ ||dG         }A|di   }Bg }C|Br	 t7        j8                  B      }C ||dn         }Et+        |      dokD  r|do   nd }Fg }G|Fr	 t7        j8                  F      }Gt+        |      dpkD  r|dp   nd }Hg }I|Hr	 t7        j8                  H      }I|8j!                  |:|;|<|=|>|?@ACEGIdq       |dz  }?|j!                  |	||||||!|#|'|%|)|8dr        |jE                          |S c c}w c c}w # t&        $ r Y w xY w# t&        $ r"}5t	        dct5        |5              Y d }5~5d }5~5ww xY w# t&        $ r"}5t	        ddt5        |5              Y d }5~5d }5~5ww xY w# t&        $ r t5        B      jA                  djd2      jA                  dkd2      jA                  dld2      j                  dm      D Dcg c]#  }D|DjC                         sDjC                         % nc c}Dw }C}DY w xY w# t&        $ r t5        F      jA                  djd2      jA                  dkd2      jA                  dld2      j                  dm      D Dcg c]#  }D|DjC                         sDjC                         % nc c}Dw }G}DY w xY w# t&        $ r t5        H      jA                  djd2      jA                  dkd2      jA                  dld2      j                  dm      D Dcg c]#  }D|DjC                         sDjC                         % nc c}Dw }I}DY nw xY w# t&        $ r}Jt	        dst5        J              |Jd }J~Jww xY w)tNzError: file not found at r   c                 l    t        | j                  j                  f      r| j                         S | S rR  )r  datetimedate	isoformat)valrI  s    r+   serialize_valuez.parse_excel_merchants.<locals>.serialize_value  s-    cH--x}}=>==?"
r=   T)	data_onlyz	^M\d{3}\br4   r   r   )rowcolumnc                    | D cg c])  }t        |      j                         j                         + }}t              D ]>  \  }}|d   st        |d         j                         j                         |v s9|dz   c S  y c c}w )Nr   r   )r  r_  r   r   )keysk
keys_loweridxr   grids        r+   find_row_by_keysz/parse_excel_merchants.<locals>.find_row_by_keys  sv    >BCdc!flln224d
C'oFCtAaD	 1 7 7 9Z G"Qw . 	 Ds   .B)Logo URLlogoUrllogo_urllogo)Merchant Namer  r   rb   )Descriptionr  r   Industryr  Categoryr   Subcategorysubcategory)Keywords (max 10)Keywordskeywords)
Price TierzPrice Range
priceRange
price_tierprice_range	priceTier)StatusbusinessStatusbusiness_statusstatus)Merchant Thumbnail URLmerchantThumbnailUrlmerchant_thumbnail_urlthumbnailUrlthumbnail_url	thumbnail)
rX  r\  r]  r^  r_  r`  rb  re  rj  rn  rX  i   rn  r   i  i  F&Brb  r  z, food, beverage, singaporeCountrycountry)Postal Code
postalCodepostal_codeRegionregionArear  )Address Line 1addressLine1address_line_1address1)Address Line 2addressLine2address_line_2address2)Latitudelatituder8  )	Longitude	longituder9  )ru  rw  rz  r|  r}  r  r  r  	Singaporer}  r/   rt  rw  鰡 %   r  3?r  PkwY@)phonePhone	telephonetelemailEmail)websiteWebsitewebtimezoneTimezone)defaultLanguagedefault_languagelanguage)acceptsReservationsaccepts_reservationsreservations)deliveryAvailabledelivery_availabledelivery)r  r  r  r  r  r  r  zOPERATING HOURSoperatingHoursr   r:  r   r   )dayr   close	lastOrderclosednoteszBANNER MEDIAbannerMedia)zITEM INFORMATION / PRODUCTSr   zITEM INFORMATION)rn   	mediaTypemediaUrlaltText	sortOrderactive)COUPONScouponsr   r   )	rn   rs  rb   r   r   r   optionr   stockStatusr  r  )TERMS_AND_CONDITIONStermsAndConditionsterms)	rn   titler   discountPercentr  r)   lastBoughtAt	soldCountremainingCount)	PERSONNELr   )number	conditionr  r   )FACILITIES_JSON_FORMATr  
FACILITIES)	rn   rs  rb   r   r   
experiencer   specialtiesworkingDays)REVIEWSr   	media_urlzsource.unsplash.comi  iI  z1Warning: Failed to parse facilities images JSON: z.Warning: Failed to parse sub-facilities JSON: )
categoryIdr  orderimagesr  r  r   )zPAYMENT METHODSPAYMENT_METHODSpaymentMethodsr   []r   ,   r   r  )rn   r  reviewerImageUrlr  r  r  r  r  r  r  recommendedProductIdsrecommendedPersonnelIds)rn   detailsr  contactshoursbannersr   r  r   r  r  r   z Exception during Excel parsing: )#r\   r   r   r   r#   rI  openpyxlload_workbookre
sheetnamesmatchr    r   max_rowcellr)   r   itemsintr  r   r   r	   r_  r  r  r  rS   r   r   rU   r
   rL   r   r  )MrN   rM  wbr  r   target_sheetsr  
sheet_namewsr   r   r7   row_valsrW  r  details_mappingr(   r  r_idxr   
logo_photothumb_photor   r  address_mappingr  contacts_mappingr  	hours_idxr*  r  banners_idxrow_idxr   products_idxr  coupons_idxr  	terms_idxr   personnel_idxr  facilities_idxcat_idcat_name	order_val
images_rawparsed_imagesimg_listrU  imgm_urlp_idr*   fac_list_rawparsed_facsr   reviews_idxr_idr_namer_imgoverallqualityserviceenvr)   vibe_raw	vibe_tagsvrev_textrec_products_rawrec_productsrec_personnel_rawrec_personnel	parse_excrI  rV  sM                                                                              @@r+   parse_excel_merchantsr    s   868I77>>)$))56	
F##I>$&MMOMqRXXlA5NMO	'JJB""3*1-C D1bjj1n-DI!RLQLqBGG!G4::LQH% . GG![E'4'4 -}=%RsS +eO $3#8#8#:Z(4#24a=3C#DGCL#'GCL $; Gc!"g, :&,Ws?7K-KL
&6z4&N
#34-w{c/>R.RS4D[RVX\4]01:&&+
#./j)3V25))+>Y/Z+, G%y1K#X.("b"b;>	O $3#8#8#:Z(4#24a=3C#DGCL#'GCL $; 9%%0	"6?",W5E-F-L""M8$$.wv/K)$L!=)),Vgl-B)C&:&&,
#;''/$ H?!7+8'4#V'f%\  $4#9#9#;Z(4$3DqM!4D$EHSM$(HSM $< E(*;=M)NOI#AqkF#I$67H{#28A;#?$3HQK$@%4Xa[%A)8!)E&5hqk&B%4Xa[%A&  * G*NM+JKK)F2G#d)+#G}H#A;(1+9x*xNN-hqk:%4Xa[%A$3HQK$@#28A;#?%4Xa[%A"1(1+">$  aKF $ H+,klL*V3G#d)+#G}H#A;(1+9O*OOO-hqk:%4Xa[%A / < / <!0!!=.x{;"1(1+">$3HQK$@'6x{'C
% 
 aKF% * G*Iy+ABK)F2G#d)+#G}H#A;(1+9p*pNN-hqk:!0!!=!0!!=+:8A;+G / <!0!!=(7(D%4Xa[%A*9(1+*F
$ 
 aKF% * E()`aI'&0G#d)+#G}H#A;(1+9S*SLL"1(1+">%4Xa[%A"  aKF  I,k;-GHM+f4G#d)+#G}H#A;(1+9o*o$$-hqk:%4Xa[%A / < / < / <&5hqk&B!0!!='6x{'C'6x{'C
& 
 aKF% * J-.deN,v5G#d)+#G}H#A;(1+9O*O,Xa[9F.x{;H / <I "*!J$&M!
`'+zz*'=H,5h,?S(+R(@#8E#A+:GcMSQ`Ma;a+bD,<T4,ME38K 0 - 4 4S 9 -@ $,A;L"$K#]*.**\*BK %%&,(0!*"/&1'  aKFY ^ G*Iy+ABK)F2G#d)+#G}H#A;(1+9q*q*8A;7D,Xa[9F+HQK8E!6%!? 0 @-hqk:G-hqk:G-hqk:G)(1+6C+HQK8E'{H "IS(,

8(<I  /x|<H 8;8}r7Ix|t$#%L'^+/::6F+GL 9<H8JPT%$&M(`,0JJ7H,IM NN"(.,1)0)0)0-0/4$-&.1=3@$  aKF} @ ""$"$"&(" W (t 	
 P RH  L  ) `!$UVYZ[V\U]"^__`  ) ]!$RSVWXSYRZ"[\\]V  ) S<?M<Q<QRUVX<Y<a<abefh<i<q<qruvx<y<<  AD  =E  )S  =Eq  IJ  IP  IP  IR  =E  )SI  )SS  ) ^?BCS?T?\?\]`ac?d?l?lmpqs?t?|?|  ~A  BD  @E  @K  @K  LO  @P  ,^  @P!  TU  T[  T[  T]AGGI  @P  ,^L  ,^^  ) `@CDU@V@^@^_bce@f@n@norsu@v@~@~  @C  DF  AG  AM  AM  NQ  AR  -`  AR1  VW  V]  V]  V_QWWY  AR  -`M  -``H  0Y0@ABsf  
)m 3d:d:Am $d?A;m ?eGm Nm ;A=e8
m fCm $f09$m h<3m k"Am :
m 	em em 	e?e:4m :e??m 	f-f("m (f--m 0Ah9h.h.-h95m 8h99m <Akj:'j:9km km Amm3mmm mm 	m;m66m;c                   8     e Zd Z fdZd Z fdZ fdZ xZS )BackendHandlerc                     | j                  dd       | j                  dd       | j                  dd       t        | 	          y )NzAccess-Control-Allow-Origin*zAccess-Control-Allow-Headersr  zAccess-Control-Allow-MethodszPOST, GET, OPTIONS)send_headersuperend_headers)self	__class__s    r+   r  zBackendHandler.end_headersJ  sB    6<7H79MNr=   c                 F    | j                  d       | j                          y )N   )send_responser  )r  s    r+   
do_OPTIONSzBackendHandler.do_OPTIONSQ  s    3r=   c           	         | j                   dk(  r	 t               }t        |      }| j                  d       | j	                  dd       | j                          | j                  j                  t        j                  d|t        |      d      j                  d             y t        | A          y # t        $ r}t        dt        |              | j                  d	       | j	                  dd       | j                          | j                  j                  t        j                  d
t        |      d      j                  d             Y d }~y d }~ww xY w)Nz/api/get-merchantsr  r  r  okrm  r  referenceFilePathr   zError parsing Excel:   r  rm  r  )r   r\   r  r  r  r  wfilewriterS   r  rO   r  r  r#   r  r  do_GET)r  active_reference_pathr  r*   r  s       r+   r"  zBackendHandler.do_GETU  s)   99,,$(A(C%12GH	""3'  1CD  "

  "!*)ABW)X- " 6'?	$ GN  $-c!fX67""3'  1CD  "

  %"1v- " 6'?$ $$s   BB4 4	E=BEEc                    | j                   dk(  rC	 t        | j                  d         }| j                  j	                  |      }t        j                  |j                  d            }|j                  dd      }|j                  dd      }|st        d      t        |      }t        j                  |      }t        |d	      5 }|j                  |       d d d        | j                  d
       | j!                  dd       | j#                          | j$                  j                  t        j&                  d|d      j)                  d             y | j                   dk(  r	 t        | j                  d         }| j                  j	                  |      }t        j                  |j                  d            }|j                  dd      }|j                  dd      }
|st        d      t        |
      }t        j                  |      }t        |d	      5 }|j                  |       d d d        t-        d       	 ddlm}  ||       t-        d       t5        |      }t7        |      }| j                  d
       | j!                  dd       | j#                          | j$                  j                  t        j&                  d||d      j)                  d             y | j                   dk(  rpt        | j                  d         }| j                  j	                  |      }t        j                  |j                  d            }|j                  dd      j9                         }|j                  dd      j9                         xs$ t;        j<                  d d      j9                         }|j                  d!d      j9                         xs$ t;        j<                  d"d      j9                         }|j                  d#d$      }|j                  d%d&      }t        |j                  d'd(            }t?        d)t        |j                  d*d)                  }d+d,d-d.d/d0d1d2d3}|j                  |d-      }tA        ||      \  }}t-        d4| d5| d6       |rt-        d7| d8| d9       	 g }d }d}|rtC        |      |d:z   k  r|d;k  rt-        d<|d=z    d>       tE        ||||      } | j                  d?      d@v r-t+        dA| j                  dB| j                  d?                   | j                  dCg       }!|jG                  |!       | j                  dD      }|d=z  }|sn1|d;k  rtI        jJ                  dE       tC        |      |d:z   k  r|d;k  rtM               }"g }#|D ]:  }$|$j                  dF      }%|%|"vs|"jO                  |%       |#jQ                  |$       < |d$k(  r4|#D $cg c]  }$tS        |$|      s|$ }#}$t-        dGtC        |#       dH       |#st-        dI       tU        ||      }#|#d | }&g }'tW        |&      D ]  \  }(})t-        dJ|(d=z    dKtC        |&       dL|)j                  dM       d6       d }*|rV|)j                  dFd      jY                  dN      s5	 t[        ||)dF         }+|+j                  d?      dOk(  r|+j                  dP      }*|*r|*n|)}-|d$k(  r*tS        |-|      st-        dR|-j                  dM              |-j                  dS|-j                  dTd            }.t]        |.      }/dd l/}0|0ja                  dU|.      }1|1r|1jc                  d      nt/        dV||(z   dWz  z         }2|-j                  dXi       j                  dYi       }3|-j                  dZ|)j                  dZg             xs g }4|4D $cg c]$  }$|$j                  d[      r|$j                  d[      & c}$d d\ }5d]d]d^d_d`da}6|-j                  dbdc      }7|6j                  |7d^      }8|'jQ                  |)j                  dF      |-j                  dM      |.r|.je                  dd      d   n||(z    de|-j                  dT      |/tg        |/      |2|3j                  dfdg      |3j                  dhdi      |-j                  dj      r<dji                  tk        t.        jl                  |-j                  djd                  dkd  nd |-j                  dl      |-j                  dmdn      |8|5do        |d$k(  rtC        |'      |k  rt-        dptC        |'       dq       |tC        |'      z
  }9tU        |9|      }:tW        |:tC        |'      r      D ]G  \  };})t]        |)j                  dTd            }/|'jQ                  |)j                  dF      |)j                  dM      |)j                  dT||;z    de      je                  dd      d   |)j                  dT      |/tg        |/      t/        dV||;z   dWz  z         |)j                  dXi       j                  dYi       j                  dfdg      |)j                  dXi       j                  dYi       j                  dhdi      d d |)j                  dmdn      d]d]d^d_d`daj                  |)j                  dbdc      d^      g do       J |s|rto        |||'|||       | j                  d
       | j!                  dd       | j#                          ddstq               v rtr        n|'dt}<| j$                  j                  t        j&                  |<      j)                  d             y tt        >|           y # 1 sw Y   
xY w# t*        $ r}	t-        dt/        |	              | j                  d       | j!                  dd       | j#                          | j$                  j                  t        j&                  dt/        |	      d      j)                  d             Y d }	~	y d }	~	ww xY w# 1 sw Y   	xY w# t*        $ r"}t-        dt/        |              Y d }~
d }~ww xY w# t*        $ r}t-        dt/        |              | j                  d       | j!                  dd       | j#                          | j$                  j                  t        j&                  dt/        |      d      j)                  d             Y d }~y d }~ww xY wc c}$w # t*        $ r"},t-        dQt/        |,              Y d },~,_d },~,ww xY wc c}$w # t*        $ r}=t-        dut/        |=              | j                  d       | j!                  dd       | j#                          | j$                  j                  t        j&                  dt/        |=      d      j)                  d             Y d }=~=y d }=~=ww xY w)vNz/api/save-excelzContent-Lengthr   r:   r.   fileDatar/   z Missing fileData for backup saver  r  r  r  r  )rm  r   zError saving Excel backup: r  r  r  z/api/upload-excelzuploaded_reference.xlsxzMissing fileData for uploadz)Excel file uploaded. Refreshing images...r   )update_excel_imagesz.Images successfully updated in uploaded Excel.z5Warning: Failed to refresh images in uploaded Excel: r  zError in upload handler: z
/api/fetchapiKeygeminiApiKeyGEMINI_API_KEYopenaiApiKeyOPENAI_API_KEYr  r  r   r  startMide   2   targetCount	ChildcareBeautyrt  PetsRetailFitnessPlayOthersr	  zBackend fetching z merchants for category: z...zUsing Google Maps search type 'z' with keyword 'z'.r   r   zFetching page r   z from Maps API...rm  )REQUEST_DENIEDINVALID_REQUESTzGoogle Maps API Error: error_messageresultsnext_page_tokeng@r  zFiltered F&B candidates to z- restaurant/cafe/food-centre-relevant places.z;No Google Maps places retrieved. Using synthetic dataset...zProcessing details rI   r  rb   r6  OKr/  zDetail fetch failed: z+Skipping non-F&B place after detail check: formatted_addressr;  z	\b\d{6}\br  r  r<  r7  photosphoto_referencer   $z$$z$$$z$$$$)r   r   r:  r   r   r>  r:  r  z Singapore Roadr8  r  r9  r  formatted_phone_numberir  r=  g      @)r  rb   r  r;  r  r{  rx  r8  r9  r  r  r=  ri  googlePhotoReferenceszOnly zK F&B-relevant merchants found. Filling remainder with synthetic F&B data...)startdetailedMerchants)rm  r  zError in fetch execution: )<r   r  r   rfiler   rS   r   r   rU   
ValueErrorrG   base64	b64decoder   r!  r  r  r  r   r  r  r  r#   r  refresh_imagesr&  r`   r  r   r   getenvr?  r  r   r   extendtimesleepsetaddr   rr  rF  r   r   r   r  r  searchgroupr    r  r   filterisdigitr0  localsrD  r  do_POST)?r  content_length	post_dataparamsr:   	file_databackup_pathworkbook_bytesbackup_filesave_errupload_namerN   fr&  
update_errreference_relative_pathr  errr   r#  r$  r  r   r&  target_countindustry_labelsr  r   search_keyword
all_placesr   pagerY   r:  seenunique_placesr   pidselectedr%  r   rb  detailrespr*   r   r  r  r  postal_matchry  geor>  photo_referencesprice_tiersr>  price_tier_valmissing_countsynthetic_placesr*  response_payload	outer_errr  s?                                                                 r+   rU  zBackendHandler.do_POSTn  s   99))$!$T\\2B%C!D JJOON;	I$4$4W$=>!::j-@"JJz26	 $%GHH1(;!'!1!1)!<+t,%%n5 - ""3'  1CD  "

  "'- " 6'?$ YY--+$!$T\\2B%C!D JJOON;	I$4$4W$=>"JJz26	$jj5NO $%BCC/<	!'!1!1)!<)T*aGGN+ + ABeB'	2JK +DI*N'1)<	""3'  1CD  "

  "!*)@- " 6'?	$ YY,& .>!?@N

7IZZ	 0 0 9:Fjj2.446GNB7==?j299M]_aCbChChCjJNB7==?j299M]_aCbChChCjJ!::j%8Lzz*.>?HFJJz378Ir3vzz-'D#EFL )HU(y(O
 -00uEN)@x)X&J%l^3LXJVYZ[7
|CSTbSccefgU$
!
j/L2,==$(tAvh6GHI27J
Tbc88H-1VV"+.Edhh`d`h`hiq`rFsEt,u"vv"&((9b"9"))'2%)XX.?%@
	)!!8 JJsO j/L2,==$(" u "#A%%
+C$%,,Q/	 $  5(0=$d1AVWXZbAcQM$d7M8J7KKxyz$WX$=lH$UM(,7 &(" )( 3HAu/!uAc(m_BuyyQWGXFYY\]^!FuyyR'@'K'KL'YD#6wj@Q#RD#xx1T9)-(); #)eA#u,5J1h5W KAEERXM?[\ ee$7z29NOG%g.D #%99\7#CL;G,"4"4Q"7SQW[dgh[hlnZnQnMoK%%
B/33JCCUU8UYYx-DEKF "((!'A55!23 /0!'( q	($ '*cdu"PK"#%%q"9K%0__[$%GN&--$)IIj$9 !f=DGMM#$6q$9YYZ]O[jJk$%EE*$5 $",T"2&1"wwuf5"wwuh7klkpkp  rJ  lKQUUC[]_=`)a!bcecf!g  QU#$55#3"#%%#"6%31A/ G !4h  5(S1C-D|-SE#&8"9!:  ;F  G  H$037I3J$JM'@PX'Y$)23C3OaKb)c)c)%))J*CD*11(-		*(=$)IIf$5(-		*VAS@TTc>d(e(k(klo(pqr(s(-		*(=$(&0&6*-f	F8Jb7P.P*Q#(99Z#<#@#@R#P#T#TUZ\b#c#(99Z#<#@#@R#P#T#TUZ\d#e%)'+&+ii#&>-0STePV)W)[)[\a\e\efsuv\wy})~573  *d( 4""*!&  ""3'  1CD  " #6IVX6U!2[m$  

  ,<!=!D!DW!MN GOe -,  $3CM?CD""3'  1CD  "

  %"8}- " 6'?$ $$. +* ! eQRUV`RaQbcdde  $1#c(<=""3'  1CD  "

  %"3x- " 6'?$ $$J %e&  ) D!$9#a&"BCCD"(P  $23y>2BCD""3'  1CD  "

  %"9~- " 6'?$ $$s  B4n  m3A;n  &B4q$ p),q$  p6 B
q$ .C*u &u  -u -t?tB!u %4tC+u )u -M6u 3m=8n   	p&	Bp!!p&)p3.q$ 6	q!?qq$ q!!q$ $	t
-Btt
u 	t=t82u 8t==u 	w+Bw&&w+)__name__
__module____qualname__r  r  r"  rU  __classcell__)r  s   @r+   r  r  I  s    2A Ar=   r  __main__z3Starting Python Backend server at http://localhost:z"Serving files and handling APIs...r/   z
Exiting server.)NNrR  )Cr   rS   rG  urllib.requestr   urllib.parser@  rL  r  http.serverr   r   rI  r   r   r   r   r	   r
   PORTr!  r   r   r   r   rA   rK   rW   rR   r,   r<   rG   rO   r\   r`   r   r   r   r   r  r   r   r   r   r   r  r   rr  r  r  r  r  r	  r  r  r0  rF  r  r  rw  chdirabspathr#   serverserve_foreverKeyboardInterruptserver_closerS  r=   r+   <module>r     s=   	        < C  C WW\\"''//(3_E
ggooh'ww||J0~ [2IJ 9& ^H  *L[df*L[df*L[df*L[df*L[df*L[df*L[df!-O^eg-O^eg$/Q`gi -O^eg!-O^fh-O^fh-L]eg-L]eg-O^hj-L]gi!-L]gi-L]gi-L]gi)	.3#ET]_3#ET]_3 %GV_a3 #ET]_	3
 $FU^`3 #ET]_3 !CR[]3 "DS\^3 #ET]_3 %GV_a3 "DS\^3 %GV_a3 #ET]_3 $FU^`3 "APY[3  %DS\^!3" &HW`b#3$ &HW`b%3& #ET]_'3( &ET]_)3* $FU^`+3, %GV_a-3. &HW`b/30 "APY[132  *L[bd334 'IX_a536 )KZac738 !+JY`b93: $.M\ce;3< ",N]df=3>  *L[bd?3@ ",N]dfA3B !+M\ceC3D #-O^egE3F !+M\ceG3H ",N]dfI3J  *L[bdK3L ",N]dfM3N &HW_aO3P %GV^`Q3R &HW_aS3T %GV^`U3V &HW_aW3X 'IX`bY3Z %GV^`[3\ %GV^`]3^ !+M\fh_3` (JYcea3b  *L[egc3d (JYcee3j 	e	7%
)3y{%
,6qs% -6vx% *3qs	%
 -6z|% *3uw% -6km% -6xz% -6oq% -6jl% -6qs% -6jl% -6df% *3ln% -6ik%  -6gi!%" -6np#%$ -6jl%%& -6jl'%( -6ik)%* -6oq+%, -6qs-%. -6pr/%0 -6oq1%2 -6qs3%4 *3mo5%6 -6oq7%8 -6rt9%: -6km;%< -6mo=%> -6gi?%@ *3hjA%B -7UWd6egC%D -7OQZ6[]E%F -6egG%H -7QS]6^`I%R   'M  WX  `j  k"  ,b  ln  v~     *^  hj  rz  {  !G  QR  Zb  c  &H  RS  [e  f  "P  Z[  cm  n  "I  ST  \f  g  #G  QR  Zd  e   *X  bc  ks  t  ([  ef  nw  x  (S  ]_  gs  t  (U  _a  ip  q  (U  _`  hr  s   *X  bd  ls  t  $B  LM  U_  `$ %u  @B  JP  Q${  FH  PV  W!x  CE  MR  S$  .C  MO  We  f#z  EG  OU  V$w  BD  LU  V"lvx  AJ  K$oy{  DM  N&s}  HO  P!hrt  }C  D'{  FH  PY  Z!dno  xB  C gqr  {E  F&q{}  FO  P&r|~  GP  Q%# J	*0	6B6, \6,*G6G<<3|e$?YB DSrf- fP	 zBHHRWW__RWW__X678	?v
FG	
./T
N3F   !"s   7U	 	U*)U*