
    0j              	          d 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Zddl	Z	ddl
mZmZ ddlmZmZ ddlmZ ddlmZ ddlZddlZej.                  j1                  ej.                  j3                  e            Zej.                  j9                  ed      Zej.                  j=                  e      r ee      5 Z e D ]y  Z!e!jE                         Z!e!sd	e!v se!jG                  d
      r-e!jI                  d	d      \  Z%Z&ejN                  jQ                  e%jE                         e&jE                                { 	 ddd        ed      Z)e)jU                  d eej.                  j9                  ed            d       e)jU                  d eej.                  j9                  ed            d        eej.                  j9                  ed            Z+ejN                  jY                  dd      Z-dZ.i Z/dZ0dZ1dede2fdZ3 eej.                  j9                  ed      d      5 Z4 ejj                  e4      Z6ddd       d Z7e)jY                  d!e"      defd#       Z8e)jY                  d$      d%        Z9e)ju                  d&      defd'       Z;e)ju                  d(      defd)       Z<e)ju                  d*      defd+       Z=e)ju                  d,      defd-       Z>e)ju                  d.      defd/       Z?e)ju                  d0      defd1       Z@i ZAi ZBdGd2e2d3e2fd4ZCe)ju                  d5      defd6       ZDe)ju                  d7      defd8       ZEe)ju                  d9      defd:       ZFe)ju                  d;      defd<       ZGe)ju                  d=      defd>       ZHe)ju                  d?      defd@       ZIeJdAk(  r ej                  dBdCdDdEF       yy# 1 sw Y   xY w# 1 sw Y   xY w)Hu?   QuitSure Chatbot Onboarding — MVP Server with LLM Integration    N)date	timedelta)FastAPIRequest)HTMLResponseJSONResponse)StaticFiles)Jinja2Templatesz.env=#   zQuitSure Chatbot Onboarding)titlez/chatbot-onboarding/staticstatic)	directoryz	cb-static)namez/static	templatesGEMINI_API_KEY zXhttps://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent   <   requestreturnc                     | j                   j                  dd      }|r"|j                  d      d   j                         S | j                  r| j                  j
                  S dS )u   Real client IP. Behind a reverse proxy (nginx), request.client.host is the
    proxy's IP — shared by every user — so honour X-Forwarded-For first.zx-forwarded-forr   ,r   unknown)headersgetsplitstripclienthost)r   xffs     2/var/www/python/quitsure-chatbot-onboarding/app.py
_client_ipr$   +   sU     //

/
4C
yy~a &&((")..7>>?i?    zstatic/facts.jsonzutf-8)encodingu:  You are QuitSure's quit coach, a friendly AI coach who helps people understand their smoking/vaping patterns. You talk like a real person texting a friend, not a therapist writing a report. Your users are everyday smokers from Google/Meta ads. Keep it simple, warm, real.

## YOUR ROLE
You're running a structured quiz. The app controls what question comes next. You do NOT ask the next question. Your job is to:
1. Acknowledge what the user just said. Make them feel heard, not judged.
2. Connect their answer to something they said earlier when possible. This is what makes it feel like a real conversation.
3. Keep it short and human.

## YOUR VOICE
- Talk like a smart friend, not a doctor. Casual but caring.
- Simple words only. NEVER use: "neuroplasticity", "dopamine", "cortisol", "cognitive dissonance", "neural pathways", "cessation".
- NEVER use em-dashes. Use periods, commas, or "..." instead. Em-dashes make it sound AI-generated.
- NEVER start with: "Thanks", "Thank you", "Got it", "Great", "I appreciate", "Wow", "That's really interesting", "That's a great answer". Start with the actual insight.
- NEVER say: "us", "we", "our" (you're one person), "it sounds like" (lazy filler), "that's really" (padding), "I hear you" (canned empathy).
- NEVER pad with phrases like "and it sounds like smoking is a big part of..." — just say the sharp thing directly.
- STRICT: 1-2 sentences max. If you wrote 3, delete the last one. This is a chat, not an essay.
- Don't pitch the app. Don't mention subscriptions, pricing, or features unless explicitly asked.
- Sound like you genuinely care, not like you're reading a script.
- Use the user's name SPARINGLY — max 3-4 times across the entire conversation. Every message starting with their name feels robotic. Skip the name in most responses. Only use it at key emotional moments.

## HARD GUARDRAILS — NEVER BREAK THESE
1. NEVER invent statistics, studies, or success rates. Use ONLY the numbers in the fact library. If asked where a stat comes from, say "I don't have the specific source to hand" — NEVER fabricate a source like "internal data" or "our research shows".
2. NEVER give medical advice. If user mentions serious medical issues (heart attack, COPD diagnosis, pregnancy, mental health crisis), acknowledge it warmly but redirect: "That's really important — please talk to your doctor about that. For now, let's focus on understanding your smoking."
3. NEVER diagnose or label the user. Don't say "you have an addiction disorder" or "you're clinically dependent." Use the framing: "your brain has learned patterns."
4. NEVER describe ANY aspect of the program methodology, even vaguely. Don't say "it teaches you to handle triggers" or "it changes your relationship with smoking" or "you no longer want to smoke" or "reshape your thinking patterns." Just say "the program addresses this specifically" and stop. No hints about what's inside or what the outcome feels like.
5. NEVER answer off-topic questions. If user asks about politics, other apps, unrelated topics, gently redirect: "Good question — but let's stay focused on you right now. [next thing]."
6. NEVER use shame, fear tactics, or guilt. "Smoking kills you" is forbidden. Frame everything around self-awareness and possibility.
7. NEVER ask them another question. The app does that. Your response should NOT end with a question.
8. NEVER say "I understand how you feel" or similar canned empathy. Be specific to what they said.
9. NEVER promise a specific outcome ("you WILL quit in 6 days"). Use "can" and "most people find."
11. NEVER fabricate precision from range data. If the user data says years_smoking=7 but years_smoking_label="5-10 years", say "5-10 years" NOT "7 years". Same for cigs_per_day — use the _label field if available. The numeric values are midpoints for calculations, the labels are what the user actually said.
10. If a user types something concerning (suicide, self-harm, abuse, violence, death, being hurt/beaten), respond ONLY with: "What you're sharing sounds really serious, and it goes beyond what I can help with here. Please reach out to someone who can support you — findahelpline.com has free, confidential help. I'm here for the quit journey whenever you're ready." Do NOT engage with the details, do NOT repeat what they said, do NOT offer smoking-related advice in this context.
12. If the user types gibberish, random characters, or nonsense (e.g. "hjl", "asdf", "lol ok", single letters), respond: "Hmm, I didn't quite catch that. Could you try again?" Do NOT affirm or interpret gibberish as a real answer.
13. If the user types something clearly trolling or absurd (e.g. "my dad forces me to smoke", "aliens made me", obviously fake scenarios), don't take it at face value or get drawn in. Respond briefly and neutrally: "That's an unusual one. Let's keep going." Do NOT build emotional responses around troll answers.

## APPROVED STATISTICS — ONLY use these numbers. Never invent or round.
- 80.1% success rate among program completers (JMIR Human Factors, 2024, n=1,286)
- 85% of successful quitters reported no withdrawal symptoms after 30 days (JMIR, 2024)
- 3,000,000+ downloads globally
- Available in 175+ countries
- Program duration: 6 days
- Methodology: CBT + REBT + guided self-hypnosis
- Pricing: Do not quote exact amounts. Say "you'll see pricing on the next screen"
- 100% money-back guarantee if you complete the program and still smoke
- Developed with Stanford scientists
- Published in JMIR (peer-reviewed medical journal)
- Featured in Google Play's "Made in India" collection
- Celebrity endorsements: Arshad Warsi (Bollywood actor) publicly credited QuitSure
- Cold turkey success rate: less than 5% (PMC)
- NRT (patches/gums) success rate: 6-8% (WHO, 2021)
- Prescription medication success rate: 7-14% (WHO/PubMed)
- Hypnosis success rate: 14-25% (Cochrane Library)
- Physical addiction = ~20% of the problem. Psychological = ~80%. Physical withdrawal resolves in 1-2 weeks. Psychological can persist for years.

## TONE CALIBRATION
- If user is emotional: acknowledge it specifically, briefly.
- If user is skeptical: don't argue. Meet them there: "Fair. A lot of people feel that way at first."
- If user is enthusiastic: match the energy, stay grounded.
- If user is confused: simplify, don't over-explain.

## FACT LIBRARY — only source of truth for claims
{facts}

## USER PROFILE SO FAR
{user_data}

## CURRENT STAGE
{stage_hint}
/)response_classc                 6   K   t         j                  | d      S w)Nz
index.html)r   TemplateResponse)r   s    r#   indexr+   ~   s     %%g|<<s   z/api/configc                  R   K   t         j                  j                  dd      } d| iS w)u7   Return subscription URL from .env — no Jinja2 needed.SUBSCRIPTION_URLzghttps://tprogram.quitsure.app/v3/eng/direct?qs=cHJvZ3JhbT0zJmxpbmstc291cmNlPWNoYXRib3Qtb25ib2FyZGluZw==subscription_url)osenvironr   )sub_urls    r#   
get_configr2      s-      jjnnqG ((s   %'z	/api/chatc           
        K   t        |       }t        j                         }t        j                  |d|f      }||d   z
  t        kD  rd|ft        |<   n0|d   t
        k\  rt        dddd      S |d   dz   |d   ft        |<   	 | j                          d{   }|j                  d
d      }|j                  di       }|j                  dd      }|j                  dd      }|j                  dg       }	t        j                  t        j                  t        d      dd |rt        j                  |d      nd|xs d      }
d}|	dd D ]5  }|j                  d      dk(  rdnd}|| d|j                  dd       dz  }7 |
 d| d|xs d  d!| d"}d}t        j                  d#$      4 d{   }	 |j                  t         d%t"        d&d'd(|igigd)d*d+did,d-.       d{   }|j$                  d/k(  r`|j                         }|j                  d0g       }|r<|d   j                  di       j                  d'g       }|r|d   j                  d(d      }ddd      d{    |r~|j)                         }|j+                  d2      r|j-                  d2      r|dd3 }d4D ]B  }|j/                         j+                  |      s#|t1        |      d j)                         } d5|iS  d5|iS 7 \# t        $ r t        dddd	      cY S w xY w7 a7 -# t        $ r}t'        d1|        Y d}~d}~ww xY w7 # 1 d{  7  sw Y   xY ww)6z/Send user message to LLM, get coach's response.r   r   Nrate limitedreplyerror  status_codeinvalid JSON  messager   userDatacontextstageconversation   )indenti  zNo data collected yetzAssessment in progress.)facts	user_data
stage_hintirole	assistantCoachUser: content
z

--- CONVERSATION SO FAR ---
z
--- CONTEXT ---
zRespond naturally.z:. Keep it to 1-3 short sentences.

--- USER'S MESSAGE ---
z

Respond as Coach:g      9@timeoutapplication/jsonzContent-Typezx-goog-api-keypartstextgffffff?,  thinkingBudgettemperaturemaxOutputTokensthinkingConfigcontentsgenerationConfigr   json   
candidateszGemini failed: "zcoach:zquira:zquincy:zquitsure coach:r6   )r$   time_rate_limitr   RATE_LIMIT_WINDOWRATE_LIMIT_MAXr   r^   	ExceptionSYSTEM_PROMPTformatdumpsFACTShttpxAsyncClientpost
GEMINI_URL
GEMINI_KEYr:   printr   
startswithendswithlowerlen)r   ipnowentrybodyuser_messagerE   r?   r@   rA   system	conv_textmsgrG   full_promptr6   r    respdatar`   rR   eprefixs                          r#   chatr      s     
G	B
))+COOBC)E
U1X~))c(B8~%$ HVYZZ 8a<q2BW\\^# 88Ir*LR(Ihhy"%GHHWb!E88NB/L !!jjq)%405>$**Yq1D[55 " F ICD! ;''&/[8wfvR	2 67r::	;
 h      ! "  
K E  . ) )&	));zZ")V[,A+B!C D'*+.+;Q*?) %  D 3&yy{!XXlB7
&qM--i<@@"ME %aVR 8)) )6 C U^^C%8!BKEH 	F{{}''/c&kl+113U	
 UU $ Wd^DRUVVWH)&  	)OA3'((	)+) ) ) )s   BMK8 K5K8 DM6L7M:M<1L-L.A3L!M,M-A!M&M5K8 8LMLML	L>&L94M9L>>MMM	M
MMz/api/extract-namec           
        K   	 | j                          d{   }|j                  dd      j	                         }|sddiS d| d}d}t        j                  d	
      4 d{   }	 |j                  t        dt        ddd|igigddddidd       d{   }|j                  dk(  r|j                         }|j                  dg       }|rh|d   j                  di       j                  dg       }	|	rA|	d   j                  dd      j	                         j	                  d      j	                  d      }ddd      d{    |r3|j                         dk(  s t        |      dkD  sd|j	                         v r;|r5|j                         dk7  r"t        |      dk  r|j                         d   }nddiS |j	                         j                         }d|iS 7 # t        $ r t        ddid      cY S w xY w7 7 f# t        $ r}
t        d|
        Y d}
~
d}
~
ww xY w7 # 1 d{  7  sw Y   xY ww)z>Use LLM to extract a person's first name from free-text input.Nr   r<   r9   rawr   ur  Extract ONLY the person's first name from this input. The input may be in ANY language (Hindi, Spanish, French, etc). Examples: 'mera naam Neel hai' -> Neel, 'je m'appelle Pierre' -> Pierre, 'my name is Sarah' -> Sarah, 'Rahul' -> Rahul. Reply with JUST the name — no quotes, no punctuation, no explanation. If you can't find a name, reply with exactly: NONE

Input: "z"

Name:g       @rN   rP   rQ   rR   rS              rU   r   rV   rZ   r]   r_   r`   rL   ra   'zName extraction LLM failed: NONE2    )r^   rh   r   r   r   rm   rn   ro   rp   rq   r:   rr   upperrv   r   
capitalize)r   rz   r   promptr   r    r   r   r`   rR   r   s              r#   extract_namer      sF    =\\^# ((5"

#
#
%C~	
 5	%  D  - 6 6	6);zZ")VV,<+=!> ?'*+-+;Q*?) %  D 3&yy{!XXlB7
&qM--i<@@"ME$Qx||FB7==?EEcJPPQTU)6 62 4::<6)SY^sdjjl?RDJJLF*s4yB::<?DD>! ::<""$DD>i $ =VTN<<="6&  	60455	6+6 6 6 6s   H?G  GG  AH?$G>%H?(H**1HHBH;H?H(BH?G   G;8H?:G;;H?H	H%H H* H%%H*(H?*H<0H31H<8H?z/api/ask-quincyc           
         K   t        |       }t        j                         }t        j                  |d|f      }||d   z
  t        kD  rd|ft        |<   n0|d   t
        k\  rt        dddd      S |d   dz   |d   ft        |<   	 | j                          d{   }|j                  d
d      j                         dd }|j                  di       }|sddiS t        j                  dd|      j                         }|sddiS t        j                  dd|j                  dd            }t        |j                  dd            }|j                  dd      }	t        j                  dd|	      dd j                         xs d}
d| d|rdnd }d|
 d| d|
 d| d	}d}t        j                  d !      4 d{   }	 |j                  t         d"t"        d#d$d%|igigd&d'd(did)d*+       d{   }|j$                  d'k(  rn|j                         }|j                  d,g       }|rJ|d   j                  d-i       j                  d$g       }|r#|d   j                  d%d      j                         }ddd      d{    |r|j                         }|j)                  d/      r|j+                  d/      r|dd0 }d1D ]?  }|j-                         j)                  |      s#|t/        |      d j                         } n t        j                  d2d|      }d|iS 7 # t        $ r t        ddid	      cY S w xY w7 7 N# t        $ r}t'        d.|        Y d}~d}~ww xY w7 # 1 d{  7  sw Y   xY ww)3zHAnswer user questions about QuitSure program, grounded in real FAQ data.r   r   Nr4   r5   r8   r9   r6   r<   questionr   i  r>   z\(?i)(ignore|forget|disregard)\s+(all|previous|above|prior)\s+(instructions?|rules?|prompts?)z[^a-z_]smoker_typeisVaperFr   Friend[^a-zA-Z\s'\-]r   za r   vapersmokerz*You are QuitSure's quit coach, talking to z (a  ). They just saw their assessment results and have a question. Answer like a knowledgeable friend. Start with the answer, not a greeting.

PROGRAM DETAILS:
- QuitSure is a 6-day app-based program. Daily sessions of 45-60 min. You quit on Day 6.
- It works by reshaping your thinking patterns around smoking. By the end, you don't just stop, you no longer want to.
- You keep smoking during the program until quit day. No cold turkey. No white-knuckling it on Day 1.
- No patches, no medication, no willpower needed.
- Works for cigarettes, vapes, e-cigarettes, chewing tobacco. Whether you smoke 5 a day or 40.

WHAT A SESSION LOOKS LIKE:
- Each day has short videos, reading materials, and guided mental exercises.
- Day 1-2: Understanding your personal smoking psychology (why YOU smoke, not why people in general smoke).
- Day 3-4: Rewiring beliefs about cigarettes. This is where most people start to feel a shift.
- Day 5: Preparing for your final cigarette. By now, most people are surprised how ready they feel.
- Day 6: Quit day. Not because you're forcing it, but because the desire has already changed.
- After Day 6: Relapse prevention toolkit with continued exercises to stay free.

COACHING:
- Every subscriber gets a personal 1-on-1 human coach (not a bot, not this quiz).
- You can message your coach anytime through the app. They respond within hours.
- Coaches help with doubts, tough moments, and personalized guidance.
- You (this quiz coach) are just the assessment coach. The program coach is a separate real person.

COMMUNITY:
- A private space inside the app where people share their quit journey.
- People post wins ("Day 10 smoke-free!"), struggles, and support each other.
- It's like a private group of people all going through the same thing. Not a public forum.

PRICING:
- The pricing shows on the very next screen with an exclusive web discount already applied.
- For most smokers, the daily cost works out to less than what they spend on cigarettes in a day.
- Don't quote exact rupee/dollar amounts. Just say "you'll see the pricing on the next screen with a special discount."
- 100% money-back guarantee: complete the full program and if you're still smoking, full refund, no questions asked.
- Downloading the app and signing up is free. You only pay when you choose to subscribe.
- The subscription is monthly because the journey includes post-quit support beyond the 6 days.

RESULTS:
- 3 million+ users across 175+ countries.
- 80.1% success rate among program completers (peer-reviewed, JMIR Human Factors 2024, n=1,286). 85% of successful quitters reported no withdrawal symptoms after 30 days.
- The founder smoked 20 cigarettes a day for 17 years, tried quitting 30 times, then built QuitSure after figuring out why everything else fails.

COMMON CONCERNS:
- Weight gain: The program doesn't involve restriction or diet changes. Most people don't gain weight because the approach removes cravings rather than suppressing them.
- Cravings: 80.1% success rate among program completers, and 85% reported no withdrawal symptoms after 30 days. The program changes the desire before quit day, so there's nothing to fight.
- Failing again: Past methods failed because they target the physical nicotine (10% of the problem). This targets the psychological 90%.
- Stress: Nicotine withdrawal actually creates stress. Smoking "relieves" that withdrawal, not the actual stress. Breaking this loop is a core part of the program.
- Social situations: The program doesn't ask you to avoid friends or situations. It removes the desire so you can be in the same places without wanting a cigarette.

RULES:
- 2-3 sentences max. Be direct.
- NEVER start with "That's a great question", "Great question", "Hey aq  ", or any greeting. Start with the answer.
- NEVER say "our", "we", "us" (you're one person, not a company).
- NEVER say "I know seeing those results", "I totally get", "I completely understand" (filler).
- NEVER use em-dashes. Use periods or commas.
- NEVER use jargon (no "CBT", "neuroplasticity", "cognitive behavioral").
- NEVER guarantee they will quit. Use "most people find" or "80.1% success rate among completers".
- If you genuinely don't know: "Your personal coach will cover that in detail once you start. Zero risk with the money-back guarantee."

IF ASKED IF QUINCY IS A DOCTOR/REAL PERSON/AI:
"I'm not a doctor. I'm a quit coach who specializes in the psychology behind smoking. For medical advice, always talk to your doctor. For understanding why you smoke and how to stop, that's where I can help."

IF USER SAYS IT'S TOO EXPENSIVE / A SCAM / MAKING MONEY FROM THEIR DESPERATION:
Don't be defensive. Be empathetic. Say something like: "I get it. Nobody wants to pay for something they're not sure about. That's exactly why there's a 100% money-back guarantee. You literally can't lose money on this."

USER'S QUESTION: z

Answer directly:      (@rN   rP   rQ   rR   rS   g      ?r_   rU   rV   rZ   r]   r`   rL   zFAQ LLM failed: ra   rb   rc   z<[^>]*>)r$   rd   re   r   rf   rg   r   r^   rh   r   resubboolrm   rn   ro   rp   rq   r:   rr   rs   rt   ru   rv   )r   rw   rx   ry   rz   r   rE   r   is_vaperraw_namer   
user_label
faq_promptr6   r    r   r   r`   rR   r   r   s                        r#   ask_quincy_faqr   $  s     
G	B
))+COOBC)E
U1X~))c(B8~%$ HVYZZ 8a<q2B>\\^# xx
B'--/5HR(I vvuwy  |D  E  K  K  MH&&R}b)IJKIMM)U34H}}VX.H66#R23B7==?K8Dk]!xGX#FGJ?vR
| 2TFd GKV L  ABJH E  . * *&	*);zZ")VZ,@+A!B C'*+.+;Q*?) %  D 3&yy{!XXlB7
&qM--i<@@"ME %aVR 8 > > @)* *0 C U^^C%8!BKEH 	F{{}''/c&kl+113	
 z2u-U{ $ >WdO==>r*&  	*$QC())	*+* * * *s   BM>L LL DM>;L=<M>?M)1M2M 3BM4M>?M' A!M>":M>L L:7M>9L::M> M	M$MM)M$$M)'M>)M;/M20M;7M>z/api/classify-typec           
        K   	 | j                          d{   }t        j                  dd|j                  dd            dd j                         xs d	}t        |j                  d
d            }|rdnd}dddddddd}ddddddddd}|j                  dg       }|D cg c]  }|j                  ||       }	}|j                  d d!      }
|j                  |
|
      }dj                  g d"| d#| d$| d%|j                  d&d!       d'|j                  d(d!       d)| d%d*j                  |j                  d+g             xs d, d-d*j                  |j                  d.g             xs d, d/|j                  d0d!       d1|j                  d2d!       d3| d4|j                  d5d!       d6d*j                  |j                  d7g             xs d8 d9d*j                  |j                  d:g             xs d; d<|j                  d=d>       d?d*j                  |j                  d@g             xs dA dBd*j                  |j                  dCg             xs d; dD| d%d*j                  |	      xs d! dE| dF      }dG| dH}d}t        j                  dIJ      4 d{   }	 |j                  t        dKt        dLdMdN|igigdOdPdQd>idRdST       d{   }|j                  dUk(  r|j                         }|j                  dVg       }|r|d>   j                  dWi       j                  dMg       }|r|d>   j                  dNd      j                         }t        j                  dXd|      }t        j                  dYd|      }d>dl }|j                  |      }h dZ}|j                  d[      |v r|j                  d\      |v r|}ddd      d{    d^|iS 7 # t        $ r t        ddid      cY S w xY wc c}w 7 b7 .# t        $ r}t!        d]|        Y d}~\d}~ww xY w7 X# 1 d{  7  sw Y   d^|iS xY ww)_z:Use LLM to classify smoker type based on all quiz answers.Ntyper<   r9   r   r   r   r   rJ   r   Fvapingsmokingscared of cravingsneeds it for stressit's part of who they areit's just automaticthey actually enjoy itthey've given up tryingthey don't knowwithdrawal_fearstress_needidentity	automatic	enjoymentgiven_upr   cravingsgaining weightlosing stress outletfailing again#not knowing who they are without itmissing the social sidenot feeling readynothing
withdrawalweightstress
fail_againr   social_miss	not_readyreadydeeper_whysbiggest_fearr   
Name: 
Habit: 
Years rK   years_smoking_label

Per day: cigs_per_day_label
What they like about , likes_about_smokingnothing specified
What they dislike: dislikes_about_smokingz'
How they feel before reaching for it: feeling_before&
How often they think about stopping: thinks_about_stopping
First  after waking: first_cig_minutes minutes
Triggers: triggersnone specified
Denial patterns selected: denial_patternsnone
Past quit attempts: past_attempts_countr   z
Relapse triggers: relapse_triggerszn/a
Methods tried: methods_tried
Why they keep 
Biggest fear about quitting: rM   u  You are a smoking cessation psychologist. Based on this person's quiz answers, classify them into EXACTLY ONE of these 6 smoker types. Pick the type that BEST fits their overall pattern — not just one answer.

THE 6 TYPES:
1. stress — Reaches for nicotine when pressure mounts. Smoking/vaping is their stress relief valve. Key signals: anxious before smoking, stress triggers, "it relaxes me", needs it for stress.
2. social — Smokes/vapes mainly in social situations. Key signals: social + alcohol triggers, likes the social side, occasional/light usage, few morning triggers.
3. habitual — Smoking/vaping is woven into daily routine on autopilot. Key signals: morning+meals+coffee triggers, "it's automatic", many triggers (4+), doesn't think about it.
4. emotional — Uses nicotine to process feelings (sadness, anger, loneliness). Key signals: feels anxious/bored before smoking, guilt/example in dislikes, emotional deeper-why.
5. reward — Smokes/vapes as a treat or "me moment" after accomplishments. Key signals: "it's my reward", "I genuinely enjoy it", happy before smoking, break-taking.
6. identity — Smoking/vaping is part of who they are. Key signals: 10+ years smoking, "it's part of who I am", can't imagine life without it.

QUIZ ANSWERS:
ux  

Respond with ONLY valid JSON in this exact format — no markdown, no explanation:
{"primary": "type_name", "secondary": "type_name", "explanation": "2-3 sentences explaining why this person is this type. Reference what they SAID (their actual words like 'it helps me calm up' or 'I love the ritual'), NOT internal field names like 'handles_stress' or 'stress_event'. NEVER use words like 'handles_stress', 'stress_event', 'identity', 'withdrawal_fear' — these are internal codes. Use the person's ACTUAL WORDS instead. Write as the quit coach speaking directly to them. No em-dashes."}

RULES:
- primary and secondary MUST be different
- primary and secondary MUST be one of: stress, social, habitual, emotional, reward, identity
- explanation should reference THEIR specific answers, not generic descriptions
- Do NOT default to stress — really look at what makes this person tickr   rN   rP   rQ   rR   rS   g333333?rT   rU   rV   rZ   r]   r_   r`   rL   z^```json\s*z\s*```$>   rewardsocialr   habitualr   	emotionalprimary	secondaryzLLM classify failed: result)r^   rh   r   r   r   r   r   r   joinrm   rn   ro   rp   rq   r:   loadsrr   )r   dr   r   
habit_word
why_labelsfear_labelsdeeper_whys_rawwdeeper_whys_readablebiggest_fear_rawbiggest_fear_readableanswersr   r   r    r   r   r`   rR   r   json_modparsedvalid_typesr   s                            r#   classify_smoker_typer     s    =,,.  66#Rvr):;CR@FFHRFDAEE)U+,H%9J &:J_  nI  Xm  |T  b{  HY  ZJ!+7GSi  zI  W|  Mf  uH  S\  ]KeeM2.O:IJQJNN1a0JJuu^Y7'OO,<>NO   f| l  UU0)<=>
 %%$i
0	1	2	
 !\
 "$
 %)IIaee4I2.N$O$fSf#g
h IIaee$<bABYFYZ[' ()uu-=y'I&JK& '(ee,CY&O%PQ l " #$%%(;Y"G!HI 99QUU:r*+?/?
@A  99QUU+<b%ABLfMN UU0!456 99QUU#5r:;DuEF 		!%%45?@A l  "YY';<I	JK  44! 5!G& 		 	
JMF, F  . / /&	/);zZ")VV,<+=!> ?'*+.+;Q*?) %  D 3&yy{!XXlB7
&qM--i<@@"ME#Ahll626<<> ff^R= ffZS9/!)!4&i!::i0K?FJJ{D[_jDj%+F9/ /@ fw ! =VTN<<= K^/6  	/)!-..	/;/ / / /@ fs   Q'P  O=P  BQ'$P=HQ'P#Q'Q1P)8P&9C0P))Q'4Q5Q'=P   PQ'P	Q'&P))	Q
2Q QQ

QQ'Q$QQ$
Q'z/api/personal-letterc           
      	  K   	 | j                          d{   }t        j                  dd|j                  dd            dd j                         xs d	}t        |j                  d
d            }|rdnd}|j                  dd      }t        j                  di       j                  |i       }|j                  dd      }dddddddd}dddddddd d!}	|j                  d"g       D 
cg c]  }
|j                  |
|
       }}
|	j                  |j                  d#d$      |j                  d#d$            }dj                  g d%| d&| d'| d(|j                  d)d$       d*|j                  d+|j                  d,d$             d-| d(d.j                  |j                  d/g             xs d0 d1d.j                  |j                  d2g             xs d0 d3|j                  d4d$       d5| d6|j                  d7d$       d8d.j                  |j                  d9g             xs d: d;d.j                  |j                  d<g             xs d= d>|j                  d?d@       dAd.j                  |j                  dBg             xs d= dC| d(d.j                  |      xs d$ dD| dEd.j                  |j                  dFg             xs d$ dG|j                  dHdI       dJ| dK      }dL| dM| dN| dO| dP	}d}t        j                  dQR      4 d{   }	 |j                  t        dSt        dTdUdV|igigdWdXdYd@idZd[\       d{   }|j                  d]k(  r|j                         }|j                  d^g       }|rc|d@   j                  d_i       j                  dUg       }|r<|d@   j                  dVd      j                         }t        j                  d`da|      }|}ddd      d{    d|iS 7 # t        $ r t        ddid      cY S w xY wc c}
w 7 7 # t        $ r}t!        db|        Y d}~[d}~ww xY w7 W# 1 d{  7  sw Y   d|iS xY ww)czDGenerate a personalized assessment letter based on all quiz answers.Nletterr<   r9   r   r   r   r   r   r   Fr   r   r   smoker_typesr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rK   r   r   r   cigs_per_dayr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   z
Smoking gets in the way of: core_valuesz
Future vision (smoke-free): future_visionz
not sharedz
Classified as: rM   z<You are QuitSure's quit coach, writing a personal letter to z after their smoking assessment. You've spent the last 5 minutes learning about them. Now write them a letter that makes them feel truly SEEN.

THEIR ANSWERS:
aB  

WRITE A LETTER with these 4 sections (use these exact headers):

**Your Pattern**
2-3 sentences about WHY they smoke. Reference their SPECIFIC answers. Don't say "you're a stress smoker." Instead say something like "When things get tough at work, you reach for a cigarette. Not because you want one, but because your brain has learned that's what relief feels like." Make it personal.

**What's Really Going On**
2-3 sentences about the psychological mechanism behind their pattern. This is the "hidden truth" moment. Connect their triggers, their likes, and their deeper-why into one insight. Keep it simple. No jargon.

**Why Nothing Else Worked**
2-3 sentences connecting the methods they tried to why those methods failed. If they've never tried, talk about why willpower alone wouldn't work for their pattern. Reference the 10%/90% split (physical vs psychological).

**What Would Actually Work For You**
2-3 sentences about what approach fits their specific pattern. Describe the approach first, then end with: "That's exactly what QuitSure is designed to do." Make QuitSure feel like the natural conclusion, not a pitch.

RULES:
- Talk like a smart friend writing a thoughtful text, not a therapist writing a report
- Reference THEIR specific answers (triggers, what smoking gets in the way of, fears, years)
- NEVER use em-dashes. Use periods or commas.
- Keep each section to 2-3 sentences MAX
- NEVER use jargon (no "neuroplasticity", "cognitive", "cessation")
- NEVER use bullet points or numbered lists
- Sound like you genuinely care about this specific person
- Do NOT start with "Dear z
" or "Hey uw   " — jump straight into the first section header
- Do NOT end with "Sincerely" or any sign-off — the UI handles thatg      4@rN   rP   rQ   rR   rS   g333333?iX  rU   rV   rZ   r]   r_   r`   rL   z\*\*(.+?)\*\*z<strong>\1</strong>zPersonal letter LLM failed: )r^   rh   r   r   r   r   r   r   rl   r   rm   rn   ro   rp   rq   r:   rr   )r   r   r   r   r   r   type_details	type_namer   r   r   r   r   r   r   r   r    r   r   r`   rR   r   r   s                          r#   personal_letterr    s$    ?,,.  66#Rvr):;CR@FFHTHDAEE)U+,H%9J%%r*K99^R044["EL  2I%9J_  nI  Xm  |T  b{  HY  ZJ!+7GSi  zI  W|  Mf  uH  S\  ]K:;%%r:RSQJNN1a0SS'OOAEE.),LaeeTbdmNno   f| l  UU0)<=>
 %%$aeeNI&F
G	H	I	
 !\
 "$
 %)IIaee4I2.N$O$fSf#g
h IIaee$<bABYFYZ[& '(ee,CY&O%PQ l " #$%%(;Y"G!HI 99QUU:r*+?/?
@A  99QUU+<b%ABLfMN UU0!456 		!%%45?@A l  "YY';<I	JK 445 "YYquu]B'?@MINO  UU?LAB! C!" {#"#G( NdV T 		 
0  &
4& 1F7IF< F  . 6 6&	6);zZ")VV,<+=!> ?'*+.+;Q*?) %  D 3&yy{!XXlB7
&qM--i<@@"ME#Ahll626<<> ff%57MsS!$/6 66 f} ! ?Xt,#>>? Tl6,  	60455	616 6 6 66 fs   RP PP CR,P<H0R5Q6R9Q,;1Q,Q-BQRQ*RP P96R8P99	RQ	Q'Q"Q,"Q''Q,*R,R2Q53R:
Rz/api/profilec                 ~  $K   	 | j                          d{   }dRd}dSd}|j                  d	g       xs g }|j                  d
g       xs g }|j                  dg       xs g }|j                  dd      }|j                  dd      }t	        t        ||z               }	|dk(  r|	j                  d       n[|dk(  r|	j                  d       nD|dk(  r|	j                  d       n-|dk(  r|	j                  d       n|dk(  r|	j                  d       |dk(  r|	j                  d       nJ|dk(  rnD|dk(  r|	j                  d       n-|dk(  r|	j                  d       n|dk(  r|	j                  d       |j                  dg       xs g }
d|
v r|	j                  d       d|
v r|	j                  d       t	        t        |	            }	 ||j                  d       d      }|j                  d!       ||j                  d!      d"      nd"}|d#k\  rd$n|d"k\  rd%nd}|d#k\  rd&n|d"k\  rd'nd}t        |      }t        |	D cg c]
  }|d(v rd$nd c}      t        |D cg c]  }|dk(  rd$nd c}      z   t        |D cg c]
  }|d)v rd%nd c}      z   |z   t        |	D cg c]  }|d*k(  rd$nd c}      t        |D cg c]
  }|d+v rd$nd c}      z   |z   t        |	D cg c]  }|dk(  rd$nd c}      t        |D cg c]
  }|d,v rd%nd c}      z   t        |	D cg c]  }|d-k(  rd%nd c}      z   |d.k\  rd$ndz   |z   t        |	D cg c]  }|dk(  rd$nd c}      t        |	D cg c]  }|dk(  rd%nd c}      z   t        |	D cg c]
  }|d/v rd$nd c}      |r|d#k\  rd0nd|r|d"k\  rd$ndz   d1}|j                         D ci c]  \  }}|t        d|       }}}|rt        |j                               nd}|dk(  rd2}nEg d3}|j                         D cg c]  \  }}||k(  s| c}}$t        $fd4|D        $d         } ||j                  d5      d6      }|j                  d7      xs d}|s|j                  d8d      }|d9v rd:}n|d;v rd<} ||j                  d=      d      }|j                  d>       ||j                  d>      d?      nd?}d@}|dAk\  r|dBz  }n|d6k\  r|d"z  }n|d6z  }|r
|dCk7  r|d"z  }|dk(  r|d6z  }n
|d.k\  r|d6z  }|d"k  r|d6z  }n
|dBkD  r|d6z  }|d?k\  r|d6z  }n
|d6k  r|d"z  }t        dDt        dE|            }dF}dG}t        j                         t!        |H      z   j#                  dI      } ||j                  dJ      d      } | dkD  r| dKz  }!n ||j                  dL      d      }"||"z  }!t%        |!d?z        t%        |!dMz        t%        |!dMz  d6z        dN}#|t&        j                  dOi       j                  |i       ||t&        j                  dPi       j                  |i       |||#dQS 7 A# t        $ r t        ddid      cY S w xY wc c}w c c}w c c}w c c}w c c}w c c}w c c}w c c}w c c}w c c}w c c}w c c}}w c c}}w w)Tu   Server-side scoring: smoker type, success probability, program routing.

    Deterministic fallback — used when LLM classification fails.Nr7   r;   r<   r9   r   c                 N    | |S t        |       S # t        t        f$ r |cY S w xY wN)int
ValueError	TypeErrorvaldefaults     r#   safe_intzbuild_profile.<locals>.safe_int  s(    ;wH_I&6w6   
 $$c                 N    | |S t        |       S # t        t        f$ r |cY S w xY wr  )floatr	  r
  r  s     r#   
safe_floatz!build_profile.<locals>.safe_float  s)    ;w#JI&6w6r  r   r   	why_smoker   r   
deeper_whyanxiousr   boredboredomr   happyr   relaxedenjoyr   r   r   r   r   guiltangryexampleyears_smokingr   
      rB   r   rb   )r   relax)workeveningr   )r   alcohol)morningmealscoffeefocus   )r   r     )r   r   r   r   r   r   r   )r   r   r   r   r   r   c              3   ,   K   | ]  }|v s|  y wr   ).0tr`   s     r#   	<genexpr>z build_profile.<locals>.<genexpr>  s     G!qJAGs   		readiness   urgency_reasonr   )oftenalwayshigh_intent)r   r   
controlledr   r   r   r      r   r   A   ^   six_day   )daysz	%A, %B %dvaper_weekly_cost   price_per_cigim  )monthlyyearly	five_yearr   program_routing)r   smoker_type_detailssuccess_probabilityprogramprogram_details	quit_datedays_to_quitsavings)r   )r   )r^   rh   r   r   listsetappendrv   sumitemsmaxvaluesnextminr   todayr   strftimeroundrl   )%r   r   r  r  r   likeswhy_smoke_rawfeelingdeeperr  dislikesr  cigsvolume_bonussocial_penaltytrigger_countxtype_scoreskv	max_scorer   tie_priorityr1  urgencythinkspast_attempts	first_cigprobrG  rJ  rI  vaper_weekly
daily_costpricerK  r`   s%                                       @r#   build_profilero  z  s-    
H,,. 
77 uuZ$*H EE',2EEE+r*0bMee$b)GUU<$FS./0I)Y--h7	G	Y--i8	K	!1!1+!>	G	Y--h7	I	y//8	 0 0 :	:	t	;		 0 0 =	;		 0 0 9	$	$i&6&6x&@uu-r28bH(I,,W5Hi..w7S^$IQUU?3Q7M23%%2G2S8AEE.)2.Y[D 
1dbjaL2:R$"*B!NMM yQ!q$66!A=QRXFqH}!!3FGHxP!q$66!A=PQR "" YGqH}!!3GHRAq$88!a?RST#$ 	J1qK/!Q6JKQYZAq$@@!aGZ[\IFqqG|!2FGH )A-a16 "	"
 IFqqG|!2FGiHqI~!14HIJyQ!q$66!A=QR(]b-@aa(]b-@aaI!K( -8,=,=,?@DAq1c!Qi<@K@-8K&&()aIA~  [$/$5$5$7JDAq1	>aJ
G|GTUW {+Q/Iee$%+G.3((M'99\7quu%:;Q?M<=EEBU<V<b23R8hjIDA~trzt	a
!)$ 7g%trzt4194	!	TQYTby$!)$	DAIDB		ar3r4=!D G L	| <<FF{SI aee$78!<La!A%
155115E\
b)
S():+a/0G #$yy<@@bQ# 99%6;??L$	 	Q ! HWn53GGHl RFPGRJZF GHQ
 A Ks   Z=Y YY H#Z= Y:Z=Y?
.Z=?Z
Z="Z	
2Z=ZZ=$Z4Z=ZZ=#Z3Z=Z"!Z=0Z' Z=Z, 2Z=Z1)?Z=(Z76Z7:HZ=Y Y74Z=6Y77AZ=
session_idemailc                     t         j                  | i       j                  d      }|r|S |rt        j                  |      }|r|d   S y)aE  Resolve the userId for a session. In-memory fast path, DB fallback by email.

    The in-memory _email_captures map is per-worker and lost on restart, so under
    multiple uvicorn workers results-shown/complete could miss it and silently skip
    the DB writes. Falling back to a lookup by email keeps those writes reliable.userIdiUserIDN)_email_capturesr   
chatbot_dbcheck_user_exists)rp  rq  cachedexistings       r#   _resolve_user_idrz    sM       R044X>F//6I&&r%   z/api/track-batchc           
      .  K   	 | j                          d{   }|j                  dd      }|j                  dg       }|r|sddiS |j                  d	d      }|j                  d
d      }|j                  dd      }|j                  dd      }|j                  dd      }|t        vr	g t        |<   |D ]J  }	||	d	<   ||	d
<   ||	d<   ||	d<   ||	d<   t        j
                         |	d<   t        |   j                  |	       L t        j                  |||||||       |D 
cg c]  }
|
j                  dd       }}
t        d|dd  dt        |       ddj                  |              ddiS 7 X# t        $ r t        ddid      cY S w xY wc c}
w w)u  Clickstream tracking — receives a batch of events from the browser.
    Flushes happen at 8 points: after Q3, Q6, Q12, Q15, email, Q19, Q22, CTA.
    Also fires immediately for page_load and quiz_start.
    TODO: Write to tbl_ChatbotClickstream when DB is ready.NokFr<   r9   	sessionIdr   events
deviceTypecountrysourcemediumcampaigntsstep?z[TRACK-BATCH] r8  z... z	 events: r   T)r^   rh   r   r   _clickstreamrd   rN  rv  write_clickstream_batchrr   rv   r   )r   rz   rp  r~  device_typer  r  r  r  evtr   stepss               r#   track_batchr  )  s    <\\^# +r*JXXh#FVe}((<,Khhy"%GXXh#FXXh#Fxx
B'H%#%Z  -'L IHH"JIIKD	Z '',- &&FK&&( *00AQUU630E0	N:bq>*$s6{m9TYYuEUDV
WX$<G $ <T5Ms;;<> 1sD   FE2 E/E2 DF!F:5F/E2 2F
FFFz/api/save-emailc                   K   	 | j                          d{   }|j                  dd      }|j                  dd      j	                         }|j                  d	d      j	                         }|j                  d
d      }|j                  di       }|sddiS |||t        j
                         dt        |<   d}t        j                  |      }|rEt        j                  |       |d   t        |   d<   t        d|dd  d|d    d|        dddddS i d|d	|d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  dg       d|j                  dg       d|j                  dg       d |j                  d g       d!|j                  d!      d"|j                  d"g       d#|j                  d#g       d$|j                  d$g       d%|j                  d%g       |j                  d&d'      |j                  d(      t        |j                  d)      t              r"|j                  d)i       j                  d*d+      nd+|j                  d,d-      t        |j                  d)      t              r"|j                  d)i       j                  d.d/      nd/t        |j                  d)      t              r"|j                  d)i       j                  d0d1      nd1t        |j                  d)      t              r"|j                  d)i       j                  d2d3      nd3|j                  d4d      d5d6	}	t        j                  |	      }t        d|dd  d7| d8|        |t        |   d9<   d|dddS 7 # t        $ r t        ddid      cY S w xY ww):u   Save email + name immediately when user enters it on email gate.
    Don't wait for CTA click — captures leads who read results but don't convert.Nr|  Fr<   r9   r}  r   rq  r   
smokerTypequizData)rq  r   r  r  rt  pendingUserIdz[EMAIL] r8  z... EXISTING user u    — OTP sent to T)r|  rs  
isExistingneedsOtpagesmoker_statusr   r   r  r   r   rX  r   r\  r   r   r   r   r   fearsr   currencyINRr@  _utmr  IN_onboard_platformzweb-chatbot-desktop-chrome
utm_sourcezchatbot-onboarding
utm_mediumwebutm_campaignchatbot
source_urlu   ₹)	currency_coder@  r  onboard_platformr  r  r  r  pay_currencyz... NEW user z email=rs  )r^   rh   r   r   r   rd   ru  rv  rw  send_otprr   
isinstancedictcreate_user)
r   rz   rp  rq  r   r   	quiz_datauser_idry  rE   s
             r#   
save_emailr  V  s/    <\\^# +r*JHHWb!'')E88FB%%'D((<,KR(Ie} !iik	#OJ G++E2HE"7?	7J
#O4BQ((:8I;N:OO`af`ghid$DQQ
U
D
 9=='
 Y]]?;	

 y}}Y/
 IMM.9
 Y]]?;
  /B!C
 	j"5
 Y]]#8"=
 	&>C
 y}}->C
 "9==1F#G
 Y]]?B?
 	.@" E
  Y]]7B/!
" 9==;#
$ ']]:u=&]]?;IST]TaTabhTikoIpy}}VR044YEvz ).AC_ `_ijsjwjwx~j  BF  `G)--377FZ[  MaPZ[d[h[hio[prvPw)--377eL  ~CXbclcpcpqwcxz~XIMM&"599.)T  FO#--b9$5
	8 ((3BQ(gYgeWMN,3OJ)'ERRA $ <T5Ms;;<s8   O#O OO N&O#O O O#O  O#z/api/verify-otpc                 H  K   	 | j                          d{   }|j                  dd      }|j                  dd      j	                         }t
        j                  |i       }|j                  d	d      }|j                  d
      }|r|r|sdddS t        j                  ||      }|d   rM|j                  di       }t        j                  ||       |t
        |   d<   t        d|dd  d|        d|dS d|d   dS 7 # t        $ r t        ddid      cY S w xY ww)zVerify OTP for existing users.Nr|  Fr<   r9   r}  r   otprq  r  zMissing data. Please try again.r|  r=   validr  rs  z[OTP VERIFIED] r8  z	... user T)r|  rs  r=   )
r^   rh   r   r   r   ru  rv  
verify_otpupdate_existing_userrr   )	r   rz   rp  r  
email_datarq  pending_user_idr   r  s	            r#   verify_otp_endpointr    s+    <\\^# +r*J
((5"

#
#
%C $$Z4JNN7B'E nn_5O?(IJJ""5#.FgHHZ,	''C0?
#H-
2A/y8IJKo66y(9::/ $ <T5Ms;;<s8   D"D DD C&D"D DD"DD"z/api/resend-otpc                 2  K   	 | j                          d{   }|j                  dd      }t        j                  |i       }|j                  dd      }|sdd	d
S t        j                  |       ddd
S 7 _# t        $ r t        ddid      cY S w xY ww)zResend OTP for existing users.Nr|  Fr<   r9   r}  r   rq  zNo email found.r  Tz
Code sent.)r^   rh   r   r   ru  rv  
resend_otp)r   rz   rp  r  rq  s        r#   resend_otp_endpointr    s     <\\^# +r*J $$Z4JNN7B'E(9::% <00 $ <T5Ms;;<s8   BA9 A7A9 AB7A9 9BBBBz/api/results-shownc                 ^  K   | j                          d{   }|j                  dd      }t        ||j                  dd            }|r\t        j                  ||j                  di       j                  dd             t        j
                  |||       t        d| d	       d
diS 7 w)uN   Fires when results page renders — marks onboard complete + writes quiz data.N
_sessionIdr   rq  rK  rA  r   z[RESULTS] User u(    — onboard complete, quiz data writtenr|  T)r^   r   rz  rv  mark_onboard_completewrite_chatbot_onboardingrr   r   r   rp  r  s       r#   results_shownr    s      D,+Jz488GR+@AG(($((9b2I2M2MiYZ2[\++JFy(PQR$<  s   B-B+BB-z/api/completec                 :  K   | j                          d{   }|j                  dd      }t        ||j                  dd            }t        j	                  |d       t        d       t        d|dd  d|        |rt        j                  |       d	d
iS 7 w)u7   Fires when user clicks CTA — marks quiz as completed.Nr  r   rq  z=== CTA CLICKED ===z	Session: r8  z... | UserID: statusr|  )r^   r   rz  ru  poprr   rv  mark_cta_clickedr  s       r#   complete_onboardingr    s      D,+Jz488GR+@AG
D)	
 	Ij!n%^G9
=>##J/d  s   BBBB__main__zapp:appz0.0.0.0i  T)r!   portreload)r   )L__doc__r^   r/   r   rd   datetimer   r   hashlibrm   fastapir   r   fastapi.responsesr   r   fastapi.staticfilesr	   fastapi.templatingr
   uvicorndbrv  pathdirnameabspath__file__BASE_DIRr   	_env_pathexistsopen_ef_liner   rs   r   _k_vr0   
setdefaultappmountr   r   rq   rp   re   rg   rf   strr$   floadrl   ri   r+   r2   ro   r   r   r   r   r  ro  r  ru  rz  r  r  r  r  r  r  __name__runr-  r%   r#   <module>r     sZ   E  	 	  $   $ 8 + .   77??277??845GGLL6*	77>>)	i >C 	>EKKMEe.>.>s.CS!,B

%%bhhj"((*=		>> 12 		
&bggll8U]>^(_fq	 r 		)[277<<(+KLS[	 \bggll8[&IJ	 ZZ^^,b1
g
  @ @C @ 
"'',,x!4
5H ADIIaLEDN \*= = += ) ) +X X Xv 
7 7 7t 
K' K K\ 
^ ^  ^B 
 !a7 a "aH .V V Vt  S   
)w ) )X 
DSg DS DSN 
;w ; ;: 
1w 1 1$ 
    /w    zGKK		TB w> >@ s+   :OOO*AO(O'O$'O1